diff --git a/.changeset/odd-lilies-sniff.md b/.changeset/odd-lilies-sniff.md index 1090de2a1..8991abd79 100644 --- a/.changeset/odd-lilies-sniff.md +++ b/.changeset/odd-lilies-sniff.md @@ -8,15 +8,18 @@ The full class names used in this package were defined as `string`, although we ## Breaking changes -- `ClassGroupingNodeKey.className` is now of type `EC.FullClassName` instead of `string`. -- `NodesQueryClauseFactory.createSelectClause` prop `grouping.byBaseClasses.fullClassNames` is now of type `EC.FullClassName[]` instead of `string[]`. -- `NodesQueryClauseFactory.createSelectClause` prop `grouping.byProperties.propertiesClassName` is now of type `EC.FullClassName` instead of `string`. -- `HierarchyNodesDefinition.fullClassName` is now of type `EC.FullClassName` instead of `string`. -- `createPredicateBasedHierarchyDefinition` prop `hierarchies.childNodes[number].parentInstancesNodePredicate` now accepts `EC.FullClassName` instead of `string`. -- `NodesQueryClauseFactory.createFilterClauses` prop `contentClass.fullName` is now of type `EC.FullClassName` instead of `string`. -- `PropertyOtherValuesGroupingNodeKey.properties[number].className` is now of type `EC.FullClassName` instead of `string`. -- `PropertyValueGroupingNodeKey.propertyClassName` is now of type `EC.FullClassName` instead of `string`. -- `PropertyValueRangeGroupingNodeKey.propertyClassName` is now of type `EC.FullClassName` instead of `string`. +- `ClassGroupingNodeKey.className` is now of type `EC.FullClassNameDotNotation` instead of `string`. +- `PropertyOtherValuesGroupingNodeKey.properties[number].className` is now of type `EC.FullClassNameDotNotation` instead of `string`. +- `PropertyValueGroupingNodeKey.propertyClassName` is now of type `EC.FullClassNameDotNotation` instead of `string`. +- `PropertyValueRangeGroupingNodeKey.propertyClassName` is now of type `EC.FullClassNameDotNotation` instead of `string`. +- A hierarchy node's `processingParams.grouping.byBaseClasses.fullClassNames` is now of type `EC.FullClassNameDotNotation[]` instead of `string[]`. +- A hierarchy node's `processingParams.grouping.byProperties.propertiesClassName` is now of type `EC.FullClassNameDotNotation` instead of `string`. +- `NodesQueryClauseFactory.createSelectClause` prop `grouping.byBaseClasses.fullClassNames` is now of type `EC.FullClassNameDotNotation[]` instead of `string[]`. +- `NodesQueryClauseFactory.createSelectClause` prop `grouping.byProperties.propertiesClassName` is now of type `EC.FullClassNameDotNotation` instead of `string`. +- `NodesQueryClauseFactory.createFilterClauses` prop `contentClass.fullName` is now of type `EC.FullClassNameDotNotation` instead of `string`. +- `HierarchyNodesDefinition.fullClassName` is now of type `EC.FullClassNameDotNotation` instead of `string`. +- `createPredicateBasedHierarchyDefinition` prop `hierarchies.childNodes[number].parentInstancesNodePredicate` now accepts `EC.FullClassNameDotNotation` instead of `string`. +- `DefineInstanceNodeChildHierarchyLevelProps.parentNodeClassName` (provided to `HierarchyDefinition.defineHierarchyLevel`) is now of type `EC.FullClassNameDotNotation` instead of `string`. In many cases migration will be seamless, as long as the input string matches the expected format. In some cases (e.g. when assigning a string variable to one of the affected properties), you may need to use `normalizeFullClassName` from `@itwin/presentation-shared` to ensure the value is of the correct type: diff --git a/.changeset/shaggy-shoes-deny.md b/.changeset/shaggy-shoes-deny.md index eabd6770b..dd4c3c67d 100644 --- a/.changeset/shaggy-shoes-deny.md +++ b/.changeset/shaggy-shoes-deny.md @@ -19,17 +19,19 @@ The full class names used in this package were defined as `string`, although we ## Breaking changes -- Changed `EC.CustomAttribute.className` to be of type `EC.FullClassName` instead of `string`. -- Changed `EC.CustomAttributeSet.get` argument to be of type `EC.FullClassName` instead of `string`. -- Changed `EC.SchemaItem.fullName` to be of type `EC.FullClassName` instead of `string`. -- Changed `compareFullClassNames` arguments to be of type `EC.FullClassName` instead of `string`. -- Changed `createClassBasedInstanceLabelSelectClauseFactory` prop `clauses` to take objects with `{ className: EC.FullClassName }` arguments instead of `{ className: string }`. -- Changed `IInstanceLabelSelectClauseFactory.createSelectClause` prop `className` to be of type `EC.FullClassName` instead of `string`. -- Changed `ECSql.createPrimitivePropertyValueSelectorProps` prop `propertyClassName` to be of type `EC.FullClassName` instead of `string`. -- Changed `ECSql.createRelationshipPathJoinClause` prop `path` to take class names (`relationshipName`, `sourceClassName` and `targetClassName`) of type `EC.FullClassName` instead of `string`. -- Changed `ECClassHierarchyInspector.classDerivesFrom` arguments to be of type `EC.FullClassName` instead of `string`. -- Changed the second argument of `getClass` to be of type `EC.FullClassName` instead of `string`. -- Changed `InstanceKey.className` to be of type `EC.FullClassName` instead of `string`. +- Changed `EC.CustomAttribute.className` to be of type `EC.FullClassNameDotNotation` instead of `string`. +- Changed `EC.CustomAttributeSet.get` argument to be of type `EC.FullClassNameDotNotation` instead of `string`. +- Changed `EC.SchemaItem.fullName` to be of type `EC.FullClassNameDotNotation` instead of `string`. +- Changed `createClassBasedInstanceLabelSelectClauseFactory` prop `clauses` to take objects with `{ className: EC.FullClassNameDotNotation }` arguments instead of `{ className: string }`. +- Changed `IInstanceLabelSelectClauseFactory.createSelectClause` prop `className` to be of type `EC.FullClassNameDotNotation` instead of `string`. +- Changed `ECSql.createPrimitivePropertyValueSelectorProps` prop `propertyClassName` to be of type `EC.FullClassNameDotNotation` instead of `string`. +- Changed `ECSql.createRelationshipPathJoinClause` prop `path` to take class names (`relationshipName`, `sourceClassName` and `targetClassName`) of type `EC.FullClassNameDotNotation` instead of `string`. +- Changed `ECClassHierarchyInspector.classDerivesFrom` arguments to be of type `EC.FullClassNameDotNotation` instead of `string`. +- Changed the second argument of `getClass` to be of type `EC.FullClassNameDotNotation` instead of `string`. +- Changed `InstanceKey.className` to be of type `EC.FullClassNameDotNotation` instead of `string`. The value is now always in dot notation, so consumers no longer need to call `normalizeFullClassName` on it. +- Changed `RelationshipPathStep` class-name properties (`sourceClassName`, `targetClassName` and `relationshipName`) to be of type `EC.FullClassNameDotNotation` instead of `string`. +- Changed `NavigationValueDescriptor.targetClassName` to be of type `EC.FullClassNameDotNotation` instead of `string`. +- Removed `compareFullClassNames`. With `EC.FullClassNameDotNotation` being used across the package, this function is no longer needed. Use `===` operator to compare full class names instead. In many cases migration will be seamless, as long as the input string matches the expected format. In some cases (e.g. when assigning a string variable to one of the affected properties), you may need to use `normalizeFullClassName` to ensure the value is of the correct type: diff --git a/apps/full-stack-tests/src/ECDbUtils.ts b/apps/full-stack-tests/src/ECDbUtils.ts index 251087533..3cfdc1ef9 100644 --- a/apps/full-stack-tests/src/ECDbUtils.ts +++ b/apps/full-stack-tests/src/ECDbUtils.ts @@ -135,7 +135,7 @@ export class ECDbBuilder { } public insertInstance( - fullClassName: EC.FullClassName, + fullClassName: EC.FullClassNameDotNotation, props?: { [propertyName: string]: PrimitiveValue | undefined }, ) { const query = this.createInsertQuery(fullClassName, props); @@ -153,7 +153,7 @@ export class ECDbBuilder { } public insertRelationship( - fullClassName: EC.FullClassName, + fullClassName: EC.FullClassNameDotNotation, sourceId: Id64String, targetId: Id64String, props?: { [propertyName: string]: PrimitiveValue | undefined }, diff --git a/apps/full-stack-tests/src/components/Utils.ts b/apps/full-stack-tests/src/components/Utils.ts new file mode 100644 index 000000000..b9c53b6f0 --- /dev/null +++ b/apps/full-stack-tests/src/components/Utils.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +import type { Id64String } from "@itwin/core-bentley"; +import type { InstanceKey } from "@itwin/presentation-common"; +import type { EC } from "@itwin/presentation-shared"; + +export function toColonInstanceKey(instanceKey: InstanceKey): { + className: EC.FullClassNameColonNotation; + id: Id64String; +} { + return { ...instanceKey, className: instanceKey.className.replace(/\./g, ":") as EC.FullClassNameColonNotation }; +} diff --git a/apps/full-stack-tests/src/components/properties/PropertyPaneDataProvider.test.ts b/apps/full-stack-tests/src/components/properties/PropertyPaneDataProvider.test.ts index 44ab9ab3f..7acacbd85 100644 --- a/apps/full-stack-tests/src/components/properties/PropertyPaneDataProvider.test.ts +++ b/apps/full-stack-tests/src/components/properties/PropertyPaneDataProvider.test.ts @@ -27,6 +27,7 @@ import { Presentation } from "@itwin/presentation-frontend"; import { buildTestIModel } from "../../IModelUtils.js"; import { initialize, terminate } from "../../IntegrationTests.js"; import { importSchema } from "../../SchemaUtils.js"; +import { toColonInstanceKey } from "../Utils.js"; import type { PrimitiveValue, PropertyDescription, PropertyRecord, PropertyValue } from "@itwin/appui-abstract"; import type { PropertyCategory } from "@itwin/components-react"; @@ -59,7 +60,7 @@ describe("PropertyDataProvider", async () => { it("creates empty result when properties requested for 0 instances", async () => { const { imodelConnection } = await buildTestIModel(async (imodel) => { withEditTxn(imodel, (txn) => { - insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }); + insertSpatialCategory({ txn, codeValue: "My Category" }); }); }); using provider = createProvider({ imodel: imodelConnection, ruleset: DEFAULT_PROPERTY_GRID_RULESET }); @@ -75,11 +76,10 @@ describe("PropertyDataProvider", async () => { const { imodelConnection } = await buildTestIModel(async (imodel) => { withEditTxn(imodel, (txn) => { - categoryKey = insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }); - modelKey = insertPhysicalModelWithPartition({ txn, fullClassNameSeparator: ":", codeValue: "My Model" }); + categoryKey = insertSpatialCategory({ txn, codeValue: "My Category" }); + modelKey = insertPhysicalModelWithPartition({ txn, codeValue: "My Model" }); elementKey = insertPhysicalElement({ txn, - fullClassNameSeparator: ":", userLabel: "My Element", modelId: modelKey.id, categoryId: categoryKey.id, @@ -124,11 +124,10 @@ describe("PropertyDataProvider", async () => { const { imodelConnection } = await buildTestIModel(async (imodel) => { withEditTxn(imodel, (txn) => { - categoryKey = insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }); - modelKey = insertPhysicalModelWithPartition({ txn, fullClassNameSeparator: ":", codeValue: "My Model" }); + categoryKey = insertSpatialCategory({ txn, codeValue: "My Category" }); + modelKey = insertPhysicalModelWithPartition({ txn, codeValue: "My Model" }); elementKey = insertPhysicalElement({ txn, - fullClassNameSeparator: ":", userLabel: "My Element", modelId: modelKey.id, categoryId: categoryKey.id, @@ -170,9 +169,7 @@ describe("PropertyDataProvider", async () => { let categoryKey: InstanceKey; const { imodelConnection } = await buildTestIModel(async (imodel) => { - categoryKey = withEditTxn(imodel, (txn) => - insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }), - ); + categoryKey = withEditTxn(imodel, (txn) => insertSpatialCategory({ txn, codeValue: "My Category" })); }); using provider = createProvider({ imodel: imodelConnection, ruleset: DEFAULT_PROPERTY_GRID_RULESET }); vi.spyOn(provider as any, "isFieldFavorite").mockReturnValue(true); @@ -197,9 +194,7 @@ describe("PropertyDataProvider", async () => { let categoryKey: InstanceKey; const { imodelConnection } = await buildTestIModel(async (imodel) => { - categoryKey = withEditTxn(imodel, (txn) => - insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }), - ); + categoryKey = withEditTxn(imodel, (txn) => insertSpatialCategory({ txn, codeValue: "My Category" })); }); using provider = createProvider({ imodel: imodelConnection, @@ -233,9 +228,7 @@ describe("PropertyDataProvider", async () => { let categoryKey: InstanceKey; const { imodelConnection } = await buildTestIModel(async (imodel) => { - categoryKey = withEditTxn(imodel, (txn) => - insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }), - ); + categoryKey = withEditTxn(imodel, (txn) => insertSpatialCategory({ txn, codeValue: "My Category" })); }); using provider = createProvider({ imodel: imodelConnection, ruleset: DEFAULT_PROPERTY_GRID_RULESET }); @@ -249,7 +242,7 @@ describe("PropertyDataProvider", async () => { expect(record).toBeDefined(); const keys = await provider.getPropertyRecordInstanceKeys(record!); - expect(keys).toEqual([categoryKey!]); + expect(keys).toEqual([toColonInstanceKey(categoryKey!)]); }); it("finds nested property record keys", async () => { @@ -258,28 +251,21 @@ describe("PropertyDataProvider", async () => { const { imodelConnection } = await buildTestIModel(async (imodel) => { withEditTxn(imodel, (txn) => { - const categoryKey = insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }); - const modelKey = insertPhysicalModelWithPartition({ - txn, - fullClassNameSeparator: ":", - codeValue: "My Model", - }); + const categoryKey = insertSpatialCategory({ txn, codeValue: "My Category" }); + const modelKey = insertPhysicalModelWithPartition({ txn, codeValue: "My Model" }); elementKey = insertPhysicalElement({ txn, - fullClassNameSeparator: ":", userLabel: "My Element", modelId: modelKey.id, categoryId: categoryKey.id, }); const repositoryLinkKey = insertRepositoryLink({ txn, - fullClassNameSeparator: ":", repositoryUrl: "Repository URL", repositoryLabel: "Repository Label", }); externalsSourceAspectKey = insertExternalSourceAspect({ txn, - fullClassNameSeparator: ":", elementId: elementKey.id, identifier: "My External Source Aspect", repositoryId: repositoryLinkKey.id, @@ -311,7 +297,7 @@ describe("PropertyDataProvider", async () => { expect(record).toBeDefined(); const keys = await provider.getPropertyRecordInstanceKeys(record!); - expect(keys).toEqual([externalsSourceAspectKey!]); + expect(keys).toEqual([toColonInstanceKey(externalsSourceAspectKey!)]); }); }); }; @@ -339,11 +325,11 @@ describe("PropertyDataProvider", async () => { `, ); - const categoryKey = insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }); - const modelKey = insertPhysicalModelWithPartition({ txn, fullClassNameSeparator: ":", codeValue: "My Model" }); + const categoryKey = insertSpatialCategory({ txn, codeValue: "My Category" }); + const modelKey = insertPhysicalModelWithPartition({ txn, codeValue: "My Model" }); const elementKey = insertPhysicalElement({ txn, - classFullName: `${schema.schemaAlias}:TestPhysicalObject` as const, + classFullName: `${schema.schemaAlias}.TestPhysicalObject` as const, userLabel: "Test element", modelId: modelKey.id, categoryId: categoryKey.id, @@ -508,9 +494,7 @@ describe("PropertyDataProvider", async () => { let categoryKey: InstanceKey; const { imodelConnection } = await buildTestIModel(async (imodel) => { - categoryKey = withEditTxn(imodel, (txn) => - insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }), - ); + categoryKey = withEditTxn(imodel, (txn) => insertSpatialCategory({ txn, codeValue: "My Category" })); }); const checkDataProvider = async () => { using provider = new PresentationPropertyDataProvider({ imodel: imodelConnection }); @@ -577,12 +561,8 @@ describe("PropertyDataProvider", async () => { `, ); return withEditTxn(imodel, (txn) => { - const categoryKey = insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }); - const modelKey = insertPhysicalModelWithPartition({ - txn, - fullClassNameSeparator: ":", - codeValue: "My Model", - }); + const categoryKey = insertSpatialCategory({ txn, codeValue: "My Category" }); + const modelKey = insertPhysicalModelWithPartition({ txn, codeValue: "My Model" }); // Both selected elements have a `MyType` type definition (`PhysicalElement -> PhysicalType`), // and each type owns a `CommonTypeAspect` (`PhysicalType -> ElementAspect`), forming two // levels of nested content below the selected elements. diff --git a/apps/full-stack-tests/src/components/unified-selection/Tree.test.tsx b/apps/full-stack-tests/src/components/unified-selection/Tree.test.tsx index 76effff2c..2e927d322 100644 --- a/apps/full-stack-tests/src/components/unified-selection/Tree.test.tsx +++ b/apps/full-stack-tests/src/components/unified-selection/Tree.test.tsx @@ -25,6 +25,7 @@ import { buildTestIModel } from "../../IModelUtils.js"; import { initialize, terminate } from "../../IntegrationTests.js"; import { act, fireEvent, render, waitFor } from "../../RenderUtils.js"; import { getNodeByLabel, isNodeSelectedInTree, toggleExpandNode } from "../TreeUtils.js"; +import { toColonInstanceKey } from "../Utils.js"; import type { IModelConnection } from "@itwin/core-frontend"; import type { InstanceKey, Ruleset } from "@itwin/presentation-common"; @@ -75,11 +76,10 @@ describe("Learning snippets", async () => { let elementKey: InstanceKey; const { imodelConnection } = await buildTestIModel(async (imodel) => { withEditTxn(imodel, (txn) => { - const categoryKey = insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }); - modelKey = insertPhysicalModelWithPartition({ txn, fullClassNameSeparator: ":", codeValue: "My Model" }); + const categoryKey = insertSpatialCategory({ txn, codeValue: "My Category" }); + modelKey = insertPhysicalModelWithPartition({ txn, codeValue: "My Model" }); elementKey = insertPhysicalElement({ txn, - fullClassNameSeparator: ":", userLabel: "My Element", modelId: modelKey.id, categoryId: categoryKey.id, @@ -112,11 +112,11 @@ describe("Learning snippets", async () => { // test Tree selection -> Unified Selection synchronization fireEvent.click(modelNode); await waitFor(() => { - expect(getInstanceKeysInUnifiedSelection(imodelConnection)).toEqual([modelKey]); + expect(getInstanceKeysInUnifiedSelection(imodelConnection)).toEqual([toColonInstanceKey(modelKey)]); }); fireEvent.click(elementNode); await waitFor(() => { - expect(getInstanceKeysInUnifiedSelection(imodelConnection)).toEqual([elementKey]); + expect(getInstanceKeysInUnifiedSelection(imodelConnection)).toEqual([toColonInstanceKey(elementKey)]); }); }); }); diff --git a/apps/full-stack-tests/src/components/unified-selection/Viewport.test.tsx b/apps/full-stack-tests/src/components/unified-selection/Viewport.test.tsx index 8e2c3f1f4..d26c4e603 100644 --- a/apps/full-stack-tests/src/components/unified-selection/Viewport.test.tsx +++ b/apps/full-stack-tests/src/components/unified-selection/Viewport.test.tsx @@ -52,16 +52,11 @@ describe("Learning snippets", async () => { const { imodelConnection } = await buildTestIModel(async (imodel) => { withEditTxn(imodel, (txn) => { - const categoryKey = insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }); - const modelKey = insertPhysicalModelWithPartition({ - txn, - fullClassNameSeparator: ":", - codeValue: "My Model", - }); + const categoryKey = insertSpatialCategory({ txn, codeValue: "My Category" }); + const modelKey = insertPhysicalModelWithPartition({ txn, codeValue: "My Model" }); (elementKeys.push( insertPhysicalElement({ txn, - fullClassNameSeparator: ":", userLabel: "My Assembly Element", modelId: modelKey.id, categoryId: categoryKey.id, @@ -70,7 +65,6 @@ describe("Learning snippets", async () => { elementKeys.push( insertPhysicalElement({ txn, - fullClassNameSeparator: ":", userLabel: "My Child Element 1", modelId: modelKey.id, categoryId: categoryKey.id, @@ -78,7 +72,6 @@ describe("Learning snippets", async () => { }), insertPhysicalElement({ txn, - fullClassNameSeparator: ":", userLabel: "My Child Element 2", modelId: modelKey.id, categoryId: categoryKey.id, diff --git a/apps/full-stack-tests/src/hierarchies/Update.test.ts b/apps/full-stack-tests/src/hierarchies/Update.test.ts index 4cb1bbc9c..679d87d9a 100644 --- a/apps/full-stack-tests/src/hierarchies/Update.test.ts +++ b/apps/full-stack-tests/src/hierarchies/Update.test.ts @@ -36,7 +36,7 @@ import { registerTxnListeners } from "@itwin/presentation-core-interop"; import { ECSql, normalizeFullClassName } from "@itwin/presentation-shared"; import { createFileNameFromString, setupOutputFileLocation } from "../FilenameUtils.js"; import { NodeValidators, validateHierarchyLevel } from "./HierarchyValidation.js"; -import { createClassECSqlSelector, createIModelAccess, createProvider } from "./Utils.js"; +import { createIModelAccess, createProvider } from "./Utils.js"; import type { Id64String } from "@itwin/core-bentley"; import type { @@ -409,8 +409,8 @@ describe("Hierarchies", () => { ecInstanceId: { selector: `this.ECInstanceId` }, nodeLabel: { selector: props.label === "codeValue" ? `this.CodeValue` : `aspect.Identifier` }, })} - FROM ${createClassECSqlSelector(Subject.classFullName)} AS this - LEFT JOIN ${createClassECSqlSelector(ExternalSourceAspect.classFullName)} AS aspect ON aspect.Element.Id = this.ECInstanceId + FROM ${ECSql.createClassSelector(normalizeFullClassName(Subject.classFullName))} AS this + LEFT JOIN ${ECSql.createClassSelector(normalizeFullClassName(ExternalSourceAspect.classFullName))} AS aspect ON aspect.Element.Id = this.ECInstanceId WHERE this.Parent.Id = 0x1 `, }, @@ -437,8 +437,8 @@ describe("Hierarchies", () => { ecInstanceId: { selector: `this.ECInstanceId` }, nodeLabel: { selector: `this.CodeValue` }, })} - FROM ${createClassECSqlSelector(Element.classFullName)} AS this - JOIN ${createClassECSqlSelector(ElementRefersToElements.classFullName)} AS rel ON rel.TargetECInstanceId = this.ECInstanceId + FROM ${ECSql.createClassSelector(normalizeFullClassName(Element.classFullName))} AS this + JOIN ${ECSql.createClassSelector(normalizeFullClassName(ElementRefersToElements.classFullName))} AS rel ON rel.TargetECInstanceId = this.ECInstanceId WHERE rel.SourceECInstanceId = 0x1 `, }, @@ -482,8 +482,8 @@ describe("Hierarchies", () => { ]), }, })} - FROM ${createClassECSqlSelector(PhysicalModel.classFullName)} AS this - JOIN ${createClassECSqlSelector(Element.classFullName)} AS modeledElement ON modeledElement.ECInstanceId = this.ModeledElement.Id + FROM ${ECSql.createClassSelector(normalizeFullClassName(PhysicalModel.classFullName))} AS this + JOIN ${ECSql.createClassSelector(normalizeFullClassName(Element.classFullName))} AS modeledElement ON modeledElement.ECInstanceId = this.ModeledElement.Id `, }, }, diff --git a/apps/full-stack-tests/src/hierarchies/Utils.ts b/apps/full-stack-tests/src/hierarchies/Utils.ts index 213b2f3c0..0b0c7b3ca 100644 --- a/apps/full-stack-tests/src/hierarchies/Utils.ts +++ b/apps/full-stack-tests/src/hierarchies/Utils.ts @@ -11,7 +11,7 @@ import { createIModelKey, } from "@itwin/presentation-core-interop"; import { createIModelHierarchyProvider, createLimitingECSqlQueryExecutor } from "@itwin/presentation-hierarchies"; -import { createCachingECClassHierarchyInspector, parseFullClassName } from "@itwin/presentation-shared"; +import { createCachingECClassHierarchyInspector } from "@itwin/presentation-shared"; import { createSchemaContext } from "../IModelUtils.js"; import type { ECDb } from "@itwin/core-backend"; @@ -73,8 +73,3 @@ export function createProvider( sourceName: props.sourceName, }); } - -export function createClassECSqlSelector(fullClassName: string) { - const { schemaName, className } = parseFullClassName(fullClassName); - return `[${schemaName}].[${className}]`; -} diff --git a/apps/full-stack-tests/src/hierarchies/learning-snippets/MergedIModelHierarchies.test.ts b/apps/full-stack-tests/src/hierarchies/learning-snippets/MergedIModelHierarchies.test.ts index 418ab2c6a..49d96f1b0 100644 --- a/apps/full-stack-tests/src/hierarchies/learning-snippets/MergedIModelHierarchies.test.ts +++ b/apps/full-stack-tests/src/hierarchies/learning-snippets/MergedIModelHierarchies.test.ts @@ -108,7 +108,7 @@ describe("Hierarchies", () => { fullClassName, whereClauseFactory, }: Pick & { - fullClassName: EC.FullClassName; + fullClassName: EC.FullClassNameDotNotation; whereClauseFactory?: (props: { alias: string }) => Promise; }) { const whereClause = whereClauseFactory ? await whereClauseFactory({ alias: "this" }) : undefined; diff --git a/apps/performance-tests/src/hierarchies/Grouping.test.ts b/apps/performance-tests/src/hierarchies/Grouping.test.ts index 7aec96317..5fec3c04a 100644 --- a/apps/performance-tests/src/hierarchies/Grouping.test.ts +++ b/apps/performance-tests/src/hierarchies/Grouping.test.ts @@ -12,7 +12,7 @@ import type { EC } from "@itwin/presentation-shared"; describe("grouping", () => { const { schemaName, baseClassName, customPropName, itemsPerGroup, defaultClassName } = Datasets.CUSTOM_SCHEMA; const expectedNodeCount = 50_000 + 50_000 / itemsPerGroup; - const baseFullClassName: EC.FullClassName = `${schemaName}.${baseClassName}`; + const baseFullClassName: EC.FullClassNameDotNotation = `${schemaName}.${baseClassName}`; runHierarchyTest({ testName: "by label", @@ -45,10 +45,12 @@ describe("grouping", () => { const physicalElementFullClassName = "BisCore.PhysicalElement"; const baseClassQueryLimit = 10; - const fullClassNames: EC.FullClassName[] = [ + const fullClassNames: EC.FullClassNameDotNotation[] = [ physicalElementFullClassName, baseFullClassName, - ...[...Array(baseClassQueryLimit).keys()].map((i): EC.FullClassName => `${schemaName}.${defaultClassName}_${i}`), + ...[...Array(baseClassQueryLimit).keys()].map( + (i): EC.FullClassNameDotNotation => `${schemaName}.${defaultClassName}_${i}`, + ), ]; runHierarchyTest({ testName: `by base class (${baseClassQueryLimit} classes)`, diff --git a/apps/performance-tests/src/hierarchies/ModelsTree.test.ts b/apps/performance-tests/src/hierarchies/ModelsTree.test.ts index 45db658eb..271a74d86 100644 --- a/apps/performance-tests/src/hierarchies/ModelsTree.test.ts +++ b/apps/performance-tests/src/hierarchies/ModelsTree.test.ts @@ -63,7 +63,7 @@ describe("models tree", () => { ecsql: `SELECT CAST(IdToHex(ECInstanceId) AS TEXT) AS ECInstanceId FROM bis.GeometricElement3d`, }; for await (const row of imodelAccess.createQueryReader(query, { limit: "unbounded" })) { - targetItems.push({ id: row.ECInstanceId, className: "Generic:PhysicalObject" }); + targetItems.push({ id: row.ECInstanceId, className: "Generic.PhysicalObject" }); } return { iModel, imodelAccess, targetItems }; }, diff --git a/apps/performance-tests/src/hierarchies/RunHierarchyTest.ts b/apps/performance-tests/src/hierarchies/RunHierarchyTest.ts index 2ecdaeb59..a586dc07e 100644 --- a/apps/performance-tests/src/hierarchies/RunHierarchyTest.ts +++ b/apps/performance-tests/src/hierarchies/RunHierarchyTest.ts @@ -23,7 +23,7 @@ import type { RunOptions } from "../util/TestUtilities.js"; export function runHierarchyTest( testProps: { iModelName: IModelName; - fullClassName?: EC.FullClassName; + fullClassName?: EC.FullClassNameDotNotation; nodeSelectProps?: Partial>; expectedNodeCount?: number; } & Omit, "setup" | "test" | "cleanup">, diff --git a/apps/performance-tests/src/unified-selection/HiliteSet.test.ts b/apps/performance-tests/src/unified-selection/HiliteSet.test.ts index ed3db4e6b..7de7c5882 100644 --- a/apps/performance-tests/src/unified-selection/HiliteSet.test.ts +++ b/apps/performance-tests/src/unified-selection/HiliteSet.test.ts @@ -19,7 +19,7 @@ describe("hilite", () => { runHiliteTest({ testName: "50k elements", iModelName: "50k elements", - fullClassName: "BisCore:Element", + fullClassName: "BisCore.Element", inputQuery: "SELECT ECInstanceId FROM BisCore:Element", expectedCounts: { elements: 50_000 }, }); @@ -27,7 +27,7 @@ describe("hilite", () => { runHiliteTest({ testName: "50k group elements", iModelName: "50k group member elements", - fullClassName: "BisCore:GroupInformationElement", + fullClassName: "BisCore.GroupInformationElement", inputQuery: "SELECT ECInstanceId FROM BisCore:GroupInformationElement", expectedCounts: { elements: 50_000 }, }); @@ -35,7 +35,7 @@ describe("hilite", () => { runHiliteTest({ testName: "1k subjects", iModelName: "1k subjects", - fullClassName: "BisCore:Subject", + fullClassName: "BisCore.Subject", inputQuery: "SELECT ECInstanceId FROM BisCore:Subject WHERE UserLabel = 'test_subject'", expectedCounts: { models: 20 }, }); @@ -43,7 +43,7 @@ describe("hilite", () => { runHiliteTest({ testName: "50k subcategories", iModelName: "50k subcategories", - fullClassName: "BisCore:SpatialCategory", + fullClassName: "BisCore.SpatialCategory", inputQuery: "SELECT ECInstanceId FROM BisCore:SpatialCategory", expectedCounts: { subCategories: 50_000 }, }); @@ -51,7 +51,7 @@ describe("hilite", () => { runHiliteTest({ testName: "50k functional 3D elements", iModelName: "50k functional 3D elements", - fullClassName: "Functional:FunctionalElement", + fullClassName: "Functional.FunctionalElement", inputQuery: "SELECT ECInstanceId FROM Functional:FunctionalElement", expectedCounts: { elements: 50_000 }, }); @@ -59,7 +59,7 @@ describe("hilite", () => { runHiliteTest({ testName: "50k functional 2D elements", iModelName: "50k functional 2D elements", - fullClassName: "Functional:FunctionalElement", + fullClassName: "Functional.FunctionalElement", inputQuery: "SELECT ECInstanceId FROM Functional:FunctionalElement", // iModel contains one additional 2D element expectedCounts: { elements: 50_001 }, diff --git a/apps/performance-tests/src/util/Datasets.ts b/apps/performance-tests/src/util/Datasets.ts index 38fbf38a4..de4498bce 100644 --- a/apps/performance-tests/src/util/Datasets.ts +++ b/apps/performance-tests/src/util/Datasets.ts @@ -165,18 +165,13 @@ export class Datasets { await createIModel(name, localPath, async (txn) => { await txn.iModel.importSchemaStrings([getFullSchemaXml({ schemaName, schemaContentXml: schema })]); - const { id: categoryId } = insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }); - const { id: modelId } = insertPhysicalModelWithPartition({ - txn, - fullClassNameSeparator: ":", - codeValue: "My Model", - }); + const { id: categoryId } = insertSpatialCategory({ txn, codeValue: "My Category" }); + const { id: modelId } = insertPhysicalModelWithPartition({ txn, codeValue: "My Model" }); for (let groupIdx = 0; groupIdx < numGroups; ++groupIdx) { for (let j = 0; j < elementsPerGroup; ++j) { insertPhysicalElement({ txn, - classFullName: `${schemaName}:${defaultClassName}_${groupIdx}` satisfies EC.FullClassName, - fullClassNameSeparator: ":", + classFullName: `${schemaName}.${defaultClassName}_${groupIdx}` satisfies EC.FullClassNameDotNotation, userLabel: `${defaultUserLabel}_${groupIdx}`, modelId, categoryId, @@ -198,16 +193,8 @@ export class Datasets { console.log(`${numElements} elements: Creating...`); await createIModel(name, localPath, async (txn) => { - const { id: spatialCategoryId } = insertSpatialCategory({ - txn, - fullClassNameSeparator: ":", - codeValue: "My Category", - }); - const { id: physicalModelId } = insertPhysicalModelWithPartition({ - txn, - fullClassNameSeparator: ":", - codeValue: "My Model", - }); + const { id: spatialCategoryId } = insertSpatialCategory({ txn, codeValue: "My Category" }); + const { id: physicalModelId } = insertPhysicalModelWithPartition({ txn, codeValue: "My Model" }); const { id: drawingModelId } = insertDrawingModelWithPartition({ txn, codeValue: "test drawing model" }); const { id: drawingCategoryId } = insertDrawingCategory({ txn, codeValue: "test drawing category" }); @@ -271,12 +258,7 @@ export class Datasets { private static async createCategoryIModel(name: string, localPath: string, numElements: number) { console.log(`${numElements} elements: Creating...`); await createIModel(name, localPath, async (txn) => { - const { id: categoryId } = insertSpatialCategory({ - txn, - fullClassNameSeparator: ":", - codeValue: "My Category", - userLabel: "test_category", - }); + const { id: categoryId } = insertSpatialCategory({ txn, codeValue: "My Category", userLabel: "test_category" }); // Insert `numElements` - 1 subcategories as `insertSpatialCategory` provides one additional subcategory for (let i = 0; i < numElements - 1; ++i) { @@ -305,7 +287,7 @@ export class Datasets { codeValue: "group information model", }); const { id: modelId } = insertPhysicalModelWithPartition({ txn, codeValue: "test physical model" }); - const { id: categoryId } = insertSpatialCategory({ txn, fullClassNameSeparator: ":", codeValue: "My Category" }); + const { id: categoryId } = insertSpatialCategory({ txn, codeValue: "My Category" }); for (let groupIdx = 0; groupIdx < numGroups; ++groupIdx) { const { id: groupId } = insertGroupInformationElement({ txn, modelId: groupModelId, userLabel: "test_group" }); diff --git a/packages/content/api/presentation-content.api.md b/packages/content/api/presentation-content.api.md index 85b63208b..71800b0df 100644 --- a/packages/content/api/presentation-content.api.md +++ b/packages/content/api/presentation-content.api.md @@ -142,7 +142,7 @@ interface ContentSortSpec { // @public export interface ContentSource { resolvedDeclarations: ResolvedDeclarationGroup[]; - resolvedPrimaryClasses: EC.FullClassName[]; + resolvedPrimaryClasses: EC.FullClassNameDotNotation[]; target: ContentTarget; } @@ -154,7 +154,7 @@ export interface ContentTarget { bindings?: Record; }; instanceIds?: Id64String[]; - primaryClass: EC.FullClassName; + primaryClass: EC.FullClassNameDotNotation; } // @public @@ -284,7 +284,7 @@ interface IModelFieldsProvider extends BaseFieldsProvider { // @public interface InputPropertyDeclaration { path?: RelationshipPath; - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; propertyName: string; } @@ -299,16 +299,16 @@ export interface PropertyField extends BaseField { // (undocumented) kind: "property"; pathFromTarget: RelationshipPath; - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; propertyName: string; selectorId: string; - valueClassNames: EC.FullClassName[]; + valueClassNames: EC.FullClassNameDotNotation[]; } // @public (undocumented) export namespace PropertyField { export function computeId(props: { - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; propertyName: string; pathFromTarget?: RelationshipPath; forkKey?: string; @@ -389,7 +389,7 @@ interface ResolvedDeclarationGroup { // @public interface ResolvedPath { path: RelationshipPath; - targetClassNames: EC.FullClassName[]; + targetClassNames: EC.FullClassNameDotNotation[]; } // @public @@ -405,7 +405,7 @@ interface TransformableDescriptor { readonly categories: Record; // (undocumented) readonly fields: Readonly>; - forkField(id: Field["id"], valueClassNames: EC.FullClassName[]): TransformableField; + forkField(id: Field["id"], valueClassNames: EC.FullClassNameDotNotation[]): TransformableField; // (undocumented) removeField(id: string): void; // (undocumented) diff --git a/packages/content/src/content/ContentTarget.ts b/packages/content/src/content/ContentTarget.ts index ced05bf6f..7df833f41 100644 --- a/packages/content/src/content/ContentTarget.ts +++ b/packages/content/src/content/ContentTarget.ts @@ -31,7 +31,7 @@ export interface ContentTarget { /** * Full class name of the primary class whose properties we want (e.g., "BisCore.Element"). */ - primaryClass: EC.FullClassName; + primaryClass: EC.FullClassNameDotNotation; /** * Optional set of instance IDs to scope to specific instances. @@ -101,7 +101,7 @@ export interface ContentSource { * The library uses this to populate a direct field's `valueClassNames`, so overrides can be * scoped below a polymorphically-selected base primary via `TransformableDescriptor.forkField`. */ - resolvedPrimaryClasses: EC.FullClassName[]; + resolvedPrimaryClasses: EC.FullClassNameDotNotation[]; /** * Resolved declaration groups — one per provider declaration that produced @@ -133,7 +133,7 @@ export interface ResolvedPath { * Discovered by a data-driven scan during source resolution, so it lists only the concrete * classes that participate in this path, never a polymorphically-selected base. */ - targetClassNames: EC.FullClassName[]; + targetClassNames: EC.FullClassNameDotNotation[]; } /** diff --git a/packages/content/src/content/InternalUtils.ts b/packages/content/src/content/InternalUtils.ts index f549a829b..bd75ba1ca 100644 --- a/packages/content/src/content/InternalUtils.ts +++ b/packages/content/src/content/InternalUtils.ts @@ -81,7 +81,7 @@ export async function getClassLabel({ className, }: { imodelAccess: ECSchemaProvider; - className: EC.FullClassName; + className: EC.FullClassNameDotNotation; }): Promise { const ecClass = await getClass(imodelAccess, className); return ecClass.label ?? ecClass.name; diff --git a/packages/content/src/content/ResolveContentSources.ts b/packages/content/src/content/ResolveContentSources.ts index 10080aede..009991502 100644 --- a/packages/content/src/content/ResolveContentSources.ts +++ b/packages/content/src/content/ResolveContentSources.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { filter, finalize, forkJoin, from, lastValueFrom, map, mergeMap, of, race, toArray } from "rxjs"; -import { ECSql, getClass, normalizeFullClassName } from "@itwin/presentation-shared"; +import { ECSql, getClass } from "@itwin/presentation-shared"; import { ECSQL_PREFIX } from "./InternalUtils.js"; import { toSortedUniqueClassNames } from "./model/Utils.js"; @@ -268,10 +268,10 @@ async function resolveDeclarationPaths({ map((row) => ({ path: declaration.path.map((step: RelationshipPath[number], i: number) => ({ ...step, - sourceClassName: (i === 0 ? target.primaryClass : row[i]) as EC.FullClassName, - targetClassName: row[i + 1] as EC.FullClassName, + sourceClassName: (i === 0 ? target.primaryClass : row[i]) as EC.FullClassNameDotNotation, + targetClassName: row[i + 1] as EC.FullClassNameDotNotation, })), - targetClassNames: toSortedUniqueClassNames((row[0] as string).split(",") as EC.FullClassName[]), + targetClassNames: toSortedUniqueClassNames((row[0] as string).split(",") as EC.FullClassNameDotNotation[]), })), toArray(), ), @@ -293,7 +293,7 @@ async function resolvePrimaryClasses({ const primaryClass = await getClass(imodelAccess, target.primaryClass); const derivedClasses = await primaryClass.getDerivedClasses(); if (derivedClasses.length === 0) { - return [normalizeFullClassName(target.primaryClass)]; + return [target.primaryClass]; } const reader = imodelAccess.createQueryReader(buildPrimaryEnumerationQuery(target), { rowFormat: "Indexes" }); diff --git a/packages/content/src/content/descriptor-building/Categories.ts b/packages/content/src/content/descriptor-building/Categories.ts index aaeb48524..f95cf0059 100644 --- a/packages/content/src/content/descriptor-building/Categories.ts +++ b/packages/content/src/content/descriptor-building/Categories.ts @@ -125,7 +125,7 @@ export async function collectCategories(props: { // class-based category nests under its nearest existing ancestor in the path's // `rel_0 → tgt_0 → rel_1 → tgt_1 → …` chain (a step's target nests under its relationship; a // step nests under the previous step's target). - const labelsToResolve: Array<{ category: CategoryDefinition; className: EC.FullClassName }> = []; + const labelsToResolve: Array<{ category: CategoryDefinition; className: EC.FullClassNameDotNotation }> = []; const registerAnchorCategory = ( id: CategoryDefinition["id"], path: RelationshipPath, diff --git a/packages/content/src/content/descriptor-building/ClassPropertyFields.ts b/packages/content/src/content/descriptor-building/ClassPropertyFields.ts index 000b59032..923c1728e 100644 --- a/packages/content/src/content/descriptor-building/ClassPropertyFields.ts +++ b/packages/content/src/content/descriptor-building/ClassPropertyFields.ts @@ -73,11 +73,11 @@ export interface CategorizedField { export async function collectClassPropertyFields(props: { imodelAccess: ECSchemaProvider; /** The class whose properties are enumerated. */ - className: EC.FullClassName; + className: EC.FullClassNameDotNotation; /** Relationship path from the content target to `className` (`[]` for direct properties). */ pathFromTarget: RelationshipPath; /** Concrete value-supplier classes for the produced fields. */ - valueClassNames: EC.FullClassName[]; + valueClassNames: EC.FullClassNameDotNotation[]; /** Property selection + overrides. Pass `{ select: "all" }` to include every property unchanged. */ spec: ClassPropertySpec; /** How the produced fields anchor for categorization (see {@link FieldCategorization.anchor}). */ diff --git a/packages/content/src/content/descriptor-building/DirectFields.ts b/packages/content/src/content/descriptor-building/DirectFields.ts index 7b4b9a1ee..056af57d8 100644 --- a/packages/content/src/content/descriptor-building/DirectFields.ts +++ b/packages/content/src/content/descriptor-building/DirectFields.ts @@ -3,7 +3,7 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { getClass, normalizeFullClassName } from "@itwin/presentation-shared"; +import { getClass } from "@itwin/presentation-shared"; import { getOrCreate } from "../InternalUtils.js"; import { collectClassPropertyFields } from "./ClassPropertyFields.js"; @@ -36,9 +36,7 @@ export async function collectDirectPropertyFields(props: { }): Promise { const { imodelAccess, source } = props; const concreteClassNames = - source.resolvedPrimaryClasses.length > 0 - ? source.resolvedPrimaryClasses.map(normalizeFullClassName) - : [normalizeFullClassName(source.target.primaryClass)]; + source.resolvedPrimaryClasses.length > 0 ? source.resolvedPrimaryClasses : [source.target.primaryClass]; // The declaring classes that can contribute a direct property (every concrete class plus all of // its ancestors), each mapped to the concretes that derive from it. Reading each class's *own* @@ -79,7 +77,7 @@ async function collectDeclaringClassClosure( const visited = new Set(); while (pending.length > 0) { const ecClass = pending.pop()!; - const declaringClassName = normalizeFullClassName(ecClass.fullName); + const declaringClassName = ecClass.fullName; if (visited.has(declaringClassName)) { continue; } diff --git a/packages/content/src/content/descriptor-building/ExternalFields.ts b/packages/content/src/content/descriptor-building/ExternalFields.ts index 53665544b..455ab0b5e 100644 --- a/packages/content/src/content/descriptor-building/ExternalFields.ts +++ b/packages/content/src/content/descriptor-building/ExternalFields.ts @@ -9,7 +9,7 @@ import type { ExternalField, Field } from "../model/Field.js"; /** Column coordinates of a property an external fields provider needs as input. */ export interface ExternalInput { - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; propertyName: string; pathFromTarget?: RelationshipPath; } diff --git a/packages/content/src/content/descriptor-building/PropertyFieldMerge.ts b/packages/content/src/content/descriptor-building/PropertyFieldMerge.ts index 69f76932a..f644607d7 100644 --- a/packages/content/src/content/descriptor-building/PropertyFieldMerge.ts +++ b/packages/content/src/content/descriptor-building/PropertyFieldMerge.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { assert } from "@itwin/core-bentley"; -import { normalizeFullClassName, type ValueDescriptor } from "@itwin/presentation-shared"; +import { type ValueDescriptor } from "@itwin/presentation-shared"; import { DEFAULT_FIELDS_PROVIDER_PRIORITY } from "../extensions/BaseFieldsProvider.js"; import { getOrCreate } from "../InternalUtils.js"; import { PropertyField } from "../model/Field.js"; @@ -143,6 +143,6 @@ function toComparableValueDescriptor(descriptor: ValueDescriptor): unknown { descriptor.members.map((member) => [member.name, member.label, toComparableValueDescriptor(member.type)]), ]; case "navigation": - return ["navigation", normalizeFullClassName(descriptor.targetClassName)]; + return ["navigation", descriptor.targetClassName]; } } diff --git a/packages/content/src/content/descriptor-building/Selectors.ts b/packages/content/src/content/descriptor-building/Selectors.ts index 7fbc169ce..d9d070324 100644 --- a/packages/content/src/content/descriptor-building/Selectors.ts +++ b/packages/content/src/content/descriptor-building/Selectors.ts @@ -53,7 +53,7 @@ export function collectSelectors(props: { /** Creates a {@link PropertyValueSelector} with its id derived from the property's identity. */ function createPropertySelector(props: { id: string; - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; propertyName: string; pathFromTarget?: RelationshipPath; }): PropertyValueSelector { diff --git a/packages/content/src/content/extensions/DescriptorTransformer.ts b/packages/content/src/content/extensions/DescriptorTransformer.ts index 0153c6083..815843b9f 100644 --- a/packages/content/src/content/extensions/DescriptorTransformer.ts +++ b/packages/content/src/content/extensions/DescriptorTransformer.ts @@ -3,7 +3,6 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { normalizeFullClassName } from "@itwin/presentation-shared"; import { PropertyField } from "../model/Field.js"; import { computeFieldForkKey, toSortedUniqueClassNames } from "../model/Utils.js"; @@ -135,7 +134,7 @@ interface TransformableDescriptor { * value-supplier classes, if `valueClassNames` is empty, or if it contains a class not * represented by the field. */ - forkField(id: Field["id"], valueClassNames: EC.FullClassName[]): TransformableField; + forkField(id: Field["id"], valueClassNames: EC.FullClassNameDotNotation[]): TransformableField; } /** @@ -187,9 +186,7 @@ export function createTransformableDescriptor( // The subset covers every value-supplier class: mutate in place, no fork. return field; } - field.valueClassNames = field.valueClassNames.filter( - (className) => !subset.includes(normalizeFullClassName(className)), - ); + field.valueClassNames = field.valueClassNames.filter((className) => !subset.includes(className)); const fork: PropertyField = { ...field, id: forkedId, valueClassNames: subset }; descriptor.fields[forkedId] = fork; return fork; diff --git a/packages/content/src/content/extensions/ExternalFieldsProvider.ts b/packages/content/src/content/extensions/ExternalFieldsProvider.ts index 2d898d818..5e206b493 100644 --- a/packages/content/src/content/extensions/ExternalFieldsProvider.ts +++ b/packages/content/src/content/extensions/ExternalFieldsProvider.ts @@ -71,7 +71,7 @@ export interface ExternalFieldsProvider< */ export interface InputPropertyDeclaration { /** Full class name that owns the property. */ - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; /** The EC property name. */ propertyName: string; /** diff --git a/packages/content/src/content/extensions/ecexpressions/ECExpressionToECSql.ts b/packages/content/src/content/extensions/ecexpressions/ECExpressionToECSql.ts index 910ab2668..aaa820490 100644 --- a/packages/content/src/content/extensions/ecexpressions/ECExpressionToECSql.ts +++ b/packages/content/src/content/extensions/ecexpressions/ECExpressionToECSql.ts @@ -29,7 +29,7 @@ export interface ConvertECExpressionToECSqlProps { * Full name of the primary (`this`) class. When provided, instance label select clauses generated for * the primary instance can be more efficient. */ - primaryClassName?: EC.FullClassName; + primaryClassName?: EC.FullClassNameDotNotation; /** * Factory used to generate instance label select clauses for `GetDisplayLabel` and related-instance label functions. diff --git a/packages/content/src/content/extensions/ecexpressions/Emitter.ts b/packages/content/src/content/extensions/ecexpressions/Emitter.ts index c4dce71e6..ec2bc7a3d 100644 --- a/packages/content/src/content/extensions/ecexpressions/Emitter.ts +++ b/packages/content/src/content/extensions/ecexpressions/Emitter.ts @@ -34,7 +34,7 @@ export interface SymbolValues { */ export interface EmitterOptions { primaryClassAlias?: string; - primaryClassName?: EC.FullClassName; + primaryClassName?: EC.FullClassNameDotNotation; labelSelectClauseFactory?: IInstanceLabelSelectClauseFactory; context?: { getSelectedInstanceIds?: () => Id64String[]; resolveRoot?: (root: string) => SymbolValues | undefined }; } @@ -90,7 +90,7 @@ const BUILTIN_UNSUPPORTED_FUNCTIONS = new Set( */ export class Emitter { readonly #primaryClassAlias: string; - readonly #primaryClassName?: EC.FullClassName; + readonly #primaryClassName?: EC.FullClassNameDotNotation; readonly #labelFactory?: IInstanceLabelSelectClauseFactory; readonly #getSelectedInstanceIds?: () => Id64String[]; readonly #resolveRoot?: (root: string) => SymbolValues | undefined; @@ -368,7 +368,7 @@ export class Emitter { return this.#labelClause(alias, className); } - async #labelClause(classAlias: string, className?: EC.FullClassName): Promise { + async #labelClause(classAlias: string, className?: EC.FullClassNameDotNotation): Promise { if (!this.#labelFactory) { throw new Error("Generating an instance label requires a `labelSelectClauseFactory` option."); } diff --git a/packages/content/src/content/extensions/presentation-rules/DescriptorTransformerFactory.ts b/packages/content/src/content/extensions/presentation-rules/DescriptorTransformerFactory.ts index 091d10968..7d757cdb7 100644 --- a/packages/content/src/content/extensions/presentation-rules/DescriptorTransformerFactory.ts +++ b/packages/content/src/content/extensions/presentation-rules/DescriptorTransformerFactory.ts @@ -72,7 +72,7 @@ export function createDescriptorTransformerFromContentModifierRule({ // Precompute the matched value-class subset per candidate (before any forking shrinks the // originals), so repeat specs and the cross-field "hide others" rule re-resolve the same forks. - const matchedByCandidate = new Map(); + const matchedByCandidate = new Map(); for (const candidate of candidates) { matchedByCandidate.set(candidate, await computeMatchedValueClasses(imodelAccess, candidate, rule.class)); } @@ -135,11 +135,11 @@ async function computeMatchedValueClasses( imodelAccess: ECClassHierarchyInspector & ECSchemaProvider, field: TransformablePropertyField, classSpec: PresentationRules.SingleSchemaClassSpecification | undefined, -): Promise { +): Promise { if (!classSpec) { return [...field.valueClassNames]; } - const matched: EC.FullClassName[] = []; + const matched: EC.FullClassNameDotNotation[] = []; for (const valueClassName of field.valueClassNames) { if (await classMatchesSpec(imodelAccess, valueClassName, classSpec)) { matched.push(valueClassName); diff --git a/packages/content/src/content/extensions/presentation-rules/FieldsProviderFactory.ts b/packages/content/src/content/extensions/presentation-rules/FieldsProviderFactory.ts index f115019e3..e95ebd5d6 100644 --- a/packages/content/src/content/extensions/presentation-rules/FieldsProviderFactory.ts +++ b/packages/content/src/content/extensions/presentation-rules/FieldsProviderFactory.ts @@ -234,7 +234,7 @@ async function mapRelatedPropertiesSpec(props: { imodelAccess: ECSchemaProvider; spec: NormalizedRelatedPropertiesSpec; /** Full class name of the source for the first path step */ - sourceClassName: EC.FullClassName; + sourceClassName: EC.FullClassNameDotNotation; /** Category of the parent scope, used when flattening nested specs */ parentCategoryId?: CategoryDefinition["id"]; }): Promise<{ @@ -253,11 +253,11 @@ async function mapRelatedPropertiesSpec(props: { const isLastStep = i === steps.length - 1; // Determine targetClassName from the step spec or by looking up the relationship in the schema. - let targetClassName: EC.FullClassName; + let targetClassName: EC.FullClassNameDotNotation; if (step.targetClass) { targetClassName = `${step.targetClass.schemaName}.${step.targetClass.className}`; } else { - const relClassName: EC.FullClassName = `${step.relationship.schemaName}.${step.relationship.className}`; + const relClassName: EC.FullClassNameDotNotation = `${step.relationship.schemaName}.${step.relationship.className}`; const relClass = await getClass(imodelAccess, relClassName); if (!relClass.isRelationshipClass()) { throw new Error(`"${relClassName}" is not a relationship class`); @@ -338,7 +338,7 @@ async function mapRelatedPropertiesSpec(props: { async function flattenRelatedPropertiesSpecs(props: { imodelAccess: ECSchemaProvider; specs: PresentationRules.RelatedPropertiesSpecification[]; - sourceClassName: EC.FullClassName; + sourceClassName: EC.FullClassNameDotNotation; parentCategoryId?: CategoryDefinition["id"]; }): Promise<{ declarations: RelatedPropertiesDeclaration[]; diff --git a/packages/content/src/content/extensions/presentation-rules/Utils.ts b/packages/content/src/content/extensions/presentation-rules/Utils.ts index 3328612d0..41e87cd63 100644 --- a/packages/content/src/content/extensions/presentation-rules/Utils.ts +++ b/packages/content/src/content/extensions/presentation-rules/Utils.ts @@ -76,7 +76,7 @@ export async function checkRequiredSchemas( */ export async function classMatchesSpec( imodelAccess: ECClassHierarchyInspector, - className: EC.FullClassName, + className: EC.FullClassNameDotNotation, classSpec: PresentationRules.SingleSchemaClassSpecification | undefined, ): Promise { if (!classSpec) { diff --git a/packages/content/src/content/model/Field.ts b/packages/content/src/content/model/Field.ts index 58c589f2d..5c140fc70 100644 --- a/packages/content/src/content/model/Field.ts +++ b/packages/content/src/content/model/Field.ts @@ -3,13 +3,7 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { - type EC, - type ECSqlBinding, - normalizeFullClassName, - type RelationshipPath, - type ValueDescriptor, -} from "@itwin/presentation-shared"; +import { type EC, type ECSqlBinding, type RelationshipPath, type ValueDescriptor } from "@itwin/presentation-shared"; import { serializeRelationshipPath } from "./Utils.js"; /** @@ -48,7 +42,7 @@ export interface PropertyField extends BaseField { * Drives the SQL column / property metadata. Distinct from {@link (PropertyField:interface).valueClassNames}, * which are the concrete *value-supplier* classes this field represents. */ - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; /** The EC property name within the property's class. */ propertyName: string; /** @@ -72,9 +66,9 @@ export interface PropertyField extends BaseField { * value classes coincide with the target classes, but for a related property they are the * related-endpoint classes rather than the target. * - * Always non-empty, normalized, de-duplicated, and sorted by normalized full name. + * Always non-empty, de-duplicated, and sorted by full name. */ - valueClassNames: EC.FullClassName[]; + valueClassNames: EC.FullClassNameDotNotation[]; /** * ID of the {@link ValueSelector} (column) this field reads. Equals this field's *base* id (its * {@link (PropertyField:namespace).computeId} result without a `forkKey`), so all fork/override @@ -95,12 +89,12 @@ export namespace PropertyField { * its original ID. */ export function computeId(props: { - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; propertyName: string; pathFromTarget?: RelationshipPath; forkKey?: string; }): Field["id"] { - let identity = `${normalizeFullClassName(props.propertyClassName)}.${props.propertyName}`; + let identity = `${props.propertyClassName}.${props.propertyName}`; if (props.pathFromTarget && props.pathFromTarget.length > 0) { identity += `(${serializeRelationshipPath({ path: props.pathFromTarget })})`; } diff --git a/packages/content/src/content/model/Utils.ts b/packages/content/src/content/model/Utils.ts index 68183a424..486b6eac5 100644 --- a/packages/content/src/content/model/Utils.ts +++ b/packages/content/src/content/model/Utils.ts @@ -3,8 +3,6 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { normalizeFullClassName } from "@itwin/presentation-shared"; - import type { EC, RelationshipPath } from "@itwin/presentation-shared"; /** @@ -34,27 +32,23 @@ export function serializeRelationshipPath({ let result = ""; path.forEach((step, index) => { if (result.length === 0) { - result = normalizeFullClassName(step.sourceClassName); + result = step.sourceClassName; } - const rel = step.relationshipReverse - ? `[!${normalizeFullClassName(step.relationshipName)}]` - : `[${normalizeFullClassName(step.relationshipName)}]`; + const rel = step.relationshipReverse ? `[!${step.relationshipName}]` : `[${step.relationshipName}]`; result += `-${rel}`; if (!(omitLastTargetClass && index === path.length - 1)) { - result += `->${normalizeFullClassName(step.targetClassName)}`; + result += `->${step.targetClassName}`; } }); return result; } /** - * Normalizes, de-duplicates, and sorts the given class names. Produces the canonical + * De-duplicates, and sorts the given class names. Produces the canonical * representation used for a property field's `valueClassNames` invariant. */ -export function toSortedUniqueClassNames( - classNames: TClassName[], -): EC.FullClassNameDotNotation[] { - return Array.from(new Set(classNames.map((name) => normalizeFullClassName(name)))).sort(); +export function toSortedUniqueClassNames(classNames: EC.FullClassNameDotNotation[]): EC.FullClassNameDotNotation[] { + return Array.from(new Set(classNames)).sort(); } /** @@ -62,7 +56,7 @@ export function toSortedUniqueClassNames( * `forkKey` when carving a property field. The same subset always yields the same key * (normalized + sorted), so forking the same subset twice produces the same field ID. */ -export function computeFieldForkKey(valueClassNames: EC.FullClassName[]): string { +export function computeFieldForkKey(valueClassNames: EC.FullClassNameDotNotation[]): string { const joined = toSortedUniqueClassNames(valueClassNames).join(";"); // Keep the key human-readable when short; fall back to a stable hash when long. return joined.length <= MAX_READABLE_FORK_KEY_LENGTH ? joined : hashString(joined); diff --git a/packages/content/src/content/model/ValueSelector.ts b/packages/content/src/content/model/ValueSelector.ts index cd3869ec3..f1d3d2312 100644 --- a/packages/content/src/content/model/ValueSelector.ts +++ b/packages/content/src/content/model/ValueSelector.ts @@ -65,7 +65,7 @@ export interface CalculatedValueSelector extends Pick { const result = await resolveContentSources({ imodelAccess, - targets: [{ primaryClass: "TestSchema:ClassA" }], + targets: [{ primaryClass: "TestSchema.ClassA" }], config: { imodelFieldsProviders: [provider] }, }); diff --git a/packages/content/src/test/MetadataStubs.ts b/packages/content/src/test/MetadataStubs.ts index 40e2ba988..d7eaa0937 100644 --- a/packages/content/src/test/MetadataStubs.ts +++ b/packages/content/src/test/MetadataStubs.ts @@ -3,7 +3,7 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { normalizeFullClassName } from "@itwin/presentation-shared"; +import { normalizeFullClassName, parseFullClassName } from "@itwin/presentation-shared"; import type { EC, ECClassHierarchyInspector, ECSchemaProvider } from "@itwin/presentation-shared"; @@ -16,9 +16,9 @@ export function createPrimitiveProperty(props: { /** When `true`, the property reports as an array of the given primitive type. */ array?: boolean; /** Full name of the class that declares the property (defaults to the owning class). */ - declaringClassName?: EC.FullClassName; + declaringClassName?: EC.FullClassNameDotNotation; /** EC schema property category assigned to the property, if any. */ - category?: { fullName: EC.FullClassName; label?: string }; + category?: { fullName: EC.FullClassNameDotNotation; label?: string }; }): EC.Property { return { name: props.name, @@ -46,7 +46,7 @@ export function createPrimitiveProperty(props: { /** Creates an entity `EC.Class` stub for tests. */ export function createEntityClass(props: { - fullName: EC.FullClassName; + fullName: EC.FullClassNameDotNotation; label?: string; /** All properties visible on the class (own + inherited), returned by `getProperties`. */ properties?: EC.Property[]; @@ -62,10 +62,7 @@ export function createEntityClass(props: { /** Classes that derive directly or indirectly from this class. */ derivedClasses?: EC.Class[]; }): EC.EntityClass { - const normalized = normalizeFullClassName(props.fullName); - const dotIndex = normalized.indexOf("."); - const schemaName = normalized.slice(0, dotIndex); - const className = normalized.slice(dotIndex + 1); + const { schemaName, className } = parseFullClassName(props.fullName); return { schema: { name: schemaName } as unknown as EC.Schema, fullName: props.fullName, @@ -88,16 +85,15 @@ export function createEntityClass(props: { /** Creates a mixin class stub for tests. */ export function createMixinClass(props: { - fullName: EC.FullClassName; + fullName: EC.FullClassNameDotNotation; ownProperties?: EC.Property[]; baseClass?: EC.Class; }): EC.Mixin { - const normalized = normalizeFullClassName(props.fullName); - const dotIndex = normalized.indexOf("."); + const { schemaName, className } = parseFullClassName(props.fullName); return { - schema: { name: normalized.slice(0, dotIndex) } as unknown as EC.Schema, + schema: { name: schemaName } as unknown as EC.Schema, fullName: props.fullName, - name: normalized.slice(dotIndex + 1), + name: className, baseClass: Promise.resolve(props.baseClass), is: async () => false, getProperty: async (name: string) => props.ownProperties?.find((property) => property.name === name), @@ -117,7 +113,7 @@ export function createMixinClass(props: { * looked up by their (normalized) full name. `classDerivesFrom` walks the stubs' `baseClass` chain. */ export function createSchemaAccess(classes: EC.Class[]): ECSchemaProvider & ECClassHierarchyInspector { - const byFullName = new Map(classes.map((cls) => [normalizeFullClassName(cls.fullName), cls])); + const byFullName = new Map(classes.map((cls) => [cls.fullName, cls])); return { getSchema: async (schemaName: string) => ({ name: schemaName, @@ -129,7 +125,7 @@ export function createSchemaAccess(classes: EC.Class[]): ECSchemaProvider & ECCl const target = normalizeFullClassName(candidateBaseClassFullName); let current = byFullName.get(normalizeFullClassName(derivedClassFullName)); while (current) { - if (normalizeFullClassName(current.fullName) === target) { + if (current.fullName === target) { return true; } current = await current.baseClass; diff --git a/packages/content/src/test/descriptor-building/BuildDescriptor.test.ts b/packages/content/src/test/descriptor-building/BuildDescriptor.test.ts index 859d9ae0e..ed87f7843 100644 --- a/packages/content/src/test/descriptor-building/BuildDescriptor.test.ts +++ b/packages/content/src/test/descriptor-building/BuildDescriptor.test.ts @@ -16,8 +16,8 @@ import type { ExternalFieldsProvider } from "../../content/extensions/ExternalFi import type { IModelFieldsProvider } from "../../content/extensions/IModelFieldsProvider.js"; function createSource( - primaryClass: EC.FullClassName, - resolvedPrimaryClasses: EC.FullClassName[] = [primaryClass], + primaryClass: EC.FullClassNameDotNotation, + resolvedPrimaryClasses: EC.FullClassNameDotNotation[] = [primaryClass], ): ContentSource { return { target: { primaryClass }, resolvedPrimaryClasses, resolvedDeclarations: [] }; } diff --git a/packages/content/src/test/descriptor-building/ClassPropertyFields.test.ts b/packages/content/src/test/descriptor-building/ClassPropertyFields.test.ts index b34bd5b51..3bc2f4ca9 100644 --- a/packages/content/src/test/descriptor-building/ClassPropertyFields.test.ts +++ b/packages/content/src/test/descriptor-building/ClassPropertyFields.test.ts @@ -14,7 +14,7 @@ const path: RelationshipPath = [ { sourceClassName: "TestSchema.A", targetClassName: "TestSchema.B", relationshipName: "TestSchema.AtoB" }, ]; -function createSingleClassIModelAccess(fullName: EC.FullClassName, properties: EC.Property[]) { +function createSingleClassIModelAccess(fullName: EC.FullClassNameDotNotation, properties: EC.Property[]) { return createSchemaAccess([createEntityClass({ fullName, properties })]); } diff --git a/packages/content/src/test/descriptor-building/DirectFields.test.ts b/packages/content/src/test/descriptor-building/DirectFields.test.ts index 886f0c6e0..1f02e8788 100644 --- a/packages/content/src/test/descriptor-building/DirectFields.test.ts +++ b/packages/content/src/test/descriptor-building/DirectFields.test.ts @@ -12,8 +12,8 @@ import type { ContentSource } from "../../content/ContentTarget.js"; import type { PropertyField } from "../../content/model/Field.js"; function createSource(props: { - primaryClass: EC.FullClassName; - resolvedPrimaryClasses?: EC.FullClassName[]; + primaryClass: EC.FullClassNameDotNotation; + resolvedPrimaryClasses?: EC.FullClassNameDotNotation[]; }): ContentSource { return { target: { primaryClass: props.primaryClass }, @@ -89,7 +89,7 @@ describe("collectDirectPropertyFields", () => { const [field] = await enumerate({ imodelAccess, - source: createSource({ primaryClass: "TestSchema:Element", resolvedPrimaryClasses: [] }), + source: createSource({ primaryClass: "TestSchema.Element", resolvedPrimaryClasses: [] }), }); expect(field.valueClassNames).to.deep.equal(["TestSchema.Element"]); diff --git a/packages/content/src/test/descriptor-building/PropertyFieldMerge.test.ts b/packages/content/src/test/descriptor-building/PropertyFieldMerge.test.ts index 0970fd17f..fcc922fff 100644 --- a/packages/content/src/test/descriptor-building/PropertyFieldMerge.test.ts +++ b/packages/content/src/test/descriptor-building/PropertyFieldMerge.test.ts @@ -10,9 +10,9 @@ import { PropertyField } from "../../content/model/Field.js"; import type { EC } from "@itwin/presentation-shared"; function createField(props: { - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; propertyName: string; - valueClassNames: EC.FullClassName[]; + valueClassNames: EC.FullClassNameDotNotation[]; label?: string; hidden?: boolean; readOnly?: boolean; @@ -58,12 +58,12 @@ describe("mergePropertyFieldsByIdentity", () => { it("passes a single candidate through, keyed by base id, with normalized target classes", () => { const field = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window", "Stuff:Door"], + valueClassNames: ["Stuff.Window", "Stuff.Door"], }); const result = merge([field]); - const id = PropertyField.computeId({ propertyClassName: "Stuff:Thing", propertyName: "Height" }); + const id = PropertyField.computeId({ propertyClassName: "Stuff.Thing", propertyName: "Height" }); expect(result).to.deep.equal({ [id]: { ...field, id, selectorId: id, valueClassNames: ["Stuff.Door", "Stuff.Window"] }, }); @@ -71,70 +71,70 @@ describe("mergePropertyFieldsByIdentity", () => { it("does not mutate the input candidate", () => { const field = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], }); merge([field]); - expect(field.id).to.equal(PropertyField.computeId({ propertyClassName: "Stuff:Thing", propertyName: "Height" })); - expect(field.valueClassNames).to.deep.equal(["Stuff:Window"]); + expect(field.id).to.equal(PropertyField.computeId({ propertyClassName: "Stuff.Thing", propertyName: "Height" })); + expect(field.valueClassNames).to.deep.equal(["Stuff.Window"]); }); it("unions target classes of direct-property candidates that share identity", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], }); const result = merge([a, b]); - const id = PropertyField.computeId({ propertyClassName: "Stuff:Thing", propertyName: "Height" }); + const id = PropertyField.computeId({ propertyClassName: "Stuff.Thing", propertyName: "Height" }); expect(result[id].valueClassNames).to.deep.equal(["Stuff.Door", "Stuff.Window"]); }); it("de-duplicates overlapping target classes across candidates", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door", "Stuff:Window"], + valueClassNames: ["Stuff.Door", "Stuff.Window"], }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window", "Stuff:Roof"], + valueClassNames: ["Stuff.Window", "Stuff.Roof"], }); const result = merge([a, b]); - const id = PropertyField.computeId({ propertyClassName: "Stuff:Thing", propertyName: "Height" }); + const id = PropertyField.computeId({ propertyClassName: "Stuff.Thing", propertyName: "Height" }); expect(result[id].valueClassNames).to.deep.equal(["Stuff.Door", "Stuff.Roof", "Stuff.Window"]); }); it("unions related-endpoint candidates that share identity", () => { const path: PropertyField["pathFromTarget"] = [ { - sourceClassName: "BisCore:Element", - targetClassName: "BisCore:ExternalSourceAspect", - relationshipName: "BisCore:ElementOwnsMultiAspects", + sourceClassName: "BisCore.Element", + targetClassName: "BisCore.ExternalSourceAspect", + relationshipName: "BisCore.ElementOwnsMultiAspects", }, ]; const a = createField({ - propertyClassName: "BisCore:ExternalSourceAspect", + propertyClassName: "BisCore.ExternalSourceAspect", propertyName: "Identifier", pathFromTarget: path, - valueClassNames: ["BisCore:ExternalSourceAspectX"], + valueClassNames: ["BisCore.ExternalSourceAspectX"], }); const b = createField({ - propertyClassName: "BisCore:ExternalSourceAspect", + propertyClassName: "BisCore.ExternalSourceAspect", propertyName: "Identifier", pathFromTarget: path, - valueClassNames: ["BisCore:ExternalSourceAspectY"], + valueClassNames: ["BisCore.ExternalSourceAspectY"], }); const result = merge([a, b]); const id = PropertyField.computeId({ - propertyClassName: "BisCore:ExternalSourceAspect", + propertyClassName: "BisCore.ExternalSourceAspect", propertyName: "Identifier", pathFromTarget: path, }); @@ -146,26 +146,26 @@ describe("mergePropertyFieldsByIdentity", () => { it("keeps candidates with distinct identities separate", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], }); - const b = createField({ propertyClassName: "Stuff:Thing", propertyName: "Width", valueClassNames: ["Stuff:Door"] }); + const b = createField({ propertyClassName: "Stuff.Thing", propertyName: "Width", valueClassNames: ["Stuff.Door"] }); const result = merge([a, b]); expect(Object.keys(result)).to.have.length(2); }); it("throws when grouped candidates have divergent metadata", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], label: "One", }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], label: "Two", }); expect(() => merge([a, b])).to.throw(/divergent metadata/); @@ -173,14 +173,14 @@ describe("mergePropertyFieldsByIdentity", () => { it("throws when grouped candidates have divergent category facts", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], }); expect(() => mergePropertyFieldsByIdentity([ @@ -192,14 +192,14 @@ describe("mergePropertyFieldsByIdentity", () => { it("merges candidates that agree on schema category facts", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], }); const schema = { anchor: "targetClass", category: { source: "schema", id: "Geo", label: "Geometry" } } as const; const result = toRecord( @@ -208,21 +208,21 @@ describe("mergePropertyFieldsByIdentity", () => { { field: b, categorization: schema, provider: { id: "p_v1" } }, ]), ); - const id = PropertyField.computeId({ propertyClassName: "Stuff:Thing", propertyName: "Height" }); + const id = PropertyField.computeId({ propertyClassName: "Stuff.Thing", propertyName: "Height" }); expect(result[id].valueClassNames).to.deep.equal(["Stuff.Door", "Stuff.Window"]); }); it("throws when grouped candidates have divergent hidden flag", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], hidden: true, }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], hidden: false, }); expect(() => merge([a, b])).to.throw(/divergent metadata/); @@ -230,15 +230,15 @@ describe("mergePropertyFieldsByIdentity", () => { it("throws when grouped candidates have divergent readOnly flag", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], readOnly: true, }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], readOnly: false, }); expect(() => merge([a, b])).to.throw(/divergent metadata/); @@ -246,15 +246,15 @@ describe("mergePropertyFieldsByIdentity", () => { it("throws when grouped candidates have divergent value type", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], type: { kind: "primitive", type: "String" }, }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], type: { kind: "primitive", type: "Double" }, }); expect(() => merge([a, b])).to.throw(/divergent metadata/); @@ -262,15 +262,15 @@ describe("mergePropertyFieldsByIdentity", () => { it("throws when grouped navigation candidates have divergent target class", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Owner", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], type: { kind: "navigation", targetClassName: "Stuff.Person" }, }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Owner", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], type: { kind: "navigation", targetClassName: "Stuff.Organization" }, }); expect(() => merge([a, b])).to.throw(/divergent metadata/); @@ -278,19 +278,19 @@ describe("mergePropertyFieldsByIdentity", () => { it("merges navigation candidates that share a target class", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Owner", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], type: { kind: "navigation", targetClassName: "Stuff.Person" }, }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Owner", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], type: { kind: "navigation", targetClassName: "Stuff.Person" }, }); const result = merge([a, b]); - const id = PropertyField.computeId({ propertyClassName: "Stuff:Thing", propertyName: "Owner" }); + const id = PropertyField.computeId({ propertyClassName: "Stuff.Thing", propertyName: "Owner" }); expect(result[id].valueClassNames).to.deep.equal(["Stuff.Door", "Stuff.Window"]); }); @@ -306,19 +306,19 @@ describe("mergePropertyFieldsByIdentity", () => { ], }); const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Point", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], type: type(), }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Point", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], type: type(), }); const result = merge([a, b]); - const id = PropertyField.computeId({ propertyClassName: "Stuff:Thing", propertyName: "Point" }); + const id = PropertyField.computeId({ propertyClassName: "Stuff.Thing", propertyName: "Point" }); expect(result[id].valueClassNames).to.deep.equal(["Stuff.Door", "Stuff.Window"]); }); @@ -336,27 +336,27 @@ describe("mergePropertyFieldsByIdentity", () => { }, }); const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Color", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], type: type(), }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Color", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], type: type(), }); const result = merge([a, b]); - const id = PropertyField.computeId({ propertyClassName: "Stuff:Thing", propertyName: "Color" }); + const id = PropertyField.computeId({ propertyClassName: "Stuff.Thing", propertyName: "Color" }); expect(result[id].valueClassNames).to.deep.equal(["Stuff.Door", "Stuff.Window"]); }); it("throws when grouped candidates have divergent enumeration metadata", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Color", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], type: { kind: "primitive", type: "Integer", @@ -364,9 +364,9 @@ describe("mergePropertyFieldsByIdentity", () => { }, }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Color", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], type: { kind: "primitive", type: "Integer", @@ -378,15 +378,15 @@ describe("mergePropertyFieldsByIdentity", () => { it("resolves inter-provider metadata conflicts in favor of the higher priority, unioning value classes", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], label: "Low priority", }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], label: "High priority", }); const result = toRecord( @@ -395,22 +395,22 @@ describe("mergePropertyFieldsByIdentity", () => { { field: b, categorization: { anchor: "none" }, provider: { id: "b_v1", priority: 2 } }, ]), ); - const id = PropertyField.computeId({ propertyClassName: "Stuff:Thing", propertyName: "Height" }); + const id = PropertyField.computeId({ propertyClassName: "Stuff.Thing", propertyName: "Height" }); expect(result[id].label).to.equal("High priority"); expect(result[id].valueClassNames).to.deep.equal(["Stuff.Door", "Stuff.Window"]); }); it("resolves inter-provider ties in favor of input order", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], label: "First", }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], label: "Second", }); const result = toRecord( @@ -419,21 +419,21 @@ describe("mergePropertyFieldsByIdentity", () => { { field: b, categorization: { anchor: "none" }, provider: { id: "b_v1", priority: 5 } }, ]), ); - const id = PropertyField.computeId({ propertyClassName: "Stuff:Thing", propertyName: "Height" }); + const id = PropertyField.computeId({ propertyClassName: "Stuff.Thing", propertyName: "Height" }); expect(result[id].label).to.equal("First"); }); it("throws when the same provider produces divergent metadata for one field id", () => { const a = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], label: "One", }); const b = createField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Window"], + valueClassNames: ["Stuff.Window"], label: "Two", }); expect(() => diff --git a/packages/content/src/test/descriptor-building/RelatedFields.test.ts b/packages/content/src/test/descriptor-building/RelatedFields.test.ts index 27b3d06d9..222c663ac 100644 --- a/packages/content/src/test/descriptor-building/RelatedFields.test.ts +++ b/packages/content/src/test/descriptor-building/RelatedFields.test.ts @@ -54,7 +54,7 @@ function createSource(resolvedDeclarations: ContentSource["resolvedDeclarations" return { target: { primaryClass: "TestSchema.A" }, resolvedPrimaryClasses: ["TestSchema.A"], resolvedDeclarations }; } -function resolvedPath(path: RelationshipPath, targetClassNames: EC.FullClassName[]): ResolvedPath { +function resolvedPath(path: RelationshipPath, targetClassNames: EC.FullClassNameDotNotation[]): ResolvedPath { return { path, targetClassNames }; } diff --git a/packages/content/src/test/descriptor-building/Selectors.test.ts b/packages/content/src/test/descriptor-building/Selectors.test.ts index ac6d59ca7..f01ae9489 100644 --- a/packages/content/src/test/descriptor-building/Selectors.test.ts +++ b/packages/content/src/test/descriptor-building/Selectors.test.ts @@ -15,9 +15,9 @@ import type { ContentDescriptor } from "../../content/model/ContentDescriptor.js import type { CalculatedField, Field, PropertyField as PropertyFieldType } from "../../content/model/Field.js"; function propertyField(props: { - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; propertyName: string; - valueClassNames: string[]; + valueClassNames: EC.FullClassNameDotNotation[]; label?: string; pathFromTarget?: PropertyFieldType["pathFromTarget"]; }): PropertyFieldType { @@ -35,7 +35,7 @@ function propertyField(props: { propertyClassName: props.propertyClassName, propertyName: props.propertyName, pathFromTarget: props.pathFromTarget ?? [], - valueClassNames: toSortedUniqueClassNames(props.valueClassNames as EC.FullClassName[]), + valueClassNames: toSortedUniqueClassNames(props.valueClassNames), }; } @@ -65,9 +65,9 @@ describe("ValueSelector", () => { describe("collectSelectors", () => { it("produces one selector per SQL-backed field", () => { const prop = propertyField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], }); const calc = calculatedField({ id: "provider:calc", expression: "1" }); const selectors = collectSelectors({ fields: [prop, calc], externalInputs: [] }); @@ -95,12 +95,12 @@ describe("ValueSelector", () => { it("deduplicates a property field and its fork into a single selector", () => { const field = propertyField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door", "Stuff:Window"], + valueClassNames: ["Stuff.Door", "Stuff.Window"], }); const descriptor = createDescriptor([field]); - const fork = createTransformableDescriptor(descriptor).forkField(field.id, ["Stuff:Door"]); + const fork = createTransformableDescriptor(descriptor).forkField(field.id, ["Stuff.Door"]); expect(fork.id).to.not.equal(field.id); const selectors = collectSelectors({ fields: Object.values(descriptor.fields), externalInputs: [] }); @@ -110,14 +110,14 @@ describe("ValueSelector", () => { it("adds a field-less selector for an external input with no matching field", () => { const selectors = collectSelectors({ fields: [], - externalInputs: [{ propertyClassName: "Stuff:Thing", propertyName: "Height" }], + externalInputs: [{ propertyClassName: "Stuff.Thing", propertyName: "Height" }], }); - const id = computePropertySelectorId({ propertyClassName: "Stuff:Thing", propertyName: "Height" }); + const id = computePropertySelectorId({ propertyClassName: "Stuff.Thing", propertyName: "Height" }); expect(Object.keys(selectors)).to.deep.equal([id]); expect(selectors[id]).to.deep.equal({ kind: "property", id, - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", pathFromTarget: [], }); @@ -125,30 +125,30 @@ describe("ValueSelector", () => { it("reuses the field-backed selector for an external input matching a field (no duplicate)", () => { const prop = propertyField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], }); const selectors = collectSelectors({ fields: [prop], - externalInputs: [{ propertyClassName: "Stuff:Thing", propertyName: "Height" }], + externalInputs: [{ propertyClassName: "Stuff.Thing", propertyName: "Height" }], }); expect(Object.keys(selectors)).to.deep.equal([prop.selectorId]); }); it("drops a removed output field's selector on recompute, but keeps it when it is also an external input (pinning replacement)", () => { const removable = propertyField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], }); const inputBacked = propertyField({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Width", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], }); const descriptor = createDescriptor([removable, inputBacked]); - const externalInputs = [{ propertyClassName: "Stuff:Thing" as EC.FullClassName, propertyName: "Width" }]; + const externalInputs = [{ propertyClassName: "Stuff.Thing" as const, propertyName: "Width" }]; const transformable = createTransformableDescriptor(descriptor); transformable.removeField(removable.id); diff --git a/packages/content/src/test/extensions/DescriptorTransformer.test.ts b/packages/content/src/test/extensions/DescriptorTransformer.test.ts index abdf2401e..8b472c49d 100644 --- a/packages/content/src/test/extensions/DescriptorTransformer.test.ts +++ b/packages/content/src/test/extensions/DescriptorTransformer.test.ts @@ -13,9 +13,9 @@ import type { ContentDescriptor } from "../../content/model/ContentDescriptor.js import type { CalculatedField, Field } from "../../content/model/Field.js"; function propertyField(props: { - sourceClassName: EC.FullClassName; + sourceClassName: EC.FullClassNameDotNotation; propertyName: string; - valueClassNames: EC.FullClassName[]; + valueClassNames: EC.FullClassNameDotNotation[]; pathFromTarget?: PropertyField["pathFromTarget"]; }): PropertyField { const fieldId = PropertyField.computeId({ @@ -43,9 +43,9 @@ function createDescriptor(fields: Field[]): ContentDescriptor { describe("createTransformableDescriptor", () => { it("exposes sources, categories, and fields of the backing descriptor", () => { const field = propertyField({ - sourceClassName: "Stuff:Thing", + sourceClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], }); const descriptor = createDescriptor([field]); const transformable = createTransformableDescriptor(descriptor); @@ -57,9 +57,9 @@ describe("createTransformableDescriptor", () => { describe("removeField", () => { it("removes a field from the backing descriptor", () => { const field = propertyField({ - sourceClassName: "Stuff:Thing", + sourceClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], }); const descriptor = createDescriptor([field]); const transformable = createTransformableDescriptor(descriptor); @@ -71,16 +71,16 @@ describe("createTransformableDescriptor", () => { describe("forkField", () => { it("carves a strict subset into a new field and shrinks the original", () => { const field = propertyField({ - sourceClassName: "Stuff:Thing", + sourceClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door", "Stuff:Window", "Stuff:Roof"], + valueClassNames: ["Stuff.Door", "Stuff.Window", "Stuff.Roof"], }); const descriptor = createDescriptor([field]); const transformable = createTransformableDescriptor(descriptor); - const fork = transformable.forkField(field.id, ["Stuff:Door"]); + const fork = transformable.forkField(field.id, ["Stuff.Door"]); const forkedId = PropertyField.computeId({ - propertyClassName: "Stuff:Thing", + propertyClassName: "Stuff.Thing", propertyName: "Height", forkKey: computeFieldForkKey(["Stuff.Door"]), }); @@ -90,41 +90,41 @@ describe("createTransformableDescriptor", () => { expect(descriptor.fields[forkedId]).to.equal(fork); // original is shrunk to the remainder expect(field.valueClassNames).to.deep.equal(["Stuff.Roof", "Stuff.Window"]); - expect(field.id).to.equal(PropertyField.computeId({ propertyClassName: "Stuff:Thing", propertyName: "Height" })); + expect(field.id).to.equal(PropertyField.computeId({ propertyClassName: "Stuff.Thing", propertyName: "Height" })); }); it("carves a related-endpoint subclass over a relationship path", () => { const path: PropertyField["pathFromTarget"] = [ { - sourceClassName: "BisCore:Element", - targetClassName: "BisCore:ExternalSourceAspect", - relationshipName: "BisCore:ElementOwnsMultiAspects", + sourceClassName: "BisCore.Element", + targetClassName: "BisCore.ExternalSourceAspect", + relationshipName: "BisCore.ElementOwnsMultiAspects", }, ]; const field = propertyField({ - sourceClassName: "BisCore:ExternalSourceAspect", + sourceClassName: "BisCore.ExternalSourceAspect", propertyName: "Identifier", pathFromTarget: path, - valueClassNames: ["BisCore:ExternalSourceAspectX", "BisCore:ExternalSourceAspectY"], + valueClassNames: ["BisCore.ExternalSourceAspectX", "BisCore.ExternalSourceAspectY"], }); const descriptor = createDescriptor([field]); const transformable = createTransformableDescriptor(descriptor); - const fork = transformable.forkField(field.id, ["BisCore:ExternalSourceAspectX"]); + const fork = transformable.forkField(field.id, ["BisCore.ExternalSourceAspectX"]); expect(fork.valueClassNames).to.deep.equal(["BisCore.ExternalSourceAspectX"]); expect(field.valueClassNames).to.deep.equal(["BisCore.ExternalSourceAspectY"]); }); it("returns the original field in place when the subset covers all classes", () => { const field = propertyField({ - sourceClassName: "Stuff:Thing", + sourceClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door", "Stuff:Window"], + valueClassNames: ["Stuff.Door", "Stuff.Window"], }); const descriptor = createDescriptor([field]); const transformable = createTransformableDescriptor(descriptor); - const fork = transformable.forkField(field.id, ["Stuff:Window", "Stuff:Door"]); + const fork = transformable.forkField(field.id, ["Stuff.Window", "Stuff.Door"]); expect(fork).to.equal(field); expect(Object.keys(descriptor.fields)).to.deep.equal([field.id]); expect(field.valueClassNames).to.deep.equal(["Stuff.Door", "Stuff.Window"]); @@ -132,15 +132,15 @@ describe("createTransformableDescriptor", () => { it("is idempotent — forking the same subset twice returns the same field", () => { const field = propertyField({ - sourceClassName: "Stuff:Thing", + sourceClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door", "Stuff:Window"], + valueClassNames: ["Stuff.Door", "Stuff.Window"], }); const descriptor = createDescriptor([field]); const transformable = createTransformableDescriptor(descriptor); - const first = transformable.forkField(field.id, ["Stuff:Door"]); - const second = transformable.forkField(field.id, ["Stuff:Door"]); + const first = transformable.forkField(field.id, ["Stuff.Door"]); + const second = transformable.forkField(field.id, ["Stuff.Door"]); expect(second).to.equal(first); // original stays shrunk to the remainder, not shrunk twice expect(field.valueClassNames).to.deep.equal(["Stuff.Window"]); @@ -149,27 +149,27 @@ describe("createTransformableDescriptor", () => { it("keeps the forked field's target classes independent of the original", () => { const field = propertyField({ - sourceClassName: "Stuff:Thing", + sourceClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door", "Stuff:Window"], + valueClassNames: ["Stuff.Door", "Stuff.Window"], }); const descriptor = createDescriptor([field]); const transformable = createTransformableDescriptor(descriptor); - const fork = transformable.forkField(field.id, ["Stuff:Door"]); + const fork = transformable.forkField(field.id, ["Stuff.Door"]); expect(fork.valueClassNames).to.not.equal(field.valueClassNames); }); it("copies the parent's selectorId onto the fork", () => { const field = propertyField({ - sourceClassName: "Stuff:Thing", + sourceClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door", "Stuff:Window"], + valueClassNames: ["Stuff.Door", "Stuff.Window"], }); const descriptor = createDescriptor([field]); const transformable = createTransformableDescriptor(descriptor); - const fork = transformable.forkField(field.id, ["Stuff:Door"]); + const fork = transformable.forkField(field.id, ["Stuff.Door"]); expect(fork.id).to.not.equal(field.selectorId); expect(fork.selectorId).to.equal(field.selectorId); }); @@ -177,7 +177,7 @@ describe("createTransformableDescriptor", () => { it("throws when the field does not exist", () => { const descriptor = createDescriptor([]); const transformable = createTransformableDescriptor(descriptor); - expect(() => transformable.forkField("missing", ["Stuff:Door"])).to.throw(/no such field/); + expect(() => transformable.forkField("missing", ["Stuff.Door"])).to.throw(/no such field/); }); it("throws when the field is not a property field", () => { @@ -191,21 +191,21 @@ describe("createTransformableDescriptor", () => { }; const descriptor = createDescriptor([calculated]); const transformable = createTransformableDescriptor(descriptor); - expect(() => transformable.forkField("calc", ["Stuff:Door"])).to.throw(/only property fields/); + expect(() => transformable.forkField("calc", ["Stuff.Door"])).to.throw(/only property fields/); }); it("throws when the field represents no value-supplier classes", () => { - const field = propertyField({ sourceClassName: "Stuff:Thing", propertyName: "Height", valueClassNames: [] }); + const field = propertyField({ sourceClassName: "Stuff.Thing", propertyName: "Height", valueClassNames: [] }); const descriptor = createDescriptor([field]); const transformable = createTransformableDescriptor(descriptor); - expect(() => transformable.forkField(field.id, ["Stuff:Door"])).to.throw(/represents no value-supplier classes/); + expect(() => transformable.forkField(field.id, ["Stuff.Door"])).to.throw(/represents no value-supplier classes/); }); it("throws when the target class subset is empty", () => { const field = propertyField({ - sourceClassName: "Stuff:Thing", + sourceClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], }); const descriptor = createDescriptor([field]); const transformable = createTransformableDescriptor(descriptor); @@ -214,13 +214,13 @@ describe("createTransformableDescriptor", () => { it("throws when a subset class is not represented by the field", () => { const field = propertyField({ - sourceClassName: "Stuff:Thing", + sourceClassName: "Stuff.Thing", propertyName: "Height", - valueClassNames: ["Stuff:Door"], + valueClassNames: ["Stuff.Door"], }); const descriptor = createDescriptor([field]); const transformable = createTransformableDescriptor(descriptor); - expect(() => transformable.forkField(field.id, ["Stuff:Window"])).to.throw(/not represented/); + expect(() => transformable.forkField(field.id, ["Stuff.Window"])).to.throw(/not represented/); }); }); }); diff --git a/packages/content/src/test/extensions/ecexpressions/ECExpressionToECSql.test.ts b/packages/content/src/test/extensions/ecexpressions/ECExpressionToECSql.test.ts index 2433e3f9f..c29b32f98 100644 --- a/packages/content/src/test/extensions/ecexpressions/ECExpressionToECSql.test.ts +++ b/packages/content/src/test/extensions/ecexpressions/ECExpressionToECSql.test.ts @@ -329,7 +329,7 @@ describe("convertECExpressionToECSql", () => { describe("related instances", () => { it("emits EXISTS for the HasRelatedInstance string form (backward)", async () => { const result = await convertECExpressionToECSql({ - expression: `this.HasRelatedInstance("BisCore:ModelContainsElements", "Backward", "BisCore:Model")`, + expression: `this.HasRelatedInstance("BisCore.ModelContainsElements", "Backward", "BisCore.Model")`, }); expect(result.bindings).to.be.undefined; expect(trimWhitespace(result.ecsql)).to.equal( @@ -347,7 +347,7 @@ describe("convertECExpressionToECSql", () => { it("emits COUNT for the GetRelatedInstancesCount string form (forward)", async () => { const result = await convertECExpressionToECSql({ - expression: `this.GetRelatedInstancesCount("BisCore:ModelContainsElements", "Forward", "BisCore:Element")`, + expression: `this.GetRelatedInstancesCount("BisCore.ModelContainsElements", "Forward", "BisCore.Element")`, }); expect(trimWhitespace(result.ecsql)).to.equal( trimWhitespace(` @@ -364,7 +364,7 @@ describe("convertECExpressionToECSql", () => { it("emits a scalar subquery for the GetRelatedValue string form", async () => { const result = await convertECExpressionToECSql({ - expression: `this.GetRelatedValue("BisCore:ModelContainsElements", "Forward", "BisCore:Element", "UserLabel")`, + expression: `this.GetRelatedValue("BisCore.ModelContainsElements", "Forward", "BisCore.Element", "UserLabel")`, }); expect(trimWhitespace(result.ecsql)).to.equal( trimWhitespace(` @@ -382,7 +382,7 @@ describe("convertECExpressionToECSql", () => { it("emits a label subquery for the GetRelatedDisplayLabel string form", async () => { const result = await convertECExpressionToECSql({ - expression: `this.GetRelatedDisplayLabel("BisCore:ModelContainsElements", "Forward", "BisCore:Element")`, + expression: `this.GetRelatedDisplayLabel("BisCore.ModelContainsElements", "Forward", "BisCore.Element")`, labelSelectClauseFactory, }); expect(trimWhitespace(result.ecsql)).to.equal( @@ -401,7 +401,7 @@ describe("convertECExpressionToECSql", () => { it("emits EXISTS for the HasRelatedInstance lambda form", async () => { const result = await convertECExpressionToECSql({ - expression: `this.HasRelatedInstance("BisCore:Element", e => e.UserLabel = "test")`, + expression: `this.HasRelatedInstance("BisCore.Element", e => e.UserLabel = "test")`, }); expect(trimWhitespace(result.ecsql)).to.equal( trimWhitespace(` @@ -417,7 +417,7 @@ describe("convertECExpressionToECSql", () => { it("emits COUNT for the GetRelatedInstancesCount lambda form", async () => { const result = await convertECExpressionToECSql({ - expression: `this.GetRelatedInstancesCount("BisCore:Element", e => e.Name = "x")`, + expression: `this.GetRelatedInstancesCount("BisCore.Element", e => e.Name = "x")`, }); expect(trimWhitespace(result.ecsql)).to.equal( trimWhitespace(` @@ -433,7 +433,7 @@ describe("convertECExpressionToECSql", () => { it("emits a scalar subquery for the GetRelatedValue lambda form", async () => { const result = await convertECExpressionToECSql({ - expression: `this.GetRelatedValue("BisCore:Element", "UserLabel", e => e.Name = "x")`, + expression: `this.GetRelatedValue("BisCore.Element", "UserLabel", e => e.Name = "x")`, }); expect(trimWhitespace(result.ecsql)).to.equal( trimWhitespace(` @@ -450,7 +450,7 @@ describe("convertECExpressionToECSql", () => { it("emits a label subquery for the GetRelatedDisplayLabel lambda form", async () => { const result = await convertECExpressionToECSql({ - expression: `this.GetRelatedDisplayLabel("BisCore:Element", e => e.Name = "x")`, + expression: `this.GetRelatedDisplayLabel("BisCore.Element", e => e.Name = "x")`, labelSelectClauseFactory, }); expect(trimWhitespace(result.ecsql)).to.equal( @@ -721,7 +721,7 @@ describe("convertECExpressionToECSql", () => { it("throws when a related-instance function is called without a receiver", async () => { await expect( - convertECExpressionToECSql({ expression: `HasRelatedInstance("A:B", "Forward", "C:D")` }), + convertECExpressionToECSql({ expression: `HasRelatedInstance("A.B", "Forward", "C.D")` }), ).rejects.toThrow(/must be called on an instance/); }); @@ -733,20 +733,20 @@ describe("convertECExpressionToECSql", () => { it("throws for an invalid full class name", async () => { await expect( - convertECExpressionToECSql({ expression: `this.HasRelatedInstance("NoColon", "Forward", "Bis:Model")` }), + convertECExpressionToECSql({ expression: `this.HasRelatedInstance("NoColon", "Forward", "Bis.Model")` }), ).rejects.toThrow(/Invalid full class name/); }); it("throws for an invalid related property identifier", async () => { await expect( - convertECExpressionToECSql({ expression: `this.GetRelatedValue("S:C", "Forward", "S2:C2", "Bad Name")` }), + convertECExpressionToECSql({ expression: `this.GetRelatedValue("S.C", "Forward", "S2.C2", "Bad Name")` }), ).rejects.toThrow(/Invalid identifier/); }); it("throws for an invalid relationship direction", async () => { await expect( convertECExpressionToECSql({ - expression: `this.HasRelatedInstance("BisCore:Rel", "Sideways", "BisCore:Model")`, + expression: `this.HasRelatedInstance("BisCore.Rel", "Sideways", "BisCore.Model")`, }), ).rejects.toThrow(/direction/); }); diff --git a/packages/content/src/test/extensions/presentation-rules/DescriptorTransformerFactory.test.ts b/packages/content/src/test/extensions/presentation-rules/DescriptorTransformerFactory.test.ts index d0150c4c0..77b445e49 100644 --- a/packages/content/src/test/extensions/presentation-rules/DescriptorTransformerFactory.test.ts +++ b/packages/content/src/test/extensions/presentation-rules/DescriptorTransformerFactory.test.ts @@ -15,9 +15,9 @@ import type { ContentDescriptor } from "../../../content/model/ContentDescriptor import type { CalculatedField, Field } from "../../../content/model/Field.js"; function propertyField(props: { - sourceClassName: EC.FullClassName; + sourceClassName: EC.FullClassNameDotNotation; propertyName: string; - valueClassNames: EC.FullClassName[]; + valueClassNames: EC.FullClassNameDotNotation[]; pathFromTarget?: PropertyField["pathFromTarget"]; label?: string; hidden?: boolean; @@ -56,9 +56,9 @@ function createDescriptor(fields: Field[]): ContentDescriptor { } function forkedId(props: { - sourceClassName: EC.FullClassName; + sourceClassName: EC.FullClassNameDotNotation; propertyName: string; - subset: EC.FullClassName[]; + subset: EC.FullClassNameDotNotation[]; pathFromTarget?: PropertyField["pathFromTarget"]; }): Field["id"] { return PropertyField.computeId({ diff --git a/packages/content/src/test/extensions/presentation-rules/FieldsProviderFactory.test.ts b/packages/content/src/test/extensions/presentation-rules/FieldsProviderFactory.test.ts index 2f82d4d89..afe96c0ef 100644 --- a/packages/content/src/test/extensions/presentation-rules/FieldsProviderFactory.test.ts +++ b/packages/content/src/test/extensions/presentation-rules/FieldsProviderFactory.test.ts @@ -97,7 +97,7 @@ function createIModelAccessFromClasses( return { getSchema, classDerivesFrom: async () => true }; } -function createTarget(primaryClass: EC.FullClassName = "TestSchema.TestElement"): ContentTarget { +function createTarget(primaryClass: EC.FullClassNameDotNotation = "TestSchema.TestElement"): ContentTarget { return { primaryClass }; } diff --git a/packages/content/src/test/model/Category.test.ts b/packages/content/src/test/model/Category.test.ts index 752609cf1..22c8d1bd6 100644 --- a/packages/content/src/test/model/Category.test.ts +++ b/packages/content/src/test/model/Category.test.ts @@ -18,9 +18,9 @@ describe("CategoryDefinition", () => { const result = CategoryDefinition.computeId({ path: [ { - sourceClassName: "BisCore:Element", - targetClassName: "BisCore:ElementAspect", - relationshipName: "BisCore:ElementOwnsUniqueAspect", + sourceClassName: "BisCore.Element", + targetClassName: "BisCore.ElementAspect", + relationshipName: "BisCore.ElementOwnsUniqueAspect", }, ], }); @@ -31,9 +31,9 @@ describe("CategoryDefinition", () => { const result = CategoryDefinition.computeId({ path: [ { - sourceClassName: "BisCore:Element", - targetClassName: "BisCore:Model", - relationshipName: "BisCore:ModelContainsElements", + sourceClassName: "BisCore.Element", + targetClassName: "BisCore.Model", + relationshipName: "BisCore.ModelContainsElements", relationshipReverse: true, }, ], @@ -45,15 +45,15 @@ describe("CategoryDefinition", () => { const result = CategoryDefinition.computeId({ path: [ { - sourceClassName: "BisCore:Element", - targetClassName: "BisCore:Model", - relationshipName: "BisCore:ModelContainsElements", + sourceClassName: "BisCore.Element", + targetClassName: "BisCore.Model", + relationshipName: "BisCore.ModelContainsElements", relationshipReverse: true, }, { - sourceClassName: "BisCore:Model", - targetClassName: "BisCore:Element", - relationshipName: "BisCore:ModelModelsElement", + sourceClassName: "BisCore.Model", + targetClassName: "BisCore.Element", + relationshipName: "BisCore.ModelModelsElement", }, ], }); @@ -64,7 +64,7 @@ describe("CategoryDefinition", () => { it("uses forward arrow when relationshipReverse is undefined", () => { const result = CategoryDefinition.computeId({ - path: [{ sourceClassName: "S:A", targetClassName: "S:B", relationshipName: "S:Rel" }], + path: [{ sourceClassName: "S.A", targetClassName: "S.B", relationshipName: "S.Rel" }], }); expect(result).to.equal("S.A-[S.Rel]->S.B"); }); @@ -73,15 +73,15 @@ describe("CategoryDefinition", () => { const result = CategoryDefinition.computeId({ path: [ { - sourceClassName: "BisCore:Element", - targetClassName: "BisCore:Model", - relationshipName: "BisCore:ModelContainsElements", + sourceClassName: "BisCore.Element", + targetClassName: "BisCore.Model", + relationshipName: "BisCore.ModelContainsElements", relationshipReverse: true, }, { - sourceClassName: "BisCore:Model", - targetClassName: "BisCore:Element", - relationshipName: "BisCore:ModelModelsElement", + sourceClassName: "BisCore.Model", + targetClassName: "BisCore.Element", + relationshipName: "BisCore.ModelModelsElement", }, ], omitTargetClass: true, @@ -96,7 +96,7 @@ describe("CategoryDefinition", () => { describe("create", () => { it("creates definition with computed id from path", () => { const result = CategoryDefinition.create({ - path: [{ sourceClassName: "S:A", targetClassName: "S:B", relationshipName: "S:Rel" }], + path: [{ sourceClassName: "S.A", targetClassName: "S.B", relationshipName: "S.Rel" }], label: "Related B", }); expect(result).to.deep.equal({ id: "S.A-[S.Rel]->S.B", label: "Related B" }); @@ -105,7 +105,7 @@ describe("CategoryDefinition", () => { it("includes parentId when provided", () => { const result = CategoryDefinition.create({ parentId: "parent-cat", - path: [{ sourceClassName: "S:A", targetClassName: "S:B", relationshipName: "S:Rel" }], + path: [{ sourceClassName: "S.A", targetClassName: "S.B", relationshipName: "S.Rel" }], label: "Related B", }); expect(result).to.deep.equal({ id: "S.A-[S.Rel]->S.B", label: "Related B", parentId: "parent-cat" }); @@ -113,7 +113,7 @@ describe("CategoryDefinition", () => { it("includes description when provided", () => { const result = CategoryDefinition.create({ - path: [{ sourceClassName: "S:A", targetClassName: "S:B", relationshipName: "S:Rel" }], + path: [{ sourceClassName: "S.A", targetClassName: "S.B", relationshipName: "S.Rel" }], label: "Related B", description: "Some description", }); @@ -122,7 +122,7 @@ describe("CategoryDefinition", () => { it("omits parentId and description when not provided", () => { const result = CategoryDefinition.create({ - path: [{ sourceClassName: "S:A", targetClassName: "S:B", relationshipName: "S:Rel" }], + path: [{ sourceClassName: "S.A", targetClassName: "S.B", relationshipName: "S.Rel" }], label: "Test", }); expect(result).not.to.have.property("parentId"); diff --git a/packages/content/src/test/model/Field.test.ts b/packages/content/src/test/model/Field.test.ts index 15fe90472..555253b29 100644 --- a/packages/content/src/test/model/Field.test.ts +++ b/packages/content/src/test/model/Field.test.ts @@ -9,13 +9,13 @@ import { PropertyField } from "../../content/model/Field.js"; describe("PropertyField", () => { describe("computeId", () => { it("computes id from class name and property name", () => { - const result = PropertyField.computeId({ propertyClassName: "BisCore:Element", propertyName: "CodeValue" }); + const result = PropertyField.computeId({ propertyClassName: "BisCore.Element", propertyName: "CodeValue" }); expect(result).to.equal("BisCore.Element.CodeValue"); }); it("normalizes colon notation to dot notation", () => { const result = PropertyField.computeId({ - propertyClassName: "BisCore:GeometricElement", + propertyClassName: "BisCore.GeometricElement", propertyName: "UserLabel", }); expect(result).to.equal("BisCore.GeometricElement.UserLabel"); @@ -41,13 +41,13 @@ describe("PropertyField", () => { it("appends single-step forward path in parentheses", () => { const result = PropertyField.computeId({ - propertyClassName: "BisCore:ElementAspect", + propertyClassName: "BisCore.ElementAspect", propertyName: "Value", pathFromTarget: [ { - sourceClassName: "BisCore:Element", - targetClassName: "BisCore:ElementAspect", - relationshipName: "BisCore:ElementOwnsUniqueAspect", + sourceClassName: "BisCore.Element", + targetClassName: "BisCore.ElementAspect", + relationshipName: "BisCore.ElementOwnsUniqueAspect", }, ], }); @@ -58,13 +58,13 @@ describe("PropertyField", () => { it("appends single-step reverse path with reverse arrows", () => { const result = PropertyField.computeId({ - propertyClassName: "BisCore:Model", + propertyClassName: "BisCore.Model", propertyName: "Name", pathFromTarget: [ { - sourceClassName: "BisCore:Element", - targetClassName: "BisCore:Model", - relationshipName: "BisCore:ModelContainsElements", + sourceClassName: "BisCore.Element", + targetClassName: "BisCore.Model", + relationshipName: "BisCore.ModelContainsElements", relationshipReverse: true, }, ], @@ -74,19 +74,19 @@ describe("PropertyField", () => { it("appends multi-step path", () => { const result = PropertyField.computeId({ - propertyClassName: "BisCore:Element", + propertyClassName: "BisCore.Element", propertyName: "UserLabel", pathFromTarget: [ { - sourceClassName: "BisCore:Element", - targetClassName: "BisCore:Model", - relationshipName: "BisCore:ModelContainsElements", + sourceClassName: "BisCore.Element", + targetClassName: "BisCore.Model", + relationshipName: "BisCore.ModelContainsElements", relationshipReverse: true, }, { - sourceClassName: "BisCore:Model", - targetClassName: "BisCore:Element", - relationshipName: "BisCore:ModelModelsElement", + sourceClassName: "BisCore.Model", + targetClassName: "BisCore.Element", + relationshipName: "BisCore.ModelModelsElement", }, ], }); @@ -97,7 +97,7 @@ describe("PropertyField", () => { it("appends fork key as a suffix", () => { const result = PropertyField.computeId({ - propertyClassName: "BisCore:Element", + propertyClassName: "BisCore.Element", propertyName: "CodeValue", forkKey: "Stuff.Door", }); @@ -106,13 +106,13 @@ describe("PropertyField", () => { it("appends fork key after the path", () => { const result = PropertyField.computeId({ - propertyClassName: "BisCore:ElementAspect", + propertyClassName: "BisCore.ElementAspect", propertyName: "Value", pathFromTarget: [ { - sourceClassName: "BisCore:Element", - targetClassName: "BisCore:ElementAspect", - relationshipName: "BisCore:ElementOwnsUniqueAspect", + sourceClassName: "BisCore.Element", + targetClassName: "BisCore.ElementAspect", + relationshipName: "BisCore.ElementOwnsUniqueAspect", }, ], forkKey: "BisCore.ElementAspectX", diff --git a/packages/content/src/test/model/PropertyValueDescriptor.test.ts b/packages/content/src/test/model/PropertyValueDescriptor.test.ts index 6e00c2101..75aabfcce 100644 --- a/packages/content/src/test/model/PropertyValueDescriptor.test.ts +++ b/packages/content/src/test/model/PropertyValueDescriptor.test.ts @@ -56,10 +56,10 @@ function createStructProperty(props: { name: string; members: EC.Property[]; arr function createNavigationProperty(props: { name: string; direction: "Forward" | "Backward"; - sourceClassName?: EC.FullClassName; - targetClassName?: EC.FullClassName; + sourceClassName?: EC.FullClassNameDotNotation; + targetClassName?: EC.FullClassNameDotNotation; }): EC.Property { - const constraint = (className: EC.FullClassName | undefined): EC.RelationshipConstraint => + const constraint = (className: EC.FullClassNameDotNotation | undefined): EC.RelationshipConstraint => ({ abstractConstraint: Promise.resolve( className ? ({ fullName: className } as unknown as EC.EntityClass) : undefined, diff --git a/packages/content/src/test/model/Utils.test.ts b/packages/content/src/test/model/Utils.test.ts index fa0f95ff0..42ae2e078 100644 --- a/packages/content/src/test/model/Utils.test.ts +++ b/packages/content/src/test/model/Utils.test.ts @@ -10,24 +10,27 @@ import type { EC } from "@itwin/presentation-shared"; describe("toSortedUniqueClassNames", () => { it("normalizes, de-duplicates, and sorts class names", () => { - const result = toSortedUniqueClassNames(["Stuff:Window", "Stuff.Door", "Stuff:Window", "Stuff:Door"]); + const result = toSortedUniqueClassNames(["Stuff.Window", "Stuff.Door", "Stuff.Window", "Stuff.Door"]); expect(result).to.deep.equal(["Stuff.Door", "Stuff.Window"]); }); }); describe("computeFieldForkKey", () => { it("uses a readable joined key for short subsets", () => { - expect(computeFieldForkKey(["Stuff:Window", "Stuff:Door"])).to.equal("Stuff.Door;Stuff.Window"); + expect(computeFieldForkKey(["Stuff.Window", "Stuff.Door"])).to.equal("Stuff.Door;Stuff.Window"); }); it("is deterministic and order-independent", () => { - expect(computeFieldForkKey(["Stuff:Window", "Stuff:Door"])).to.equal( - computeFieldForkKey(["Stuff:Door", "Stuff:Window"]), + expect(computeFieldForkKey(["Stuff.Window", "Stuff.Door"])).to.equal( + computeFieldForkKey(["Stuff.Door", "Stuff.Window"]), ); }); it("falls back to a bounded hash for long subsets", () => { - const longSubset = Array.from({ length: 20 }, (_, i) => `Schema.VeryLongClassNameNumber${i}`) as EC.FullClassName[]; + const longSubset = Array.from( + { length: 20 }, + (_, i): EC.FullClassNameDotNotation => `Schema.VeryLongClassNameNumber${i}`, + ); const key = computeFieldForkKey(longSubset); expect(key).to.not.contain(";"); expect(key.length).to.be.lessThan(20); diff --git a/packages/core-interop/src/core-interop/MetadataInternal.ts b/packages/core-interop/src/core-interop/MetadataInternal.ts index 0dbd7c604..a5eab8fad 100644 --- a/packages/core-interop/src/core-interop/MetadataInternal.ts +++ b/packages/core-interop/src/core-interop/MetadataInternal.ts @@ -433,7 +433,7 @@ async function createFromOptionalLazyLoaded = new Map(); +const EMPTY_MAP: ReadonlyMap = new Map(); async function createCustomAttributesSet( coreCustomAttributes: CoreClass["customAttributes"], ): Promise { @@ -447,10 +447,9 @@ async function createCustomAttributesSet( yield [normalizedClassName, { ...ca, className: normalizedClassName }]; } }, - get(className: EC.FullClassName) { - const normalizedClassName = normalizeFullClassName(className); - const coreCustomAttribute = coreCustomAttributes.get(normalizedClassName); - return coreCustomAttribute ? { ...coreCustomAttribute, className: normalizedClassName } : undefined; + get(className: EC.FullClassNameDotNotation) { + const coreCustomAttribute = coreCustomAttributes.get(className); + return coreCustomAttribute ? { ...coreCustomAttribute, className } : undefined; }, }; } diff --git a/packages/hierarchies-react/src/test/unit/TestUtils.ts b/packages/hierarchies-react/src/test/unit/TestUtils.ts index d93651033..8aa1ce871 100644 --- a/packages/hierarchies-react/src/test/unit/TestUtils.ts +++ b/packages/hierarchies-react/src/test/unit/TestUtils.ts @@ -132,7 +132,7 @@ export function createTestGroupingNode({ }: Partial & { id: string }): GroupingHierarchyNode { return { ...props, - key: props.key ?? { type: "class-grouping", className: "Schema:Class" }, + key: props.key ?? { type: "class-grouping", className: "Schema.Class" }, label: props.label ?? id, children: props.children ?? false, parentKeys: props.parentKeys ?? [], diff --git a/packages/hierarchies-react/src/test/unit/UseTree.test.tsx b/packages/hierarchies-react/src/test/unit/UseTree.test.tsx index 11518e010..65f6cf4bc 100644 --- a/packages/hierarchies-react/src/test/unit/UseTree.test.tsx +++ b/packages/hierarchies-react/src/test/unit/UseTree.test.tsx @@ -149,7 +149,7 @@ describe("useTree", () => { return createAsyncIterator(props.parentNode === undefined ? [createTestHierarchyNode({ id: "root-1" })] : []); }); - const searchTree = await HierarchySearchTree.createFromPathsList([[{ id: "0x1", className: "Schema:Class" }]]); + const searchTree = await HierarchySearchTree.createFromPathsList([[{ id: "0x1", className: "Schema.Class" }]]); const promise = new ResolvablePromise(); const getSearchPaths = async () => promise; @@ -272,8 +272,8 @@ describe("useTree", () => { }); it("loads hierarchy using latest search paths", async () => { - const searchTree1 = await HierarchySearchTree.createFromPathsList([[{ id: "0x1", className: "Schema:Class" }]]); - const searchTree2 = await HierarchySearchTree.createFromPathsList([[{ id: "0x2", className: "Schema:Class" }]]); + const searchTree1 = await HierarchySearchTree.createFromPathsList([[{ id: "0x1", className: "Schema.Class" }]]); + const searchTree2 = await HierarchySearchTree.createFromPathsList([[{ id: "0x2", className: "Schema.Class" }]]); const rootNode1 = createTestHierarchyNode({ id: "root-1" }); const rootNode2 = createTestHierarchyNode({ id: "root-2" }); @@ -621,7 +621,7 @@ describe("useTree", () => { ]; const groupingNode = createTestGroupingNode({ id: "grouping-node", - key: { type: "class-grouping", className: "Schema:Class" }, + key: { type: "class-grouping", className: "Schema.Class" }, nonGroupingAncestor: rootNodes[0], autoExpand: true, }); @@ -751,8 +751,8 @@ describe("useTree", () => { ); hierarchyProvider.getNodeInstanceKeys.mockImplementation(() => createAsyncIterator([ - { id: "0x1", className: "Schema:Class" }, - { id: "0x2", className: "Schema:Class" }, + { id: "0x1", className: "Schema.Class" }, + { id: "0x2", className: "Schema.Class" }, ]), ); const { result } = renderHook(useTree, { initialProps }); @@ -1053,7 +1053,7 @@ describe("useUnifiedSelectionTree", () => { function createNodeKey(id: string) { const imodelKey = "test-imodel-key"; - const instanceKey = { id, className: "Schema:Class" as const, imodelKey }; + const instanceKey = { id, className: "Schema.Class" as const, imodelKey }; const instancesNodeKey: InstancesNodeKey = { type: "instances", instanceKeys: [instanceKey] }; return { instanceKey, instancesNodeKey, imodelKey }; } diff --git a/packages/hierarchies-react/src/test/unit/internal/UseUnifiedSelection.test.tsx b/packages/hierarchies-react/src/test/unit/internal/UseUnifiedSelection.test.tsx index 9b7ef2052..fabb487ee 100644 --- a/packages/hierarchies-react/src/test/unit/internal/UseUnifiedSelection.test.tsx +++ b/packages/hierarchies-react/src/test/unit/internal/UseUnifiedSelection.test.tsx @@ -38,18 +38,18 @@ describe("useUnifiedSelection", () => { describe("isNodeSelected", () => { it("returns true if instance node is selected", () => { - const selectedInstanceKey = { id: "0x1", className: "Schema:Name" as const }; + const selectedInstanceKey = { id: "0x1", className: "Schema.Name" as const }; const selectedInstancesNodesKey: InstancesNodeKey = { type: "instances", instanceKeys: [{ ...selectedInstanceKey, imodelKey }], }; const noIModelInstancesNodesKey: InstancesNodeKey = { type: "instances", - instanceKeys: [{ id: "0x2", className: "Schema:Name" }], + instanceKeys: [{ id: "0x2", className: "Schema.Name" }], }; const differentIModelInstancesNodesKey: InstancesNodeKey = { type: "instances", - instanceKeys: [{ id: "0x3", className: "Schema:Name", imodelKey: "unknown-imodel" }], + instanceKeys: [{ id: "0x3", className: "Schema.Name", imodelKey: "unknown-imodel" }], }; const mergedInstancesNodesKey: InstancesNodeKey = { type: "instances", @@ -93,9 +93,9 @@ describe("useUnifiedSelection", () => { it("returns true if grouping node is selected", () => { const instancesNodesKey: InstancesNodeKey = { type: "instances", - instanceKeys: [{ id: "0x1", className: "Schema:Name" as const }], + instanceKeys: [{ id: "0x1", className: "Schema.Name" as const }], }; - const selectedInstanceKey = { id: "0x2", className: "Schema:Name" as const }; + const selectedInstanceKey = { id: "0x2", className: "Schema.Name" as const }; const nodes = [ createTreeModelNode({ id: "node-1", @@ -133,7 +133,7 @@ describe("useUnifiedSelection", () => { it("returns true if generic node is selected with default `createSelectableForGenericNode` handler", () => { const instancesNodesKey: InstancesNodeKey = { type: "instances", - instanceKeys: [{ id: "0x1", className: "Schema:Name" }], + instanceKeys: [{ id: "0x1", className: "Schema.Name" }], }; const genericNodeKey: GenericNodeKey = { type: "generic", id: "generic-node" }; const instancesNode = createTestHierarchyNode({ id: "instances-node", key: instancesNodesKey }); @@ -162,7 +162,7 @@ describe("useUnifiedSelection", () => { it("returns true if generic node is selected with custom `createSelectableForGenericNode` handler", () => { const instancesNodesKey: InstancesNodeKey = { type: "instances", - instanceKeys: [{ id: "0x1", className: "Schema:Name" }], + instanceKeys: [{ id: "0x1", className: "Schema.Name" }], }; const genericNodeKey: GenericNodeKey = { type: "generic", id: "generic-node" }; const instancesNode = createTestHierarchyNode({ id: "instances-node", key: instancesNodesKey }); @@ -211,7 +211,7 @@ describe("useUnifiedSelection", () => { }); it("adds instance node to selection", () => { - const instanceKey = { id: "0x1", className: "Schema:Name" as const }; + const instanceKey = { id: "0x1", className: "Schema.Name" as const }; const instancesNodesKey: InstancesNodeKey = { type: "instances", instanceKeys: [ @@ -259,7 +259,7 @@ describe("useUnifiedSelection", () => { }); it("adds grouping node to selection", async () => { - const instanceKey = { id: "0x1", className: "Schema:Name" as const }; + const instanceKey = { id: "0x1", className: "Schema.Name" as const }; const groupingNode = createTestGroupingNode({ id: "grouping-node", groupedInstanceKeys: [ @@ -358,7 +358,7 @@ describe("useUnifiedSelection", () => { }); it("removes instance node from selection", () => { - const instanceKey = { id: "0x1", className: "Schema:Name" as const }; + const instanceKey = { id: "0x1", className: "Schema.Name" as const }; const instancesNodesKey: InstancesNodeKey = { type: "instances", instanceKeys: [ @@ -410,7 +410,7 @@ describe("useUnifiedSelection", () => { }); it("removes grouping node from selection", async () => { - const instanceKey = { id: "0x1", className: "Schema:Name" as const }; + const instanceKey = { id: "0x1", className: "Schema.Name" as const }; const groupingNode = createTestGroupingNode({ id: "grouping-node", groupedInstanceKeys: [ @@ -529,7 +529,7 @@ describe("useUnifiedSelection", () => { }); it("replaces selection with node", () => { - const instanceKey = { id: "0x1", className: "Schema:Name" as const }; + const instanceKey = { id: "0x1", className: "Schema.Name" as const }; const instancesNodesKey: InstancesNodeKey = { type: "instances", instanceKeys: [{ ...instanceKey, imodelKey }] }; const nodes = [ createTreeModelNode({ @@ -567,7 +567,7 @@ describe("useUnifiedSelection", () => { storage.addToSelection({ imodelKey, source: "some-source", - selectables: [{ id: "0x1", className: "Schema:Class" }], + selectables: [{ id: "0x1", className: "Schema.Class" }], }); }); @@ -584,7 +584,7 @@ describe("useUnifiedSelection", () => { storage.addToSelection({ imodelKey, source: "some-source", - selectables: [{ id: "0x1", className: "Schema:Class" }], + selectables: [{ id: "0x1", className: "Schema.Class" }], level: 1, }); }); diff --git a/packages/hierarchies-react/src/test/unit/internal/Utils.test.ts b/packages/hierarchies-react/src/test/unit/internal/Utils.test.ts index ce78dbda5..0294649d3 100644 --- a/packages/hierarchies-react/src/test/unit/internal/Utils.test.ts +++ b/packages/hierarchies-react/src/test/unit/internal/Utils.test.ts @@ -19,28 +19,28 @@ describe("createNodeId", () => { key: { type: "instances", instanceKeys: [ - { id: "0x1", className: "Schema:Class" }, - { id: "0x2", className: "Schema:Class" }, + { id: "0x1", className: "Schema.Class" }, + { id: "0x2", className: "Schema.Class" }, ], }, }); - expect(createNodeId(node)).toBe("instances,0x1,Schema:Class,0x2,Schema:Class"); + expect(createNodeId(node)).toBe("instances,0x1,Schema.Class,0x2,Schema.Class"); }); it("creates id for `class-grouping` node", () => { const node = createTestGroupingNode({ id: "custom", - key: { type: "class-grouping", className: "Schema:Class" }, - groupedInstanceKeys: [{ id: "0x1", className: "Schema:Class" }], + key: { type: "class-grouping", className: "Schema.Class" }, + groupedInstanceKeys: [{ id: "0x1", className: "Schema.Class" }], }); - expect(createNodeId(node)).toBe("class-grouping,Schema:Class"); + expect(createNodeId(node)).toBe("class-grouping,Schema.Class"); }); it("creates id for `label-grouping` node", () => { const node = createTestGroupingNode({ id: "custom", key: { type: "label-grouping", label: "TestLabel" }, - groupedInstanceKeys: [{ id: "0x1", className: "Schema:Class" }], + groupedInstanceKeys: [{ id: "0x1", className: "Schema.Class" }], }); expect(createNodeId(node)).toBe("label-grouping,TestLabel"); }); @@ -51,25 +51,25 @@ describe("createNodeId", () => { key: { type: "property-grouping:value", formattedPropertyValue: "test-value", - propertyClassName: "TestSchema:TestClass", + propertyClassName: "TestSchema.TestClass", propertyName: "TestProp", }, - groupedInstanceKeys: [{ id: "0x1", className: "Schema:Class" }], + groupedInstanceKeys: [{ id: "0x1", className: "Schema.Class" }], }); - expect(createNodeId(node)).toBe("property-grouping:value,test-value,TestSchema:TestClass,TestProp"); + expect(createNodeId(node)).toBe("property-grouping:value,test-value,TestSchema.TestClass,TestProp"); }); it("creates id for `instances` node child node", () => { const node = createTestHierarchyNode({ id: "custom", - key: { type: "instances", instanceKeys: [{ id: "0x3", className: "Schema:Class" }] }, + key: { type: "instances", instanceKeys: [{ id: "0x3", className: "Schema.Class" }] }, parentKeys: [ - { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema:Class" }] }, - { type: "class-grouping", className: "Schema:OtherClass" }, + { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema.Class" }] }, + { type: "class-grouping", className: "Schema.OtherClass" }, ], }); expect(createNodeId(node)).toBe( - "instances,0x1,Schema:Class;class-grouping,Schema:OtherClass;instances,0x3,Schema:Class", + "instances,0x1,Schema.Class;class-grouping,Schema.OtherClass;instances,0x3,Schema.Class", ); }); }); @@ -84,31 +84,31 @@ describe("sameNodes", () => { it("compares same `instance` nodes", () => { const lhs = createTestHierarchyNode({ id: "lhs", - key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema:Class" }] }, + key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema.Class" }] }, }); const rhs = createTestHierarchyNode({ id: "rhs", - key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema:Class" }] }, + key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema.Class" }] }, }); expect(sameNodes(lhs, rhs)).toBe(true); }); it("compares same `class-grouping` nodes", () => { - const lhs = createTestGroupingNode({ id: "lhs", key: { type: "class-grouping", className: "Schema:Class" } }); - const rhs = createTestGroupingNode({ id: "rhs", key: { type: "class-grouping", className: "Schema:Class" } }); + const lhs = createTestGroupingNode({ id: "lhs", key: { type: "class-grouping", className: "Schema.Class" } }); + const rhs = createTestGroupingNode({ id: "rhs", key: { type: "class-grouping", className: "Schema.Class" } }); expect(sameNodes(lhs, rhs)).toBe(true); }); it("compares same child nodes", () => { const lhs = createTestHierarchyNode({ id: "lhs", - key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema:Class" }] }, - parentKeys: [{ type: "instances", instanceKeys: [{ id: "0x2", className: "Schema:Class" }] }], + key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema.Class" }] }, + parentKeys: [{ type: "instances", instanceKeys: [{ id: "0x2", className: "Schema.Class" }] }], }); const rhs = createTestHierarchyNode({ id: "rhs", - key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema:Class" }] }, - parentKeys: [{ type: "instances", instanceKeys: [{ id: "0x2", className: "Schema:Class" }] }], + key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema.Class" }] }, + parentKeys: [{ type: "instances", instanceKeys: [{ id: "0x2", className: "Schema.Class" }] }], }); expect(sameNodes(lhs, rhs)).toBe(true); }); @@ -116,11 +116,11 @@ describe("sameNodes", () => { it("compares different `instance` nodes", () => { const lhs = createTestHierarchyNode({ id: "lhs", - key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema:Class" }] }, + key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema.Class" }] }, }); const rhs = createTestHierarchyNode({ id: "rhs", - key: { type: "instances", instanceKeys: [{ id: "0x2", className: "Schema:Class" }] }, + key: { type: "instances", instanceKeys: [{ id: "0x2", className: "Schema.Class" }] }, }); expect(sameNodes(lhs, rhs)).toBe(false); }); @@ -128,13 +128,13 @@ describe("sameNodes", () => { it("compares child nodes from different levels", () => { const lhs = createTestHierarchyNode({ id: "lhs", - key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema:Class" }] }, - parentKeys: [{ type: "instances", instanceKeys: [{ id: "0x2", className: "Schema:Class" }] }], + key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema.Class" }] }, + parentKeys: [{ type: "instances", instanceKeys: [{ id: "0x2", className: "Schema.Class" }] }], }); const rhs = createTestHierarchyNode({ id: "rhs", - key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema:Class" }] }, - parentKeys: [{ type: "instances", instanceKeys: [{ id: "0x3", className: "Schema:Class" }] }], + key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema.Class" }] }, + parentKeys: [{ type: "instances", instanceKeys: [{ id: "0x3", className: "Schema.Class" }] }], }); expect(sameNodes(lhs, rhs)).toBe(false); }); @@ -142,15 +142,15 @@ describe("sameNodes", () => { it("compares child nodes from different depths", () => { const lhs = createTestHierarchyNode({ id: "lhs", - key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema:Class" }] }, - parentKeys: [{ type: "instances", instanceKeys: [{ id: "0x2", className: "Schema:Class" }] }], + key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema.Class" }] }, + parentKeys: [{ type: "instances", instanceKeys: [{ id: "0x2", className: "Schema.Class" }] }], }); const rhs = createTestHierarchyNode({ id: "rhs", - key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema:Class" }] }, + key: { type: "instances", instanceKeys: [{ id: "0x1", className: "Schema.Class" }] }, parentKeys: [ - { type: "instances", instanceKeys: [{ id: "0x2", className: "Schema:Class" }] }, - { type: "instances", instanceKeys: [{ id: "0x3", className: "Schema:Class" }] }, + { type: "instances", instanceKeys: [{ id: "0x2", className: "Schema.Class" }] }, + { type: "instances", instanceKeys: [{ id: "0x3", className: "Schema.Class" }] }, ], }); expect(sameNodes(lhs, rhs)).toBe(false); diff --git a/packages/hierarchies/api/presentation-hierarchies.api.md b/packages/hierarchies/api/presentation-hierarchies.api.md index 9f5ce7015..109b88cd5 100644 --- a/packages/hierarchies/api/presentation-hierarchies.api.md +++ b/packages/hierarchies/api/presentation-hierarchies.api.md @@ -37,7 +37,7 @@ interface BaseHierarchyNode { // @public export interface ClassGroupingNodeKey { - className: EC.FullClassName; + className: EC.FullClassNameDotNotation; type: "class-grouping"; } @@ -99,7 +99,7 @@ export type DefineInstanceNodeChildHierarchyLevelProps = Omit & { key: InstancesNodeKey; }; - parentNodeClassName: EC.FullClassName; + parentNodeClassName: EC.FullClassNameDotNotation; parentNodeInstanceIds: Id64String[]; }; @@ -109,7 +109,7 @@ export type DefineRootHierarchyLevelProps = Omit Promise; onlyIfNotHandled?: boolean; - parentInstancesNodePredicate: EC.FullClassName | ((parentNodeKey: InstancesNodeKey) => Promise); + parentInstancesNodePredicate: EC.FullClassNameDotNotation | ((parentNodeKey: InstancesNodeKey) => Promise); } // @public @@ -676,7 +676,7 @@ interface NodeSelectClauseProps { interface NodesQueryClauseFactory { createFilterClauses(props: { contentClass: { - fullName: EC.FullClassName; + fullName: EC.FullClassNameDotNotation; alias: string; }; filter?: GenericInstanceFilter; @@ -745,7 +745,7 @@ export type PropertyGroupingNodeKey = PropertyValueRangeGroupingNodeKey | Proper // @public export interface PropertyOtherValuesGroupingNodeKey { properties: Array<{ - className: EC.FullClassName; + className: EC.FullClassNameDotNotation; propertyName: string; }>; type: "property-grouping:other"; @@ -754,7 +754,7 @@ export interface PropertyOtherValuesGroupingNodeKey { // @public export interface PropertyValueGroupingNodeKey { formattedPropertyValue: string; - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; propertyName: string; type: "property-grouping:value"; } @@ -762,7 +762,7 @@ export interface PropertyValueGroupingNodeKey { // @public export interface PropertyValueRangeGroupingNodeKey { fromValue: number; - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; propertyName: string; toValue: number; type: "property-grouping:range"; diff --git a/packages/hierarchies/learning/imodel/MergedIModelHierarchies.md b/packages/hierarchies/learning/imodel/MergedIModelHierarchies.md index f4a1a4ea8..fc165a6e5 100644 --- a/packages/hierarchies/learning/imodel/MergedIModelHierarchies.md +++ b/packages/hierarchies/learning/imodel/MergedIModelHierarchies.md @@ -39,7 +39,7 @@ async function createInstanceNodesQueryDefinition({ fullClassName, whereClauseFactory, }: Pick & { - fullClassName: EC.FullClassName; + fullClassName: EC.FullClassNameDotNotation; whereClauseFactory?: (props: { alias: string }) => Promise; }) { const whereClause = whereClauseFactory ? await whereClauseFactory({ alias: "this" }) : undefined; diff --git a/packages/hierarchies/src/hierarchies/HierarchyNodeKey.ts b/packages/hierarchies/src/hierarchies/HierarchyNodeKey.ts index 359cde638..4a9004cdc 100644 --- a/packages/hierarchies/src/hierarchies/HierarchyNodeKey.ts +++ b/packages/hierarchies/src/hierarchies/HierarchyNodeKey.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { assert, compareStrings, compareStringsOrUndefined } from "@itwin/core-bentley"; -import { InstanceKey, normalizeFullClassName } from "@itwin/presentation-shared"; +import { InstanceKey } from "@itwin/presentation-shared"; import type { EC } from "@itwin/presentation-shared"; @@ -61,7 +61,7 @@ export interface ClassGroupingNodeKey { type: "class-grouping"; /** Full name of the ECClass that this grouping node is grouping by. */ - className: EC.FullClassName; + className: EC.FullClassNameDotNotation; } /** @@ -91,7 +91,7 @@ export interface PropertyOtherValuesGroupingNodeKey { /** Type of the node */ type: "property-grouping:other"; /** Identifiers of properties whose values are grouped under this node. */ - properties: Array<{ className: EC.FullClassName; propertyName: string }>; + properties: Array<{ className: EC.FullClassNameDotNotation; propertyName: string }>; } /** @@ -106,7 +106,7 @@ export interface PropertyValueGroupingNodeKey { propertyName: string; /** Full name of the ECClass containing the property. */ - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; /** Formatted property value that this node is grouping by. */ formattedPropertyValue: string; @@ -124,7 +124,7 @@ export interface PropertyValueRangeGroupingNodeKey { propertyName: string; /** Full name of the ECClass containing the property. */ - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; /** Defines the start of the values' range that this node is grouping by. */ fromValue: number; @@ -255,10 +255,7 @@ export namespace HierarchyNodeKey { } case "class-grouping": { assert(rhs.type === "class-grouping"); - return compareStrings( - normalizeFullClassName(lhs.className).toLocaleLowerCase(), - normalizeFullClassName(rhs.className).toLocaleLowerCase(), - ); + return compareStrings(lhs.className.toLocaleLowerCase(), rhs.className.toLocaleLowerCase()); } case "label-grouping": { assert(rhs.type === "label-grouping"); @@ -275,8 +272,8 @@ export namespace HierarchyNodeKey { } for (let i = 0; i < lhs.properties.length; ++i) { const classCompareResult = compareStrings( - normalizeFullClassName(lhs.properties[i].className).toLocaleLowerCase(), - normalizeFullClassName(rhs.properties[i].className).toLocaleLowerCase(), + lhs.properties[i].className.toLocaleLowerCase(), + rhs.properties[i].className.toLocaleLowerCase(), ); if (classCompareResult !== 0) { return classCompareResult; @@ -294,8 +291,8 @@ export namespace HierarchyNodeKey { case "property-grouping:value": { assert(rhs.type === "property-grouping:value"); const propertyClassNameCompareResult = compareStrings( - normalizeFullClassName(lhs.propertyClassName).toLocaleLowerCase(), - normalizeFullClassName(rhs.propertyClassName).toLocaleLowerCase(), + lhs.propertyClassName.toLocaleLowerCase(), + rhs.propertyClassName.toLocaleLowerCase(), ); if (propertyClassNameCompareResult !== 0) { return propertyClassNameCompareResult; @@ -312,8 +309,8 @@ export namespace HierarchyNodeKey { case "property-grouping:range": { assert(rhs.type === "property-grouping:range"); const propertyClassNameCompareResult = compareStrings( - normalizeFullClassName(lhs.propertyClassName).toLocaleLowerCase(), - normalizeFullClassName(rhs.propertyClassName).toLocaleLowerCase(), + lhs.propertyClassName.toLocaleLowerCase(), + rhs.propertyClassName.toLocaleLowerCase(), ); if (propertyClassNameCompareResult !== 0) { return propertyClassNameCompareResult; diff --git a/packages/hierarchies/src/hierarchies/imodel/IModelHierarchyDefinition.ts b/packages/hierarchies/src/hierarchies/imodel/IModelHierarchyDefinition.ts index fafda15b8..1ef606913 100644 --- a/packages/hierarchies/src/hierarchies/imodel/IModelHierarchyDefinition.ts +++ b/packages/hierarchies/src/hierarchies/imodel/IModelHierarchyDefinition.ts @@ -35,7 +35,7 @@ export interface InstanceNodesQueryDefinition { * points to a base class of multiple different classes of instances returned by the query, however * the more specific this class is, the more efficient hierarchy building process is. */ - fullClassName: EC.FullClassName; + fullClassName: EC.FullClassNameDotNotation; /** * An ECSQL query that selects nodes from an iModel. `SELECT` clause of the query is expected * to be built using `NodeSelectQueryFactory.createSelectClause`. diff --git a/packages/hierarchies/src/hierarchies/imodel/IModelHierarchyNode.ts b/packages/hierarchies/src/hierarchies/imodel/IModelHierarchyNode.ts index c65b2f21e..cc0cb3f4c 100644 --- a/packages/hierarchies/src/hierarchies/imodel/IModelHierarchyNode.ts +++ b/packages/hierarchies/src/hierarchies/imodel/IModelHierarchyNode.ts @@ -100,7 +100,7 @@ interface HierarchyNodeBaseClassGroupingParams extends HierarchyNodeGroupingPara * * Full class name format: `SchemaName.ClassName`. */ - fullClassNames: EC.FullClassName[]; + fullClassNames: EC.FullClassNameDotNotation[]; } /** @@ -114,7 +114,7 @@ export interface HierarchyNodePropertiesGroupingParams extends HierarchyNodeGrou * * Full class name format: `SchemaName.ClassName`. */ - propertiesClassName: EC.FullClassName; + propertiesClassName: EC.FullClassNameDotNotation; /** * Property grouping option that determines whether to group nodes whose grouping value is not set or is set to an empty string. * diff --git a/packages/hierarchies/src/hierarchies/imodel/NodeSelectQueryFactory.ts b/packages/hierarchies/src/hierarchies/imodel/NodeSelectQueryFactory.ts index 1eabcc525..4b5776430 100644 --- a/packages/hierarchies/src/hierarchies/imodel/NodeSelectQueryFactory.ts +++ b/packages/hierarchies/src/hierarchies/imodel/NodeSelectQueryFactory.ts @@ -6,7 +6,6 @@ import { assert } from "@itwin/core-bentley"; import { GenericInstanceFilter, GenericInstanceFilterRuleValue } from "@itwin/core-common"; import { - compareFullClassNames, ECSql, getClass, normalizeFullClassName, @@ -38,7 +37,7 @@ import type { HierarchyNodeAutoExpandProp } from "./IModelHierarchyNode.js"; * @public */ export enum NodeSelectClauseColumnNames { - /** Full class name of the instance the node represents. Type: `string` in format `{schema name}:{class name}`. */ + /** Full class name of the instance the node represents. Type: `string` in format `{schema name}.{class name}`. */ FullClassName = "FullClassName", /** ECInstance ID of the instance the node represents. Type: `Id64String`. */ ECInstanceId = "ECInstanceId", @@ -160,7 +159,7 @@ interface ECSqlSelectClausePropertiesGroupingParams extends ECSqlSelectClauseGro * * Full class name format: `SchemaName.ClassName`. */ - propertiesClassName: EC.FullClassName; + propertiesClassName: EC.FullClassNameDotNotation; /** * Property grouping option that determines whether to group nodes whose grouping value is not set or is set to an empty string. * @@ -229,7 +228,7 @@ interface ECSqlSelectClausePropertyValueRange { * @public */ interface ECSqlSelectClauseBaseClassGroupingParams extends ECSqlSelectClauseGroupingParamsBase { - fullClassNames: EC.FullClassName[] | ECSqlValueSelector[]; + fullClassNames: EC.FullClassNameDotNotation[] | ECSqlValueSelector[]; } /** @@ -253,7 +252,7 @@ export interface NodesQueryClauseFactory { * don't exist in the iModel, a special result is returned to make sure the resulting query is valid and doesn't return anything. */ createFilterClauses(props: { - contentClass: { fullName: EC.FullClassName; alias: string }; + contentClass: { fullName: EC.FullClassNameDotNotation; alias: string }; filter?: GenericInstanceFilter; }): Promise<{ from: string; where: string; joins: string }>; } @@ -285,7 +284,7 @@ class NodeSelectQueryFactory { public async createSelectClause(props: NodeSelectClauseProps) { // note: the columns order must match the order in `NodeSelectClauseColumnNames` return ` - ec_ClassName(${props.ecClassId.selector}) AS ${NodeSelectClauseColumnNames.FullClassName}, + ec_ClassName(${props.ecClassId.selector}, 's.c') AS ${NodeSelectClauseColumnNames.FullClassName}, ${props.ecInstanceId.selector} AS ${NodeSelectClauseColumnNames.ECInstanceId}, ${createECSqlValueSelector( typeof props.nodeLabel === "object" && "of" in props.nodeLabel @@ -321,7 +320,7 @@ class NodeSelectQueryFactory { * don't exist in the iModel, a special result is returned to make sure the resulting query is valid and doesn't return anything. */ public async createFilterClauses(props: { - contentClass: { fullName: EC.FullClassName; alias: string }; + contentClass: { fullName: EC.FullClassNameDotNotation; alias: string }; filter?: GenericInstanceFilter; }): Promise<{ from: string; where: string; joins: string }> { const { contentClass, filter } = props; @@ -329,7 +328,7 @@ class NodeSelectQueryFactory { ? await createInstanceFilterClauses({ imodelAccess: this._imodelAccess, contentClass, filter }) : { from: contentClass.fullName, joins: [], where: [] }; - const fromClass = await getClass(this._imodelAccess, normalizeFullClassName(from)); + const fromClass = await getClass(this._imodelAccess, from); const hiddenClasses = await getHiddenClassesTree(fromClass); const hiddenClassesWhereClause = createWhereClauseForHiddenClasses(hiddenClasses, contentClass.alias); hiddenClassesWhereClause.hideClause && where.push(hiddenClassesWhereClause.hideClause); @@ -353,9 +352,9 @@ class NodeSelectQueryFactory { */ async function createInstanceFilterClauses(props: { imodelAccess: ECSchemaProvider & ECClassHierarchyInspector; - contentClass: { fullName: EC.FullClassName; alias: string }; + contentClass: { fullName: EC.FullClassNameDotNotation; alias: string }; filter: GenericInstanceFilter; -}): Promise<{ from: EC.FullClassName; where: string[]; joins: string[] }> { +}): Promise<{ from: EC.FullClassNameDotNotation; where: string[]; joins: string[] }> { const { imodelAccess, contentClass, filter } = props; // In some cases the given filter means we want the query to return nothing - e.g. when filtering by classes that don't intersect @@ -364,7 +363,10 @@ async function createInstanceFilterClauses(props: { const from = await specializeContentClass({ classHierarchyInspector: { - async classDerivesFrom(derivedClassName: EC.FullClassName, baseClassName: EC.FullClassName): Promise { + async classDerivesFrom( + derivedClassName: EC.FullClassNameDotNotation, + baseClassName: EC.FullClassNameDotNotation, + ): Promise { try { return await imodelAccess.classDerivesFrom(derivedClassName, baseClassName); } catch (e) { @@ -387,7 +389,7 @@ async function createInstanceFilterClauses(props: { return DISABLE_QUERY; } - let filteredClassNamesInThisIModel: EC.FullClassName[] | undefined; + let filteredClassNamesInThisIModel: EC.FullClassNameDotNotation[] | undefined; if (filter.filteredClassNames && filter.filteredClassNames.length > 0) { filteredClassNamesInThisIModel = ( await Promise.all( @@ -431,7 +433,7 @@ async function createInstanceFilterClauses(props: { throw e; } - const classAliasMap = new Map([[contentClass.alias, from]]); + const classAliasMap = new Map([[contentClass.alias, from]]); filter.relatedInstances.forEach( ({ path, alias }) => path.length > 0 && classAliasMap.set(alias, normalizeFullClassName(path[path.length - 1].targetClassName)), @@ -829,7 +831,7 @@ function assignRelationshipPathAliases( sourceAlias: string, targetAlias: string, ): JoinRelationshipPath { - function createAlias(fullClassName: EC.FullClassName, index: number) { + function createAlias(fullClassName: EC.FullClassNameDotNotation, index: number) { return `rel_${pathIndex}_${fullClassName.replaceAll(/[\.:]/g, "_")}_${index}`; } const result: JoinRelationshipPath = []; @@ -853,10 +855,12 @@ function assignRelationshipPathAliases( interface SpecializeContentClassProps { classHierarchyInspector: ECClassHierarchyInspector; - contentClassName: EC.FullClassName; - filterClassNames: EC.FullClassName[]; + contentClassName: EC.FullClassNameDotNotation; + filterClassNames: EC.FullClassNameDotNotation[]; } -async function specializeContentClass(props: SpecializeContentClassProps): Promise { +async function specializeContentClass( + props: SpecializeContentClassProps, +): Promise { const filterClass = await getSpecializedPropertyClass(props.classHierarchyInspector, props.filterClassNames); if (!filterClass) { return props.contentClassName; @@ -872,8 +876,8 @@ async function specializeContentClass(props: SpecializeContentClassProps): Promi async function getSpecializedPropertyClass( classHierarchyInspector: ECClassHierarchyInspector, - classes: EC.FullClassName[], -): Promise { + classes: EC.FullClassNameDotNotation[], +): Promise { if (classes.length === 0) { return undefined; } @@ -888,7 +892,7 @@ async function getSpecializedPropertyClass( } interface HiddenClassNode { - fullName: EC.FullClassName; + fullName: EC.FullClassNameDotNotation; state: "hide" | "show"; children: HiddenClassNode[]; } @@ -935,7 +939,7 @@ async function getHiddenClassesTree( async function getDirectDerivedClasses(ecClass: EC.Class): Promise { const allDerived = await ecClass.getDerivedClasses(); return (await Promise.all(allDerived.map(async (c) => ({ derived: c, base: await c.baseClass })))) - .filter(({ base }) => base && compareFullClassNames(base.fullName, ecClass.fullName) === 0) + .filter(({ base }) => base && base.fullName.toLocaleLowerCase() === ecClass.fullName.toLocaleLowerCase()) .map(({ derived }) => derived); } @@ -994,7 +998,7 @@ function createWhereClauseForHiddenClasses( async function tryGetClass( schemaProvider: ECSchemaProvider, - fullClassName: EC.FullClassName, + fullClassName: EC.FullClassNameDotNotation, ): Promise { try { return await getClass(schemaProvider, fullClassName); diff --git a/packages/hierarchies/src/hierarchies/imodel/PredicateBasedHierarchyDefinition.ts b/packages/hierarchies/src/hierarchies/imodel/PredicateBasedHierarchyDefinition.ts index 3061f9570..5dd329d42 100644 --- a/packages/hierarchies/src/hierarchies/imodel/PredicateBasedHierarchyDefinition.ts +++ b/packages/hierarchies/src/hierarchies/imodel/PredicateBasedHierarchyDefinition.ts @@ -36,7 +36,7 @@ export type DefineInstanceNodeChildHierarchyLevelProps = Omit Promise); + parentInstancesNodePredicate: EC.FullClassNameDotNotation | ((parentNodeKey: InstancesNodeKey) => Promise); /** * Called to create a hierarchy level definition when the `parentInstancesNodePredicate` predicate passes. @@ -268,7 +268,7 @@ async function createHierarchyLevelDefinitions( } function groupInstanceIdsByClass(instanceKeys: InstanceKey[]) { - const instanceIdsByClass = new Map(); + const instanceIdsByClass = new Map(); instanceKeys.forEach((key) => { let instanceIds = instanceIdsByClass.get(key.className); if (!instanceIds) { diff --git a/packages/hierarchies/src/hierarchies/imodel/SearchHierarchyDefinition.ts b/packages/hierarchies/src/hierarchies/imodel/SearchHierarchyDefinition.ts index 55b581cb6..3ec76be97 100644 --- a/packages/hierarchies/src/hierarchies/imodel/SearchHierarchyDefinition.ts +++ b/packages/hierarchies/src/hierarchies/imodel/SearchHierarchyDefinition.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { defaultIfEmpty, defer, filter, firstValueFrom, map, merge, mergeAll, mergeMap, of, take, toArray } from "rxjs"; -import { compareFullClassNames } from "@itwin/presentation-shared"; import { HierarchyNode } from "../HierarchyNode.js"; import { HierarchyNodeIdentifier } from "../HierarchyNodeIdentifier.js"; import { createHierarchySearchHelper } from "../HierarchySearch.js"; @@ -115,7 +114,7 @@ export class SearchHierarchyDefinition implements RxjsHierarchyDefinition { if (identifier.imodelKey && identifier.imodelKey !== imodelKey) { return false; } - if (compareFullClassNames(identifier.className, rowInstanceKey.className) === 0) { + if (identifier.className.toLocaleLowerCase() === rowInstanceKey.className.toLocaleLowerCase()) { return true; } return firstValueFrom( @@ -213,7 +212,7 @@ export class SearchHierarchyDefinition implements RxjsHierarchyDefinition { for (const entry of entries) { /* v8 ignore else -- @preserve */ if ( - compareFullClassNames(entry.className, x.className) === 0 || + entry.className.toLocaleLowerCase() === x.className.toLocaleLowerCase() || (await imodelAccess.classDerivesFrom(entry.className, x.className)) || (await imodelAccess.classDerivesFrom(x.className, entry.className)) ) { @@ -231,7 +230,7 @@ export class SearchHierarchyDefinition implements RxjsHierarchyDefinition { continue; } if ( - compareFullClassNames(id.className, definition.fullClassName) !== 0 && + id.className.toLocaleLowerCase() !== definition.fullClassName.toLocaleLowerCase() && !(await Promise.all([ imodelAccess.classDerivesFrom(id.className, definition.fullClassName), imodelAccess.classDerivesFrom(definition.fullClassName, id.className), diff --git a/packages/hierarchies/src/hierarchies/imodel/TreeNodesReader.ts b/packages/hierarchies/src/hierarchies/imodel/TreeNodesReader.ts index d30d9b342..b60a1e8e3 100644 --- a/packages/hierarchies/src/hierarchies/imodel/TreeNodesReader.ts +++ b/packages/hierarchies/src/hierarchies/imodel/TreeNodesReader.ts @@ -5,7 +5,7 @@ import { from, mergeMap, of } from "rxjs"; import { Guid } from "@itwin/core-bentley"; -import { normalizeFullClassName, parseInstanceLabel } from "@itwin/presentation-shared"; +import { parseInstanceLabel } from "@itwin/presentation-shared"; import { LOGGING_NAMESPACE_INTERNAL as BASE_LOGGING_NAMESPACE } from "../internal/Common.js"; import { log } from "../internal/LoggingUtils.js"; @@ -48,7 +48,7 @@ export function readNodes(props: ReadNodesProps): Observable compareFullClassNames(c.fullName, parentNode.key.className) === 0, + (c) => c.fullName.toLocaleLowerCase() === parentNode.key.className.toLocaleLowerCase(), ); if (cutPosition >= 0) { return sortedClasses.slice(cutPosition + 1); @@ -68,7 +64,7 @@ export async function createBaseClassGroupsForSingleBaseClass( if ( !node.processingParams?.grouping?.byBaseClasses || !node.processingParams.grouping.byBaseClasses.fullClassNames.some( - (className) => compareFullClassNames(className, baseClassFullName) === 0, + (className) => className.toLocaleLowerCase() === baseClassFullName.toLocaleLowerCase(), ) ) { ungroupedNodes.push(node); @@ -106,7 +102,10 @@ export async function createBaseClassGroupsForSingleBaseClass( async function getGroupingBaseClassNames(nodes: ProcessedInstanceHierarchyNode[]) { const releaseMainThread = createMainThreadReleaseOnTimePassedHandler(); - const baseClasses = new SortedArray(compareFullClassNames, false); + const baseClasses = new SortedArray( + (a, b) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()), + false, + ); for (const node of nodes) { await releaseMainThread(); if (node.processingParams?.grouping?.byBaseClasses) { diff --git a/packages/hierarchies/src/hierarchies/imodel/operators/grouping/ClassGrouping.ts b/packages/hierarchies/src/hierarchies/imodel/operators/grouping/ClassGrouping.ts index 000697fed..c4931924c 100644 --- a/packages/hierarchies/src/hierarchies/imodel/operators/grouping/ClassGrouping.ts +++ b/packages/hierarchies/src/hierarchies/imodel/operators/grouping/ClassGrouping.ts @@ -4,11 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Dictionary } from "@itwin/core-bentley"; -import { - compareFullClassNames, - createMainThreadReleaseOnTimePassedHandler, - getClass, -} from "@itwin/presentation-shared"; +import { createMainThreadReleaseOnTimePassedHandler, getClass } from "@itwin/presentation-shared"; import { HierarchyNode } from "../../../HierarchyNode.js"; import type { EC, ECSchemaProvider } from "@itwin/presentation-shared"; @@ -18,14 +14,17 @@ import type { ProcessedInstanceHierarchyNode } from "../../IModelHierarchyNode.j import type { GroupingHandlerResult, ProcessedInstancesGroupingHierarchyNode } from "../Grouping.js"; interface ClassInfo { - fullName: EC.FullClassName; + fullName: EC.FullClassNameDotNotation; name: string; label?: string; } interface ClassGroupingInformation { ungrouped: ProcessedInstanceHierarchyNode[]; - grouped: Dictionary; + grouped: Dictionary< + EC.FullClassNameDotNotation, + { class: ClassInfo; groupedNodes: ProcessedInstanceHierarchyNode[] } + >; } /** @internal */ @@ -38,9 +37,10 @@ export async function createClassGroups( parentNode && HierarchyNode.isClassGroupingNode(parentNode) ? parentNode.key.className : undefined; const groupings: ClassGroupingInformation = { ungrouped: [], - grouped: new Dictionary( - compareFullClassNames, - ), + grouped: new Dictionary< + EC.FullClassNameDotNotation, + { class: ClassInfo; groupedNodes: ProcessedInstanceHierarchyNode[] } + >((a, b) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase())), }; const releaseMainThread = createMainThreadReleaseOnTimePassedHandler(); for (const node of nodes) { @@ -48,7 +48,7 @@ export async function createClassGroups( const nodeClassName = node.key.instanceKeys[0].className; if ( node.processingParams?.grouping?.byClass && - (!parentNodeClass || compareFullClassNames(nodeClassName, parentNodeClass) !== 0) + (!parentNodeClass || nodeClassName.toLocaleLowerCase() !== parentNodeClass.toLocaleLowerCase()) ) { let groupingInfo = groupings.grouped.get(nodeClassName); if (!groupingInfo) { diff --git a/packages/hierarchies/src/hierarchies/imodel/operators/grouping/PropertiesGrouping.ts b/packages/hierarchies/src/hierarchies/imodel/operators/grouping/PropertiesGrouping.ts index 2881b8193..27b2f5179 100644 --- a/packages/hierarchies/src/hierarchies/imodel/operators/grouping/PropertiesGrouping.ts +++ b/packages/hierarchies/src/hierarchies/imodel/operators/grouping/PropertiesGrouping.ts @@ -5,11 +5,9 @@ import { assert } from "@itwin/core-bentley"; import { - compareFullClassNames, createMainThreadReleaseOnTimePassedHandler, formatConcatenatedValue, getClass, - normalizeFullClassName, TypedPrimitiveValue, } from "@itwin/presentation-shared"; import { HierarchyNode } from "../../../HierarchyNode.js"; @@ -60,7 +58,7 @@ export interface PropertyGroupInfo { /** @internal */ export type PreviousPropertiesGroupingInfo = Array<{ - propertiesClassName: EC.FullClassName; + propertiesClassName: EC.FullClassNameDotNotation; propertyName: string; isRange?: boolean; }>; @@ -205,7 +203,7 @@ export async function createPropertyGroups( }; const hasPropertyIdentifier = groupingNode.key.properties.find( (x) => - compareFullClassNames(x.className, thisPropertyIdentifier.className) === 0 && + x.className.toLocaleLowerCase() === thisPropertyIdentifier.className.toLocaleLowerCase() && x.propertyName.toLocaleLowerCase() === thisPropertyIdentifier.propertyName.toLocaleLowerCase(), ); if (!hasPropertyIdentifier) { @@ -311,17 +309,17 @@ function createNodePropertyGroupPathMatchers( return (x) => key.properties.some( (p) => - compareFullClassNames(p.className, x.propertiesClassName) === 0 && + p.className.toLocaleLowerCase() === x.propertiesClassName.toLocaleLowerCase() && p.propertyName.toLocaleLowerCase() === x.propertyName.toLocaleLowerCase() && !!x.isRange, ); case "property-grouping:range": return (x) => - compareFullClassNames(key.propertyClassName, x.propertiesClassName) === 0 && + key.propertyClassName.toLocaleLowerCase() === x.propertiesClassName.toLocaleLowerCase() && key.propertyName.toLocaleLowerCase() === x.propertyName.toLocaleLowerCase(); case "property-grouping:value": return (x) => - compareFullClassNames(key.propertyClassName, x.propertiesClassName) === 0 && + key.propertyClassName.toLocaleLowerCase() === x.propertiesClassName.toLocaleLowerCase() && key.propertyName.toLocaleLowerCase() === x.propertyName.toLocaleLowerCase(); } }); @@ -353,8 +351,7 @@ export async function getUniquePropertiesGroupInfo( ? previousPropertiesInfo[previousPropertiesInfo.length - 1].propertyGroupKey : ""; const propertyGroupKey = `${lastKey}:${propertyGroup.propertyName}(${mapKeyRanges})`; - const mapKey = - `${normalizeFullClassName(byProperties.propertiesClassName)}:${propertyGroupKey}`.toLocaleLowerCase(); + const mapKey = `${byProperties.propertiesClassName}:${propertyGroupKey}`.toLocaleLowerCase(); let isAlreadyGrouped = false; if (parentPropertyGroupPath.length > 0 && propertyGroupIndex < parentPropertyGroupPath.length) { @@ -399,12 +396,12 @@ function getRangesAsString(ranges?: HierarchyNodePropertyValueRange[]): string { async function shouldCreatePropertyGroup( handlerGroupingParams: PropertyGroupInfo, nodePropertyGroupingParams: HierarchyNodePropertiesGroupingParams, - nodeFullClassName: EC.FullClassName, + nodeFullClassName: EC.FullClassNameDotNotation, classHierarchyInspector: ECClassHierarchyInspector, ): Promise { if ( - compareFullClassNames(nodePropertyGroupingParams.propertiesClassName, handlerGroupingParams.ecClass.fullName) !== - 0 || + nodePropertyGroupingParams.propertiesClassName.toLocaleLowerCase() !== + handlerGroupingParams.ecClass.fullName.toLocaleLowerCase() || nodePropertyGroupingParams.propertyGroups.length < handlerGroupingParams.previousPropertiesGroupingInfo.length + 1 ) { return false; @@ -433,7 +430,8 @@ export function doPreviousPropertiesMatch( previousPropertiesGroupingInfo.length <= nodesProperties.propertyGroups.length && previousPropertiesGroupingInfo.every( (groupingInfo, index) => - compareFullClassNames(groupingInfo.propertiesClassName, nodesProperties.propertiesClassName) === 0 && + groupingInfo.propertiesClassName.toLocaleLowerCase() === + nodesProperties.propertiesClassName.toLocaleLowerCase() && groupingInfo.propertyName.toLocaleLowerCase() === nodesProperties.propertyGroups[index].propertyName.toLocaleLowerCase() && !!groupingInfo.isRange === !!nodesProperties.propertyGroups[index].ranges, diff --git a/packages/hierarchies/src/test/HierarchyNodeKey.test.ts b/packages/hierarchies/src/test/HierarchyNodeKey.test.ts index 36d4810a1..ebd53c6bf 100644 --- a/packages/hierarchies/src/test/HierarchyNodeKey.test.ts +++ b/packages/hierarchies/src/test/HierarchyNodeKey.test.ts @@ -78,7 +78,7 @@ describe("HierarchyNodeKey", () => { expect( HierarchyNodeKey.equals( { type: "instances", instanceKeys: [{ className: "s.a", id: "0" }] }, - { type: "instances", instanceKeys: [{ className: "S:A", id: "0" }] }, + { type: "instances", instanceKeys: [{ className: "S.A", id: "0" }] }, ), ).toBe(true); expect( @@ -111,7 +111,7 @@ describe("HierarchyNodeKey", () => { expect( HierarchyNodeKey.equals( { type: "class-grouping", className: "s.x" }, - { type: "class-grouping", className: "S:X" }, + { type: "class-grouping", className: "S.X" }, ), ).toBe(true); expect( @@ -153,7 +153,7 @@ describe("HierarchyNodeKey", () => { expect( HierarchyNodeKey.equals( { type: "property-grouping:other", properties: [{ className: "s.x", propertyName: "y" }] }, - { type: "property-grouping:other", properties: [{ className: "S:X", propertyName: "Y" }] }, + { type: "property-grouping:other", properties: [{ className: "S.X", propertyName: "Y" }] }, ), ).toBe(true); expect( @@ -172,7 +172,7 @@ describe("HierarchyNodeKey", () => { formattedPropertyValue: "value", }; expect(HierarchyNodeKey.equals(baseValue, baseValue)).toBe(true); - expect(HierarchyNodeKey.equals(baseValue, { ...baseValue, propertyClassName: "schema:classname" })).toBe(true); + expect(HierarchyNodeKey.equals(baseValue, { ...baseValue, propertyClassName: "schema.classname" })).toBe(true); expect(HierarchyNodeKey.equals(baseValue, { ...baseValue, formattedPropertyValue: "value2" })).toBe(false); expect(HierarchyNodeKey.equals(baseValue, { ...baseValue, propertyName: "other name" })).toBe(false); expect(HierarchyNodeKey.equals(baseValue, { ...baseValue, propertyClassName: "Schema.Other" })).toBe(false); @@ -188,7 +188,7 @@ describe("HierarchyNodeKey", () => { }; expect(HierarchyNodeKey.equals(baseValueRange, baseValueRange)).toBe(true); expect( - HierarchyNodeKey.equals(baseValueRange, { ...baseValueRange, propertyClassName: "schema:classname" }), + HierarchyNodeKey.equals(baseValueRange, { ...baseValueRange, propertyClassName: "schema.classname" }), ).toBe(true); expect(HierarchyNodeKey.equals(baseValueRange, { ...baseValueRange, toValue: 3 })).toBe(false); expect(HierarchyNodeKey.equals(baseValueRange, { ...baseValueRange, fromValue: 2 })).toBe(false); diff --git a/packages/hierarchies/src/test/HierarchySearch.test.ts b/packages/hierarchies/src/test/HierarchySearch.test.ts index fb61cecc0..cf4329488 100644 --- a/packages/hierarchies/src/test/HierarchySearch.test.ts +++ b/packages/hierarchies/src/test/HierarchySearch.test.ts @@ -1092,11 +1092,11 @@ describe("createHierarchySearchHelper", () => { it("returns correct child node props when parent node has instance node target path", () => { const result = createHierarchySearchHelper([], { search: { - childrenTargetPaths: [{ identifier: { imodelKey: "a", className: "test:className", id: "id" } }], + childrenTargetPaths: [{ identifier: { imodelKey: "a", className: "test.className", id: "id" } }], hasSearchTargetAncestor: true, }, }).createChildNodeProps({ - nodeKey: { type: "instances", instanceKeys: [{ imodelKey: "test", className: "test:className", id: "id" }] }, + nodeKey: { type: "instances", instanceKeys: [{ imodelKey: "test", className: "test.className", id: "id" }] }, }); expect(result).toEqual({ search: { hasSearchTargetAncestor: true } }); diff --git a/packages/hierarchies/src/test/Utils.ts b/packages/hierarchies/src/test/Utils.ts index 356fb9cae..71fe2b80a 100644 --- a/packages/hierarchies/src/test/Utils.ts +++ b/packages/hierarchies/src/test/Utils.ts @@ -5,7 +5,7 @@ import { vi } from "vitest"; import { Dictionary, Logger } from "@itwin/core-bentley"; -import { compareFullClassNames, getClass, normalizeFullClassName } from "@itwin/presentation-shared"; +import { getClass } from "@itwin/presentation-shared"; import type { Mock } from "vitest"; import type { LogLevel } from "@itwin/core-bentley"; @@ -90,7 +90,7 @@ export function createTestProcessedGroupingNode< ): Omit & { children: TChild[] } { return { label: "test", - key: { type: "class-grouping", className: normalizeFullClassName("TestSchema.TestClass") }, + key: { type: "class-grouping", className: "TestSchema.TestClass" }, parentKeys: [], groupedInstanceKeys: [], children: new Array(), @@ -175,14 +175,18 @@ export type TStubRelationshipClassFunc = ( ) => EC.RelationshipClass & ECClassExtraMembers; export type TStubCustomAttributesFunc = (props: { schemaName: string; - attributes: Map; + attributes: Map; }) => void; export function createECSchemaProviderStub() { const schemaStubs = new Map(); - const classes = new Dictionary(compareFullClassNames); // className -> class - const classHierarchy = new Dictionary(compareFullClassNames); // className -> baseClassName - const customAttributes = new Map>(); // schemaName -> (className -> customAttribute) + const classes = new Dictionary((a, b) => + a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()), + ); // className -> class + const classHierarchy = new Dictionary((a, b) => + a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()), + ); // className -> baseClassName + const customAttributes = new Map>(); // schemaName -> (className -> customAttribute) const getSchemaImpl = (schemaName: string) => { let schemaStub = schemaStubs.get(schemaName); if (!schemaStub) { @@ -196,17 +200,17 @@ export function createECSchemaProviderStub() { } return schemaStub; }; - const getDerivedClasses = (classFullName: EC.FullClassName): EC.Class[] => { + const getDerivedClasses = (classFullName: EC.FullClassNameDotNotation): EC.Class[] => { const derivedClasses = new Array(); for (const { key: derivedClassName, value: baseClassName } of classHierarchy) { - if (compareFullClassNames(baseClassName, classFullName) === 0) { + if (baseClassName.toLocaleLowerCase() === classFullName.toLocaleLowerCase()) { derivedClasses.push(classes.get(derivedClassName)!); derivedClasses.push(...getDerivedClasses(derivedClassName)); } } return derivedClasses; }; - const getBaseClasses = (classFullName: EC.FullClassName): EC.Class[] => { + const getBaseClasses = (classFullName: EC.FullClassNameDotNotation): EC.Class[] => { const baseClasses = new Array(); const baseClassName = classHierarchy.get(classFullName); if (baseClassName) { @@ -233,14 +237,16 @@ export function createECSchemaProviderStub() { }, getDerivedClasses: async () => getDerivedClasses(`${props.schemaName}.${props.className}`), is: async (targetClassOrClassName: EC.Class | string, schemaName?: string) => { - const myName: EC.FullClassName = `${props.schemaName}.${props.className}`; - const targetName: EC.FullClassName = + const myName: EC.FullClassNameDotNotation = `${props.schemaName}.${props.className}`; + const targetName: EC.FullClassNameDotNotation = typeof targetClassOrClassName === "string" ? `${schemaName!}.${targetClassOrClassName}` : targetClassOrClassName.fullName; return ( - compareFullClassNames(targetName, myName) === 0 || - getBaseClasses(myName).some((baseClass) => compareFullClassNames(baseClass.fullName, targetName) === 0) + targetName.toLocaleLowerCase() === myName.toLocaleLowerCase() || + getBaseClasses(myName).some( + (baseClass) => baseClass.fullName.toLocaleLowerCase() === targetName.toLocaleLowerCase(), + ) ); }, getCustomAttributes: async () => props.customAttributes ?? new Map(), @@ -283,7 +289,7 @@ export function createECSchemaProviderStub() { }; const stubCustomAttribute: TStubCustomAttributesFunc = (props) => { const schemaCustomAttributes = - customAttributes.get(props.schemaName) ?? new Map(); + customAttributes.get(props.schemaName) ?? new Map(); customAttributes.set(props.schemaName, schemaCustomAttributes); for (const [className, attribute] of props.attributes) { schemaCustomAttributes.set(className, { ...attribute, className }); @@ -310,7 +316,7 @@ export function createClassHierarchyInspectorStub( stubEntityClass: schemaProvider.stubEntityClass, stubRelationshipClass: schemaProvider.stubRelationshipClass, stubOtherClass: schemaProvider.stubOtherClass, - classDerivesFrom: async (derived: EC.FullClassName, base: EC.FullClassName) => { + classDerivesFrom: async (derived: EC.FullClassNameDotNotation, base: EC.FullClassNameDotNotation) => { const derivedClass = await getClass(schemaProvider, derived); const baseClass = await getClass(schemaProvider, base); return derivedClass.is(baseClass); diff --git a/packages/hierarchies/src/test/imodel/NodeSelectQueryFactory.test.ts b/packages/hierarchies/src/test/imodel/NodeSelectQueryFactory.test.ts index 7fd0e31a7..8faedd5e8 100644 --- a/packages/hierarchies/src/test/imodel/NodeSelectQueryFactory.test.ts +++ b/packages/hierarchies/src/test/imodel/NodeSelectQueryFactory.test.ts @@ -134,7 +134,7 @@ describe("createNodesQueryClauseFactory", () => { }); expect(trimWhitespace(result)).toBe( trimWhitespace(` - ec_ClassName(class_id) AS ${NodeSelectClauseColumnNames.FullClassName}, + ec_ClassName(class_id, 's.c') AS ${NodeSelectClauseColumnNames.FullClassName}, instance_id AS ${NodeSelectClauseColumnNames.ECInstanceId}, 'label' AS ${NodeSelectClauseColumnNames.DisplayLabel}, CAST(TRUE AS BOOLEAN) AS ${NodeSelectClauseColumnNames.HasChildren}, @@ -186,7 +186,7 @@ describe("createNodesQueryClauseFactory", () => { }); expect(trimWhitespace(result)).toBe( trimWhitespace(` - ec_ClassName(class_id) AS ${NodeSelectClauseColumnNames.FullClassName}, + ec_ClassName(class_id, 's.c') AS ${NodeSelectClauseColumnNames.FullClassName}, instance_id AS ${NodeSelectClauseColumnNames.ECInstanceId}, 'label' AS ${NodeSelectClauseColumnNames.DisplayLabel}, CAST(NULL AS BOOLEAN) AS ${NodeSelectClauseColumnNames.HasChildren}, @@ -251,7 +251,7 @@ describe("createNodesQueryClauseFactory", () => { }); expect(trimWhitespace(result)).toBe( trimWhitespace(` - ec_ClassName(x.ECClassId) AS ${NodeSelectClauseColumnNames.FullClassName}, + ec_ClassName(x.ECClassId, 's.c') AS ${NodeSelectClauseColumnNames.FullClassName}, x.ECInstanceId AS ${NodeSelectClauseColumnNames.ECInstanceId}, x.Label AS ${NodeSelectClauseColumnNames.DisplayLabel}, CAST(x.HasChildren AS BOOLEAN) AS ${NodeSelectClauseColumnNames.HasChildren}, @@ -281,7 +281,7 @@ describe("createNodesQueryClauseFactory", () => { const labelClause = await instanceLabelSelectClauseFactory.createSelectClause({ classAlias: "this" }); expect(trimWhitespace(result)).toBe( trimWhitespace(` - ec_ClassName(class_id) AS ${NodeSelectClauseColumnNames.FullClassName}, + ec_ClassName(class_id, 's.c') AS ${NodeSelectClauseColumnNames.FullClassName}, instance_id AS ${NodeSelectClauseColumnNames.ECInstanceId}, ${labelClause} AS ${NodeSelectClauseColumnNames.DisplayLabel}, CAST(NULL AS BOOLEAN) AS ${NodeSelectClauseColumnNames.HasChildren}, @@ -303,7 +303,7 @@ describe("createNodesQueryClauseFactory", () => { }); expect(trimWhitespace(result)).toBe( trimWhitespace(` - ec_ClassName(class_id) AS ${NodeSelectClauseColumnNames.FullClassName}, + ec_ClassName(class_id, 's.c') AS ${NodeSelectClauseColumnNames.FullClassName}, instance_id AS ${NodeSelectClauseColumnNames.ECInstanceId}, 'label' AS ${NodeSelectClauseColumnNames.DisplayLabel}, CAST(NULL AS BOOLEAN) AS ${NodeSelectClauseColumnNames.HasChildren}, @@ -339,7 +339,7 @@ describe("createNodesQueryClauseFactory", () => { }); expect(trimWhitespace(result)).toBe( trimWhitespace(` - ec_ClassName(class_id) AS ${NodeSelectClauseColumnNames.FullClassName}, + ec_ClassName(class_id, 's.c') AS ${NodeSelectClauseColumnNames.FullClassName}, instance_id AS ${NodeSelectClauseColumnNames.ECInstanceId}, 'label' AS ${NodeSelectClauseColumnNames.DisplayLabel}, CAST(NULL AS BOOLEAN) AS ${NodeSelectClauseColumnNames.HasChildren}, @@ -533,7 +533,7 @@ describe("createNodesQueryClauseFactory", () => { describe("by properties", () => { interface TestPropertyFilterProps { - className: EC.FullClassName; + className: EC.FullClassNameDotNotation; classAlias: string; rule: GenericInstanceFilterRule | GenericInstanceFilterRuleGroup; expectedECSql: string; @@ -1343,7 +1343,7 @@ describe("createNodesQueryClauseFactory", () => { const hideClass = imodelAccess.stubEntityClass({ schemaName: "s2", className: "y", baseClass: selectClass }); imodelAccess.stubCustomAttribute({ schemaName: "s2", - attributes: new Map([ + attributes: new Map([ ["CoreCustomAttributes.HiddenSchema", { className: "CoreCustomAttributes.HiddenSchema" }], ]), }); @@ -1363,7 +1363,7 @@ describe("createNodesQueryClauseFactory", () => { const showClass = imodelAccess.stubEntityClass({ schemaName: "s3", className: "z", baseClass: hideClass }); imodelAccess.stubCustomAttribute({ schemaName: "s2", - attributes: new Map([ + attributes: new Map([ [ "CoreCustomAttributes.HiddenSchema", { className: "CoreCustomAttributes.HiddenSchema", ["ShowClasses"]: false }, @@ -1372,7 +1372,7 @@ describe("createNodesQueryClauseFactory", () => { }); imodelAccess.stubCustomAttribute({ schemaName: "s3", - attributes: new Map([ + attributes: new Map([ [ "CoreCustomAttributes.HiddenSchema", { className: "CoreCustomAttributes.HiddenSchema", ["ShowClasses"]: true }, @@ -1399,7 +1399,7 @@ describe("createNodesQueryClauseFactory", () => { }); imodelAccess.stubCustomAttribute({ schemaName: "s2", - attributes: new Map([ + attributes: new Map([ [ "CoreCustomAttributes.HiddenSchema", { className: "CoreCustomAttributes.HiddenSchema", ["ShowClasses"]: false }, diff --git a/packages/hierarchies/src/test/imodel/SearchHierarchyDefinition.test.ts b/packages/hierarchies/src/test/imodel/SearchHierarchyDefinition.test.ts index 9619307c4..3c68975d0 100644 --- a/packages/hierarchies/src/test/imodel/SearchHierarchyDefinition.test.ts +++ b/packages/hierarchies/src/test/imodel/SearchHierarchyDefinition.test.ts @@ -203,7 +203,7 @@ describe("SearchHierarchyDefinition", () => { return { label: "test", key: createTestInstanceNodeKey({ - instanceKeys: [createTestInstanceKey({ className: "Schema:Class", id: "0x1" })], + instanceKeys: [createTestInstanceKey({ className: "Schema.Class", id: "0x1" })], }), ...overrides, }; @@ -217,15 +217,15 @@ describe("SearchHierarchyDefinition", () => { }); it("assigns search props when row has matching search columns and target paths match", async () => { - const targetIdentifier: HierarchyNodeIdentifier = { className: "Schema:Class", id: "0x1", imodelKey: "imodel" }; + const targetIdentifier: HierarchyNodeIdentifier = { className: "Schema.Class", id: "0x1", imodelKey: "imodel" }; const targetPaths: HierarchySearchTree[] = [{ identifier: targetIdentifier }]; const parsedNode = createSourceInstanceNode({ - key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema:Class", id: "0x1" }] }), + key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema.Class", id: "0x1" }] }), }); const def = createSearchHierarchyDefinition({ targetPaths, source: { parseNode: () => of(parsedNode) } }); const result = await firstValueFrom( def.parseNode({ - row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema:Class" }, + row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema.Class" }, parentNode: undefined, imodelKey: "imodel", }), @@ -235,14 +235,14 @@ describe("SearchHierarchyDefinition", () => { }); it("doesn't match when identifier id differs from row id", async () => { - const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema:Class", id: "0x2" } }]; + const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema.Class", id: "0x2" } }]; const parsedNode = createSourceInstanceNode({ - key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema:Class", id: "0x1" }] }), + key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema.Class", id: "0x1" }] }), }); const def = createSearchHierarchyDefinition({ targetPaths, source: { parseNode: () => of(parsedNode) } }); const result = await firstValueFrom( def.parseNode({ - row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema:Class" }, + row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema.Class" }, parentNode: undefined, imodelKey: "imodel", }), @@ -253,12 +253,12 @@ describe("SearchHierarchyDefinition", () => { it("doesn't match generic node identifier", async () => { const targetPaths: HierarchySearchTree[] = [{ identifier: { type: "generic", id: "0x1" } }]; const parsedNode = createSourceInstanceNode({ - key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema:Class", id: "0x1" }] }), + key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema.Class", id: "0x1" }] }), }); const def = createSearchHierarchyDefinition({ targetPaths, source: { parseNode: () => of(parsedNode) } }); const result = await firstValueFrom( def.parseNode({ - row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema:Class" }, + row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema.Class" }, parentNode: undefined, imodelKey: "imodel", }), @@ -268,15 +268,15 @@ describe("SearchHierarchyDefinition", () => { it("doesn't match when imodelKey differs", async () => { const targetPaths: HierarchySearchTree[] = [ - { identifier: { className: "Schema:Class", id: "0x1", imodelKey: "other-imodel" } }, + { identifier: { className: "Schema.Class", id: "0x1", imodelKey: "other-imodel" } }, ]; const parsedNode = createSourceInstanceNode({ - key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema:Class", id: "0x1" }] }), + key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema.Class", id: "0x1" }] }), }); const def = createSearchHierarchyDefinition({ targetPaths, source: { parseNode: () => of(parsedNode) } }); const result = await firstValueFrom( def.parseNode({ - row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema:Class" }, + row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema.Class" }, parentNode: undefined, imodelKey: "imodel", }), @@ -285,14 +285,14 @@ describe("SearchHierarchyDefinition", () => { }); it("matches when identifier has no imodelKey", async () => { - const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema:Class", id: "0x1" } }]; + const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema.Class", id: "0x1" } }]; const parsedNode = createSourceInstanceNode({ - key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema:Class", id: "0x1" }] }), + key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema.Class", id: "0x1" }] }), }); const def = createSearchHierarchyDefinition({ targetPaths, source: { parseNode: () => of(parsedNode) } }); const result = await firstValueFrom( def.parseNode({ - row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema:Class" }, + row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema.Class" }, parentNode: undefined, imodelKey: "imodel", }), @@ -304,15 +304,15 @@ describe("SearchHierarchyDefinition", () => { it("falls back to classDerivesFrom when class names differ", async () => { const classDerivesFrom = vi.fn(); classDerivesFrom.mockImplementation(async (derived, candidate) => { - if (derived === "Schema:Derived" && candidate === "Schema:Base") { + if (derived === "Schema.Derived" && candidate === "Schema.Base") { return true; } return false; }); - const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema:Base", id: "0x1" } }]; + const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema.Base", id: "0x1" } }]; const parsedNode = createSourceInstanceNode({ - key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema:Derived", id: "0x1" }] }), + key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema.Derived", id: "0x1" }] }), }); const def = createSearchHierarchyDefinition({ targetPaths, @@ -321,7 +321,7 @@ describe("SearchHierarchyDefinition", () => { }); const result = await firstValueFrom( def.parseNode({ - row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema:Derived" }, + row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema.Derived" }, parentNode: undefined, imodelKey: "imodel", }), @@ -333,9 +333,9 @@ describe("SearchHierarchyDefinition", () => { it("doesn't match when classDerivesFrom returns false for both directions", async () => { const classDerivesFrom = vi.fn().mockResolvedValue(false); - const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema:Unrelated", id: "0x1" } }]; + const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema.Unrelated", id: "0x1" } }]; const parsedNode = createSourceInstanceNode({ - key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema:Derived", id: "0x1" }] }), + key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema.Derived", id: "0x1" }] }), }); const def = createSearchHierarchyDefinition({ targetPaths, @@ -344,7 +344,7 @@ describe("SearchHierarchyDefinition", () => { }); const result = await firstValueFrom( def.parseNode({ - row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema:Derived" }, + row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema.Derived" }, parentNode: undefined, imodelKey: "imodel", }), @@ -354,15 +354,15 @@ describe("SearchHierarchyDefinition", () => { it("assigns autoExpand from search tree options", async () => { const targetPaths: HierarchySearchTree[] = [ - { identifier: { className: "Schema:Class", id: "0x1" }, options: { autoExpand: true } }, + { identifier: { className: "Schema.Class", id: "0x1" }, options: { autoExpand: true } }, ]; const parsedNode = createSourceInstanceNode({ - key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema:Class", id: "0x1" }] }), + key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema.Class", id: "0x1" }] }), }); const def = createSearchHierarchyDefinition({ targetPaths, source: { parseNode: () => of(parsedNode) } }); const result = await firstValueFrom( def.parseNode({ - row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema:Class" }, + row: { [ECSQL_COLUMN_NAME_SearchECInstanceId]: "0x1", [ECSQL_COLUMN_NAME_SearchClassName]: "Schema.Class" }, parentNode: undefined, imodelKey: "imodel", }), @@ -380,15 +380,15 @@ describe("SearchHierarchyDefinition", () => { const constProps = { imodelAccess: stubIModelAccess, ...nodeSelectClauseFactory }; it("returns source definitions when search identifiers are not applicable to the level", async () => { - const sourceDefs: HierarchyLevelDefinition = [{ fullClassName: "Schema:Class", query: { ecsql: "SELECT *" } }]; + const sourceDefs: HierarchyLevelDefinition = [{ fullClassName: "Schema.Class", query: { ecsql: "SELECT *" } }]; const parentNode = { - key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema:Parent", id: "0x2" }] }), + key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema.Parent", id: "0x2" }] }), label: "parent", parentKeys: [], search: { isSearchTarget: true }, }; const def = createSearchHierarchyDefinition({ - targetPaths: [{ identifier: { className: "Schema:Class", id: "0x1" } }], + targetPaths: [{ identifier: { className: "Schema.Class", id: "0x1" } }], source: { defineHierarchyLevel: () => of(sourceDefs) }, }); const result = await firstValueFrom(def.defineHierarchyLevel({ parentNode, ...constProps })); @@ -434,9 +434,9 @@ describe("SearchHierarchyDefinition", () => { }); it("applies ECInstanceIds search for instance definitions matching search identifier", async () => { - const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema:Class", id: "0x1" } }]; + const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema.Class", id: "0x1" } }]; const instanceDef: InstanceNodesQueryDefinition = { - fullClassName: "Schema:Class", + fullClassName: "Schema.Class", query: { ecsql: "SELECT * FROM Schema.Class" }, }; const def = createSearchHierarchyDefinition({ @@ -454,9 +454,9 @@ describe("SearchHierarchyDefinition", () => { stubIModelAccess.stubEntityClass({ schemaName: "Schema", className: "Class" }); stubIModelAccess.stubEntityClass({ schemaName: "Schema", className: "Other" }); - const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema:Other", id: "0x1" } }]; + const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema.Other", id: "0x1" } }]; const instanceDef: InstanceNodesQueryDefinition = { - fullClassName: "Schema:Class", + fullClassName: "Schema.Class", query: { ecsql: "SELECT * FROM Schema.Class" }, }; const def = createSearchHierarchyDefinition({ @@ -472,9 +472,9 @@ describe("SearchHierarchyDefinition", () => { const baseClass = stubIModelAccess.stubEntityClass({ schemaName: "Schema", className: "Base" }); stubIModelAccess.stubEntityClass({ schemaName: "Schema", className: "Derived", baseClass }); - const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema:Derived", id: "0x1" } }]; + const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema.Derived", id: "0x1" } }]; const instanceDef: InstanceNodesQueryDefinition = { - fullClassName: "Schema:Base", + fullClassName: "Schema.Base", query: { ecsql: "SELECT * FROM Schema.Base" }, }; const def = createSearchHierarchyDefinition({ @@ -488,10 +488,10 @@ describe("SearchHierarchyDefinition", () => { it("skips instance identifier with non-matching imodelKey", async () => { const targetPaths: HierarchySearchTree[] = [ - { identifier: { className: "Schema:Class", id: "0x1", imodelKey: "other-imodel" } }, + { identifier: { className: "Schema.Class", id: "0x1", imodelKey: "other-imodel" } }, ]; const instanceDef: InstanceNodesQueryDefinition = { - fullClassName: "Schema:Class", + fullClassName: "Schema.Class", query: { ecsql: "SELECT * FROM Schema.Class" }, }; const def = createSearchHierarchyDefinition({ @@ -507,11 +507,11 @@ describe("SearchHierarchyDefinition", () => { stubIModelAccess.stubEntityClass({ schemaName: "Schema", className: "Derived", baseClass }); const targetPaths: HierarchySearchTree[] = [ - { identifier: { className: "Schema:Derived", id: "0x1" } }, - { identifier: { className: "Schema:Base", id: "0x1" } }, + { identifier: { className: "Schema.Derived", id: "0x1" } }, + { identifier: { className: "Schema.Base", id: "0x1" } }, ]; const instanceDef: InstanceNodesQueryDefinition = { - fullClassName: "Schema:Base", + fullClassName: "Schema.Base", query: { ecsql: "SELECT * FROM Schema.Base" }, }; const def = createSearchHierarchyDefinition({ @@ -534,11 +534,11 @@ describe("SearchHierarchyDefinition", () => { stubIModelAccess.stubEntityClass({ schemaName: "Schema", className: "Derived", baseClass }); const targetPaths: HierarchySearchTree[] = [ - { identifier: { className: "Schema:Base", id: "0x1" } }, - { identifier: { className: "Schema:Derived", id: "0x1" } }, + { identifier: { className: "Schema.Base", id: "0x1" } }, + { identifier: { className: "Schema.Derived", id: "0x1" } }, ]; const instanceDef: InstanceNodesQueryDefinition = { - fullClassName: "Schema:Base", + fullClassName: "Schema.Base", query: { ecsql: "SELECT * FROM Schema.Base" }, }; const def = createSearchHierarchyDefinition({ @@ -551,13 +551,13 @@ describe("SearchHierarchyDefinition", () => { }); it("applies ECInstanceIds selector when parent has search target ancestor", async () => { - const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema:Class", id: "0x1" } }]; + const targetPaths: HierarchySearchTree[] = [{ identifier: { className: "Schema.Class", id: "0x1" } }]; const instanceDef: InstanceNodesQueryDefinition = { - fullClassName: "Schema:Class", + fullClassName: "Schema.Class", query: { ecsql: "SELECT * FROM Schema.Class" }, }; const parentNode = { - key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema:Parent", id: "0x2" }] }), + key: createTestInstanceNodeKey({ instanceKeys: [{ className: "Schema.Parent", id: "0x2" }] }), label: "parent", parentKeys: [], search: { hasSearchTargetAncestor: true, childrenTargetPaths: targetPaths }, @@ -576,7 +576,7 @@ describe("SearchHierarchyDefinition", () => { it("skips generic identifiers when looking for instance definitions", async () => { const targetPaths: HierarchySearchTree[] = [{ identifier: { type: "generic", id: "test-key" } }]; const instanceDef: InstanceNodesQueryDefinition = { - fullClassName: "Schema:Class", + fullClassName: "Schema.Class", query: { ecsql: "SELECT * FROM Schema.Class" }, }; const def = createSearchHierarchyDefinition({ diff --git a/packages/hierarchies/src/test/imodel/operators/grouping/AutoExpand.test.ts b/packages/hierarchies/src/test/imodel/operators/grouping/AutoExpand.test.ts index 027b99d27..dc79a42db 100644 --- a/packages/hierarchies/src/test/imodel/operators/grouping/AutoExpand.test.ts +++ b/packages/hierarchies/src/test/imodel/operators/grouping/AutoExpand.test.ts @@ -23,19 +23,19 @@ describe("AutoExpand", () => { { testName: "Base class grouping", testParams: { - groupingNodeKey: { type: "class-grouping", class: { name: "TestSchema:BaseClass", label: "Base Class" } }, + groupingNodeKey: { type: "class-grouping", class: { name: "TestSchema.BaseClass", label: "Base Class" } }, groupingType: "base-class", createGroupedNodeProcessingParams: ( autoExpand: HierarchyNodeAutoExpandProp | undefined, ): InstanceHierarchyNodeProcessingParams => { - return { grouping: { byBaseClasses: { fullClassNames: ["TestSchema:BaseClass"], autoExpand } } }; + return { grouping: { byBaseClasses: { fullClassNames: ["TestSchema.BaseClass"], autoExpand } } }; }, }, }, { testName: "Class grouping", testParams: { - groupingNodeKey: { type: "class-grouping", class: { name: "TestSchema:A", label: "A" } }, + groupingNodeKey: { type: "class-grouping", class: { name: "TestSchema.A", label: "A" } }, groupingType: "class", createGroupedNodeProcessingParams: ( autoExpand: HierarchyNodeAutoExpandProp | undefined, @@ -50,7 +50,7 @@ describe("AutoExpand", () => { groupingNodeKey: { type: "property-grouping:other", label: "1", - property: { propertyName: "length", propertiesClassName: "TestSchema:BaseClass" }, + property: { propertyName: "length", propertiesClassName: "TestSchema.BaseClass" }, }, groupingType: "property", createGroupedNodeProcessingParams: ( @@ -59,7 +59,7 @@ describe("AutoExpand", () => { return { grouping: { byProperties: { - propertiesClassName: "TestSchema:BaseClass", + propertiesClassName: "TestSchema.BaseClass", autoExpand, propertyGroups: [{ propertyName: "length", propertyValue: 1 }], }, @@ -89,7 +89,7 @@ describe("AutoExpand", () => { key: testParams.groupingNodeKey as GroupingNodeKey, children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: testParams.createGroupedNodeProcessingParams("single-child"), }), @@ -114,17 +114,17 @@ describe("AutoExpand", () => { key: testParams.groupingNodeKey as GroupingNodeKey, children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: testParams.createGroupedNodeProcessingParams("single-child"), }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, label: "1", processingParams: testParams.createGroupedNodeProcessingParams("always"), }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x3" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x3" }] }, label: "1", processingParams: testParams.createGroupedNodeProcessingParams("single-child"), }), @@ -149,11 +149,11 @@ describe("AutoExpand", () => { key: testParams.groupingNodeKey as GroupingNodeKey, children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, label: "1", processingParams: testParams.createGroupedNodeProcessingParams("single-child"), }), @@ -178,7 +178,7 @@ describe("AutoExpand", () => { key: testParams.groupingNodeKey as GroupingNodeKey, children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: testParams.createGroupedNodeProcessingParams(undefined), }), diff --git a/packages/hierarchies/src/test/imodel/operators/grouping/BaseClassGrouping.test.ts b/packages/hierarchies/src/test/imodel/operators/grouping/BaseClassGrouping.test.ts index 83c7193d0..2e25f70be 100644 --- a/packages/hierarchies/src/test/imodel/operators/grouping/BaseClassGrouping.test.ts +++ b/packages/hierarchies/src/test/imodel/operators/grouping/BaseClassGrouping.test.ts @@ -182,7 +182,7 @@ describe("BaseClassGrouping", () => { grouping: { byBaseClasses: { /* cspell:disable-next-line */ - fullClassNames: ["testschema:parentclass"], + fullClassNames: ["testschema.parentclass"], }, }, }, diff --git a/packages/hierarchies/src/test/imodel/operators/grouping/GroupHiding.test.ts b/packages/hierarchies/src/test/imodel/operators/grouping/GroupHiding.test.ts index de8a2023c..626301c54 100644 --- a/packages/hierarchies/src/test/imodel/operators/grouping/GroupHiding.test.ts +++ b/packages/hierarchies/src/test/imodel/operators/grouping/GroupHiding.test.ts @@ -17,7 +17,7 @@ describe("GroupHiding", () => { createTestProcessedGroupingNode({ children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true } } }, }), @@ -40,12 +40,12 @@ describe("GroupHiding", () => { createTestProcessedGroupingNode({ children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: { grouping: { byLabel: true } }, }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true } } }, }), @@ -68,7 +68,7 @@ describe("GroupHiding", () => { createTestProcessedGroupingNode({ children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true } } }, }), @@ -77,7 +77,7 @@ describe("GroupHiding", () => { createTestProcessedGroupingNode({ children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, label: "2", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true } } }, }), @@ -96,7 +96,7 @@ describe("GroupHiding", () => { createTestProcessedGroupingNode({ children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true } } }, }), @@ -105,7 +105,7 @@ describe("GroupHiding", () => { ], ungrouped: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, label: "2", }), ], @@ -120,7 +120,7 @@ describe("GroupHiding", () => { createTestProcessedGroupingNode({ children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true } } }, }), @@ -141,7 +141,7 @@ describe("GroupHiding", () => { createTestProcessedGroupingNode({ children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: { grouping: { byClass: { hideIfOneGroupedNode: true } } }, }), @@ -164,12 +164,12 @@ describe("GroupHiding", () => { createTestProcessedGroupingNode({ children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfOneGroupedNode: true } } }, }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfOneGroupedNode: true } } }, }), @@ -191,7 +191,7 @@ describe("GroupHiding", () => { createTestProcessedGroupingNode({ children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", }), ], @@ -207,7 +207,7 @@ describe("GroupHiding", () => { grouped: [], ungrouped: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true, hideIfOneGroupedNode: true } } }, }), @@ -223,7 +223,7 @@ describe("GroupHiding", () => { createTestProcessedGroupingNode({ children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true, hideIfOneGroupedNode: true } } }, }), @@ -249,12 +249,12 @@ describe("GroupHiding", () => { createTestProcessedGroupingNode({ children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true, hideIfOneGroupedNode: true } } }, }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true, hideIfOneGroupedNode: true } } }, }), @@ -263,7 +263,7 @@ describe("GroupHiding", () => { ], ungrouped: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x3" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x3" }] }, label: "2", }), ], @@ -278,12 +278,12 @@ describe("GroupHiding", () => { createTestProcessedGroupingNode({ children: [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true, hideIfOneGroupedNode: true } } }, }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true, hideIfOneGroupedNode: true } } }, }), @@ -302,13 +302,13 @@ describe("GroupHiding", () => { it("hides if group has one child node", async () => { const childNode = createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true, hideIfOneGroupedNode: true } } }, }); const groupingNode = createTestProcessedGroupingNode({ children: [childNode] }); const ungroupedSiblingNode = createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, label: "1", processingParams: { grouping: { byLabel: { hideIfNoSiblings: true, hideIfOneGroupedNode: true } } }, }); diff --git a/packages/hierarchies/src/test/imodel/operators/grouping/LabelGrouping.test.ts b/packages/hierarchies/src/test/imodel/operators/grouping/LabelGrouping.test.ts index 11819bfa9..768c923cc 100644 --- a/packages/hierarchies/src/test/imodel/operators/grouping/LabelGrouping.test.ts +++ b/packages/hierarchies/src/test/imodel/operators/grouping/LabelGrouping.test.ts @@ -18,7 +18,7 @@ describe("LabelGrouping", () => { it("groups one node", async () => { const nodes = [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, parentKeys: [createTestGenericNodeKey({ id: "x" })], label: "1", processingParams: { grouping: { byLabel: true } }, @@ -46,7 +46,7 @@ describe("LabelGrouping", () => { it("groups one node when 'action' is set to 'group'", async () => { const nodes = [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, parentKeys: [createTestGenericNodeKey({ id: "x" })], label: "1", processingParams: { grouping: { byLabel: { action: "group" } } }, @@ -74,7 +74,7 @@ describe("LabelGrouping", () => { it("groups one node when 'byLabel' is set to empty object", async () => { const nodes = [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, parentKeys: [createTestGenericNodeKey({ id: "x" })], label: "1", processingParams: { grouping: { byLabel: {} } }, @@ -102,13 +102,13 @@ describe("LabelGrouping", () => { it("creates separate groups for nodes with same labels and different groupIds", async () => { const nodes = [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, parentKeys: [createTestGenericNodeKey({ id: "x" })], label: "1", processingParams: { grouping: { byLabel: { groupId: "groupId1" } } }, }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, parentKeys: [createTestGenericNodeKey({ id: "x" })], label: "1", processingParams: { grouping: { byLabel: { groupId: "groupId2" } } }, @@ -141,13 +141,13 @@ describe("LabelGrouping", () => { it("groups multiple nodes with same groupIds and labels", async () => { const nodes = [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, parentKeys: [createTestGenericNodeKey({ id: "x" })], label: "1", processingParams: { grouping: { byLabel: { groupId: "groupId1" } } }, }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, parentKeys: [createTestGenericNodeKey({ id: "x" })], label: "1", processingParams: { grouping: { byLabel: { groupId: "groupId1" } } }, @@ -175,13 +175,13 @@ describe("LabelGrouping", () => { it("groups multiple nodes", async () => { const nodes = [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, parentKeys: [createTestGenericNodeKey({ id: "x" })], label: "1", processingParams: { grouping: { byLabel: true } }, }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, parentKeys: [createTestGenericNodeKey({ id: "x" })], label: "1", processingParams: { grouping: { byLabel: true } }, @@ -209,13 +209,13 @@ describe("LabelGrouping", () => { it("creates different groups for differently labeled nodes", async () => { const nodes = [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, parentKeys: [createTestGenericNodeKey({ id: "x" })], label: "1", processingParams: { grouping: { byLabel: true } }, }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x2" }] }, parentKeys: [createTestGenericNodeKey({ id: "x" })], label: "2", processingParams: { grouping: { byLabel: true } }, @@ -254,7 +254,7 @@ describe("LabelGrouping", () => { it("doesn't group nodes with byLabel set to false", async () => { const nodes = [ createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "TestSchema:A", id: "0x1" }] }, + key: { type: "instances", instanceKeys: [{ className: "TestSchema.A", id: "0x1" }] }, label: "1", }), ]; diff --git a/packages/hierarchies/src/test/imodel/operators/grouping/PropertiesGrouping.test.ts b/packages/hierarchies/src/test/imodel/operators/grouping/PropertiesGrouping.test.ts index 9c6d9fa7d..2a3592bd4 100644 --- a/packages/hierarchies/src/test/imodel/operators/grouping/PropertiesGrouping.test.ts +++ b/packages/hierarchies/src/test/imodel/operators/grouping/PropertiesGrouping.test.ts @@ -1107,11 +1107,11 @@ describe("PropertiesGrouping", () => { }, }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "testSchema:Class", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "testSchema.Class", id: "0x2" }] }, processingParams: { grouping: { byProperties: { - propertiesClassName: "testSchema:Class", + propertiesClassName: "testSchema.Class", /* cspell:disable-next-line */ propertyGroups: [{ propertyName: "propertyname", propertyValue: "PropertyValue" }], }, @@ -1566,12 +1566,12 @@ describe("PropertiesGrouping", () => { }, }), createTestProcessedInstanceNode({ - key: { type: "instances", instanceKeys: [{ className: "testSchema:Class2", id: "0x2" }] }, + key: { type: "instances", instanceKeys: [{ className: "testSchema.Class2", id: "0x2" }] }, processingParams: { grouping: { byProperties: { /* cspell:disable-next-line */ - propertiesClassName: "testschema:class2", + propertiesClassName: "testschema.class2", createGroupForOutOfRangeValues: true, propertyGroups: [ /* cspell:disable-next-line */ @@ -1898,7 +1898,7 @@ describe("PropertiesGrouping", () => { processingParams: { grouping: { byProperties: { - propertiesClassName: "testSchema:Class", + propertiesClassName: "testSchema.Class", propertyGroups: [ { /* cspell:disable-next-line */ diff --git a/packages/models-tree/src/models-tree/ModelsTreeDefinition.ts b/packages/models-tree/src/models-tree/ModelsTreeDefinition.ts index 4cdf54e33..014bca19d 100644 --- a/packages/models-tree/src/models-tree/ModelsTreeDefinition.ts +++ b/packages/models-tree/src/models-tree/ModelsTreeDefinition.ts @@ -69,7 +69,7 @@ export interface ModelsTreeHierarchyConfiguration { /** Should element nodes be grouped by class. Defaults to `enable`. */ elementClassGrouping: "enable" | "enableWithCounts" | "disable"; /** Full class name of a `GeometricElement3d` sub-class that should be used to load element nodes. Defaults to `BisCore.GeometricElement3d` */ - elementClassSpecification: EC.FullClassName; + elementClassSpecification: EC.FullClassNameDotNotation; /** Should models without elements be shown. Defaults to `false` */ showEmptyModels: boolean; } @@ -685,7 +685,7 @@ function parseQueryRow( row: ECSqlQueryRow, groupInfos: ElementsGroupInfo[], separator: string, - elementClassName: EC.FullClassName, + elementClassName: EC.FullClassNameDotNotation, ) { const rowElements: string[] = row[1].split(separator); const path = new Array(); diff --git a/packages/shared/api/presentation-shared.api.md b/packages/shared/api/presentation-shared.api.md index 138941619..823122fac 100644 --- a/packages/shared/api/presentation-shared.api.md +++ b/packages/shared/api/presentation-shared.api.md @@ -34,13 +34,10 @@ interface ClassBasedInstanceLabelSelectClauseFactoryProps { // @public interface ClassBasedLabelSelectClause { - className: EC.FullClassName; + className: EC.FullClassNameDotNotation; clause: (props: CreateInstanceLabelSelectClauseProps) => Promise; } -// @public -export function compareFullClassNames(lhs: EC.FullClassName, rhs: EC.FullClassName): number; - // @public export type ConcatenatedValue = ConcatenatedValuePart[]; @@ -79,7 +76,7 @@ export function createCachingECClassHierarchyInspector(props: { export function createClassBasedInstanceLabelSelectClauseFactory(props: ClassBasedInstanceLabelSelectClauseFactoryProps): IInstanceLabelSelectClauseFactory; // @public -function createClassSelector(fullClassName: EC.FullClassName): string; +function createClassSelector(fullClassName: EC.FullClassNameDotNotation): string; // @public function createConcatenatedValueJsonSelector(selectors: TypedValueSelectClauseProps[], checkSelector?: string): string; @@ -104,7 +101,7 @@ function createInstanceKeySelector(props: { // @public interface CreateInstanceLabelSelectClauseProps { classAlias: string; - className?: EC.FullClassName; + className?: EC.FullClassNameDotNotation; selectorsConcatenator?: (selectors: TypedValueSelectClauseProps[], checkSelector?: string) => string; } @@ -120,7 +117,7 @@ function createNullableSelector(props: { // @public function createPrimitivePropertyValueSelectorProps(input: { schemaProvider: ECSchemaProvider; - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; propertyClassAlias: string; propertyName: string; }): Promise; @@ -186,13 +183,13 @@ export namespace EC { // (undocumented) [propName: string]: any; // (undocumented) - className: FullClassName; + className: FullClassNameDotNotation; } export interface CustomAttributeSet { // (undocumented) - [Symbol.iterator]: () => IterableIterator<[FullClassName, CustomAttribute]>; + [Symbol.iterator]: () => IterableIterator<[FullClassNameDotNotation, CustomAttribute]>; // (undocumented) - get(className: FullClassName): CustomAttribute | undefined; + get(className: FullClassNameDotNotation): CustomAttribute | undefined; } export interface EntityClass extends Class { getMixins(): Promise; @@ -306,7 +303,7 @@ export namespace EC { // (undocumented) description?: string; // (undocumented) - fullName: FullClassName; + fullName: FullClassNameDotNotation; // (undocumented) label?: string; // (undocumented) @@ -330,7 +327,7 @@ export namespace EC { // @public export interface ECClassHierarchyInspector { // (undocumented) - classDerivesFrom(derivedClassFullName: EC.FullClassName, candidateBaseClassFullName: EC.FullClassName): Promise | boolean; + classDerivesFrom(derivedClassFullName: EC.FullClassNameDotNotation, candidateBaseClassFullName: EC.FullClassNameDotNotation): Promise | boolean; } // @public @@ -462,7 +459,7 @@ export function formatConcatenatedValue(props: { }): Promise; // @public -export function getClass(schemaProvider: ECSchemaProvider, fullClassName: EC.FullClassName): Promise; +export function getClass(schemaProvider: ECSchemaProvider, fullClassName: EC.FullClassNameDotNotation): Promise; // @public export interface IInstanceLabelSelectClauseFactory { @@ -491,7 +488,7 @@ interface IModelInstanceLabelSelectClauseFactoryProps { // @public export interface InstanceKey { - className: EC.FullClassName; + className: EC.FullClassNameDotNotation; id: Id64String; } @@ -522,7 +519,7 @@ interface JoinRelationshipPathStep extends RelationshipPathStep { // @public interface JoinTargetClass { // (undocumented) - className: EC.FullClassName; + className: EC.FullClassNameDotNotation; // (undocumented) kind: "class"; } @@ -539,7 +536,7 @@ interface JoinTargetRelationshipSelect { // (undocumented) relationshipAlias: string; // (undocumented) - relationshipClassName: EC.FullClassName; + relationshipClassName: EC.FullClassNameDotNotation; } // @public @@ -555,7 +552,7 @@ export type LogLevel = "error" | "warning" | "info" | "trace"; export interface NavigationValueDescriptor { // (undocumented) kind: "navigation"; - targetClassName: EC.FullClassName; + targetClassName: EC.FullClassNameDotNotation; } // @public @@ -688,10 +685,10 @@ interface RelationshipPathStep { relationshipAlias?: string; bindings?: Record; }; - relationshipName: EC.FullClassName; + relationshipName: EC.FullClassNameDotNotation; relationshipReverse?: boolean; - sourceClassName: EC.FullClassName; - targetClassName: EC.FullClassName; + sourceClassName: EC.FullClassNameDotNotation; + targetClassName: EC.FullClassNameDotNotation; } // @public diff --git a/packages/shared/api/presentation-shared.exports.csv b/packages/shared/api/presentation-shared.exports.csv index 0ac77c554..55271a5f2 100644 --- a/packages/shared/api/presentation-shared.exports.csv +++ b/packages/shared/api/presentation-shared.exports.csv @@ -3,7 +3,6 @@ Release Tag;API Item Type;API Item Name public;type;ArrayElement public;type;ArrayValue public;interface;ArrayValueDescriptor -public;function;compareFullClassNames public;type;ConcatenatedValue public;namespace;ConcatenatedValue public;type;ConcatenatedValuePart diff --git a/packages/shared/src/presentation-shared.ts b/packages/shared/src/presentation-shared.ts index bfb993f13..d42c084e0 100644 --- a/packages/shared/src/presentation-shared.ts +++ b/packages/shared/src/presentation-shared.ts @@ -35,7 +35,6 @@ export { createCachingECClassHierarchyInspector, getClass } from "./shared/Metad export { createMainThreadReleaseOnTimePassedHandler, julianToDateTime, - compareFullClassNames, normalizeFullClassName, parseFullClassName, releaseMainThread, diff --git a/packages/shared/src/shared/InstanceLabelSelectClauseFactory.ts b/packages/shared/src/shared/InstanceLabelSelectClauseFactory.ts index 8e4dc0bb9..dbeb206c6 100644 --- a/packages/shared/src/shared/InstanceLabelSelectClauseFactory.ts +++ b/packages/shared/src/shared/InstanceLabelSelectClauseFactory.ts @@ -33,7 +33,7 @@ export interface CreateInstanceLabelSelectClauseProps { * implementations may be able to create a more efficient select clause (e.g. drop some pieces of clause * that don't apply for given class). */ - className?: EC.FullClassName; + className?: EC.FullClassNameDotNotation; /** * An optional function for concatenating multiple `TypedValueSelectClauseProps`. Selectors' concatenation diff --git a/packages/shared/src/shared/Metadata.ts b/packages/shared/src/shared/Metadata.ts index 62f72b9a9..60f496248 100644 --- a/packages/shared/src/shared/Metadata.ts +++ b/packages/shared/src/shared/Metadata.ts @@ -25,8 +25,8 @@ export interface ECSchemaProvider { */ export interface ECClassHierarchyInspector { classDerivesFrom( - derivedClassFullName: EC.FullClassName, - candidateBaseClassFullName: EC.FullClassName, + derivedClassFullName: EC.FullClassNameDotNotation, + candidateBaseClassFullName: EC.FullClassNameDotNotation, ): Promise | boolean; } @@ -41,13 +41,13 @@ export function createCachingECClassHierarchyInspector(props: { cacheSize?: number; }): ECClassHierarchyInspector { const map = new LRUMap | boolean>(props.cacheSize ?? 0); - function createCacheKey(derivedClassName: EC.FullClassName, baseClassName: EC.FullClassName) { + function createCacheKey(derivedClassName: EC.FullClassNameDotNotation, baseClassName: EC.FullClassNameDotNotation) { return `${derivedClassName}/${baseClassName}`; } return { classDerivesFrom( - derivedClassFullName: EC.FullClassName, - candidateBaseClassFullName: EC.FullClassName, + derivedClassFullName: EC.FullClassNameDotNotation, + candidateBaseClassFullName: EC.FullClassNameDotNotation, ): Promise | boolean { const cacheKey = createCacheKey(derivedClassFullName, candidateBaseClassFullName); let result = map.get(cacheKey); @@ -111,7 +111,7 @@ export namespace EC { */ export interface SchemaItem { schema: Schema; - fullName: FullClassName; + fullName: FullClassNameDotNotation; name: string; label?: string; description?: string; @@ -352,8 +352,8 @@ export namespace EC { * @public */ export interface CustomAttributeSet { - [Symbol.iterator]: () => IterableIterator<[FullClassName, CustomAttribute]>; - get(className: FullClassName): CustomAttribute | undefined; + [Symbol.iterator]: () => IterableIterator<[FullClassNameDotNotation, CustomAttribute]>; + get(className: FullClassNameDotNotation): CustomAttribute | undefined; } /** @@ -362,7 +362,7 @@ export namespace EC { * @public */ export interface CustomAttribute { - className: FullClassName; + className: FullClassNameDotNotation; [propName: string]: any; } } @@ -515,7 +515,7 @@ export interface NavigationValueDescriptor { * Full name of the relationship's target-constraint class the navigation points at * (the referenced instance's class, or its base constraint class). */ - targetClassName: EC.FullClassName; + targetClassName: EC.FullClassNameDotNotation; } /** @@ -524,11 +524,11 @@ export interface NavigationValueDescriptor { */ export interface RelationshipPathStep { /** Full name of the source ECClass */ - sourceClassName: EC.FullClassName; + sourceClassName: EC.FullClassNameDotNotation; /** Full name of the target ECClass */ - targetClassName: EC.FullClassName; + targetClassName: EC.FullClassNameDotNotation; /** Full name of the ECRelationshipClass */ - relationshipName: EC.FullClassName; + relationshipName: EC.FullClassNameDotNotation; /** * Indicates that the relationship direction be reversed. This should be set to `true` when step direction * doesn't match relationship direction, e.g. relationship is from source `A` to target `B` and the step @@ -602,7 +602,10 @@ export type RelationshipPath { +export async function getClass( + schemaProvider: ECSchemaProvider, + fullClassName: EC.FullClassNameDotNotation, +): Promise { const { schemaName, className } = parseFullClassName(fullClassName); const schema = await schemaProvider.getSchema(schemaName); if (!schema) { diff --git a/packages/shared/src/shared/Utils.ts b/packages/shared/src/shared/Utils.ts index 38379cba7..636bbbaf0 100644 --- a/packages/shared/src/shared/Utils.ts +++ b/packages/shared/src/shared/Utils.ts @@ -48,22 +48,6 @@ export function normalizeFullClassName(fullClassName: string): EC.FullClassNameD return `${schemaName}.${className}`; } -/** - * An utility that compares two full class names in a case insensitive way, ignoring different - * supported schema-class name separators (`.` or `:`). - * @public - */ -export function compareFullClassNames(lhs: EC.FullClassName, rhs: EC.FullClassName): number { - const parsed = { lhs: parseFullClassName(lhs), rhs: parseFullClassName(rhs) }; - const schemaCompare = parsed.lhs.schemaName - .toLocaleLowerCase() - .localeCompare(parsed.rhs.schemaName.toLocaleLowerCase()); - if (schemaCompare !== 0) { - return schemaCompare; - } - return parsed.lhs.className.toLocaleLowerCase().localeCompare(parsed.rhs.className.toLocaleLowerCase()); -} - /** * An utility to remove all extra whitespace from a given string. * @public diff --git a/packages/shared/src/shared/Values.ts b/packages/shared/src/shared/Values.ts index 41e495663..de8587714 100644 --- a/packages/shared/src/shared/Values.ts +++ b/packages/shared/src/shared/Values.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { compareStrings, Id64 } from "@itwin/core-bentley"; -import { normalizeFullClassName } from "./Utils.js"; import type { Id64String } from "@itwin/core-bentley"; import type { EC, PrimitiveValueType } from "./Metadata.js"; @@ -15,7 +14,7 @@ import type { EC, PrimitiveValueType } from "./Metadata.js"; */ export interface InstanceKey { /** Full class name in format `SchemaName.ClassName` */ - className: EC.FullClassName; + className: EC.FullClassNameDotNotation; /** ECInstance ID */ id: Id64String; } @@ -37,10 +36,7 @@ export namespace InstanceKey { *- `positive value` if lhs key is more than rhs key */ export function compare(lhs: InstanceKey, rhs: InstanceKey): number { - const classNameCompareResult = compareStrings( - normalizeFullClassName(lhs.className).toLocaleLowerCase(), - normalizeFullClassName(rhs.className).toLocaleLowerCase(), - ); + const classNameCompareResult = compareStrings(lhs.className.toLocaleLowerCase(), rhs.className.toLocaleLowerCase()); if (classNameCompareResult !== 0) { return classNameCompareResult; } diff --git a/packages/shared/src/shared/ecsql-snippets/ECSqlJoinSnippets.ts b/packages/shared/src/shared/ecsql-snippets/ECSqlJoinSnippets.ts index 6052288b1..8bc63fd69 100644 --- a/packages/shared/src/shared/ecsql-snippets/ECSqlJoinSnippets.ts +++ b/packages/shared/src/shared/ecsql-snippets/ECSqlJoinSnippets.ts @@ -41,7 +41,7 @@ export interface CreateRelationshipPathJoinClauseProps { */ interface JoinTargetClass { kind: "class"; - className: EC.FullClassName; + className: EC.FullClassNameDotNotation; } /** @@ -52,7 +52,7 @@ interface JoinTargetClass { */ interface JoinTargetRelationshipSelect { kind: "relationship-select"; - relationshipClassName: EC.FullClassName; + relationshipClassName: EC.FullClassNameDotNotation; relationshipAlias: string; /** The class inner-joined inside the subquery, its alias, and the inner `ON` condition. */ innerTarget: JoinTargetClass; diff --git a/packages/shared/src/shared/ecsql-snippets/ECSqlValueSelectorSnippets.ts b/packages/shared/src/shared/ecsql-snippets/ECSqlValueSelectorSnippets.ts index 5be295509..bd0fd4e83 100644 --- a/packages/shared/src/shared/ecsql-snippets/ECSqlValueSelectorSnippets.ts +++ b/packages/shared/src/shared/ecsql-snippets/ECSqlValueSelectorSnippets.ts @@ -60,7 +60,7 @@ export async function createPrimitivePropertyValueSelectorProps({ /** Access to schema information. */ schemaProvider: ECSchemaProvider; /** Full class name of the property. Format: `SchemaName.ClassName`. */ - propertyClassName: EC.FullClassName; + propertyClassName: EC.FullClassNameDotNotation; /** Query alias of the class that contains the property. */ propertyClassAlias: string; /** Name of the property to create `TypedPrimitiveValue` for. */ @@ -172,13 +172,13 @@ export function createRawPrimitiveValueSelector(value: PrimitiveValue | undefine } /** - * Converts a full class name (`SchemaName.ClassName` or `SchemaName:ClassName`) into a + * Converts a full class name (`SchemaName.ClassName`) into a * bracket-quoted ECSQL class selector: `[SchemaName].[ClassName]`. * * @throws Error if the provided full class name is not valid. * @public */ -export function createClassSelector(fullClassName: EC.FullClassName): string { +export function createClassSelector(fullClassName: EC.FullClassNameDotNotation): string { const { schemaName, className } = parseFullClassName(fullClassName); return `[${schemaName}].[${className}]`; } diff --git a/packages/shared/src/shared/instance-label-factory-impls/ClassBasedInstanceLabelSelectClauseFactory.ts b/packages/shared/src/shared/instance-label-factory-impls/ClassBasedInstanceLabelSelectClauseFactory.ts index f5e4babcb..2bb9737cc 100644 --- a/packages/shared/src/shared/instance-label-factory-impls/ClassBasedInstanceLabelSelectClauseFactory.ts +++ b/packages/shared/src/shared/instance-label-factory-impls/ClassBasedInstanceLabelSelectClauseFactory.ts @@ -18,7 +18,7 @@ import type { EC, ECClassHierarchyInspector } from "../Metadata.js"; */ export interface ClassBasedLabelSelectClause { /** Full class name */ - className: EC.FullClassName; + className: EC.FullClassNameDotNotation; /** A factory method to create an instance label select clause */ clause: (props: CreateInstanceLabelSelectClauseProps) => Promise; } @@ -56,7 +56,7 @@ export function createClassBasedInstanceLabelSelectClauseFactory( ): IInstanceLabelSelectClauseFactory { const { classHierarchyInspector, clauses: labelClausesByClass } = props; const defaultClauseFactory = props.defaultClauseFactory ?? createDefaultInstanceLabelSelectClauseFactory(); - async function getLabelClausesForClass(queryClassName: EC.FullClassName) { + async function getLabelClausesForClass(queryClassName: EC.FullClassNameDotNotation) { const matchingLabelClauses = await Promise.all( labelClausesByClass.map(async (entry) => { if (await classHierarchyInspector.classDerivesFrom(entry.className, queryClassName)) { diff --git a/packages/shared/src/shared/instance-label-factory-impls/IModelInstanceLabelSelectClauseFactory.ts b/packages/shared/src/shared/instance-label-factory-impls/IModelInstanceLabelSelectClauseFactory.ts index d3161f368..e6fb355f9 100644 --- a/packages/shared/src/shared/instance-label-factory-impls/IModelInstanceLabelSelectClauseFactory.ts +++ b/packages/shared/src/shared/instance-label-factory-impls/IModelInstanceLabelSelectClauseFactory.ts @@ -182,7 +182,7 @@ function normalizeInstanceLabelOverrideRule(rule: RuleBase): InstanceLabelOverri } type ExtendedCreateInstanceLabelSelectClauseProps = CreateInstanceLabelSelectClauseProps & { - visitedClasses?: Set; + visitedClasses?: Set; depth?: number; }; @@ -190,9 +190,9 @@ interface CompileContext { classAlias: string; selectorsConcatenator: NonNullable; labelFactory: IInstanceLabelSelectClauseFactory; - visitedClasses: Set; + visitedClasses: Set; schemaProvider: ECSchemaProvider; - ruleClassName: EC.FullClassName; + ruleClassName: EC.FullClassNameDotNotation; depth: number; } @@ -275,11 +275,11 @@ async function compileValueSpec(spec: InstanceLabelOverrideValueSpecification, c } const lastStep = steps[steps.length - 1]; - let targetClassName: EC.FullClassName; + let targetClassName: EC.FullClassNameDotNotation; if (lastStep.targetClass) { targetClassName = `${lastStep.targetClass.schemaName}.${lastStep.targetClass.className}`; } else { - const relName: EC.FullClassName = `${lastStep.relationship.schemaName}.${lastStep.relationship.className}`; + const relName: EC.FullClassNameDotNotation = `${lastStep.relationship.schemaName}.${lastStep.relationship.className}`; const relClass = await getClass(ctx.schemaProvider, relName); if (!relClass.isRelationshipClass()) { throw new Error(`Class ${relName} is not a relationship class`); @@ -368,7 +368,7 @@ function subqueryAlias(prefix: string, depth: number, stepIndex?: number): strin async function toJoinRelationshipPath(props: { schemaProvider: ECSchemaProvider; steps: RelationshipStepSpecification[]; - sourceClassName: EC.FullClassName; + sourceClassName: EC.FullClassNameDotNotation; sourceAlias: string; finalTargetAlias: string; depth: number; @@ -376,15 +376,15 @@ async function toJoinRelationshipPath(props: { assert(props.steps.length > 0); const result: JoinRelationshipPathStep[] = []; - let currentSourceClassName: EC.FullClassName = props.sourceClassName; + let currentSourceClassName = props.sourceClassName; let currentSourceAlias = props.sourceAlias; for (let i = 0; i < props.steps.length; i++) { const step = props.steps[i]; - const relationshipName: EC.FullClassName = `${step.relationship.schemaName}.${step.relationship.className}`; + const relationshipName: EC.FullClassNameDotNotation = `${step.relationship.schemaName}.${step.relationship.className}`; const relationshipReverse = step.direction === "Backward"; - let targetClassName: EC.FullClassName; + let targetClassName: EC.FullClassNameDotNotation; if (step.targetClass) { targetClassName = `${step.targetClass.schemaName}.${step.targetClass.className}`; } else { @@ -424,7 +424,7 @@ async function toJoinRelationshipPath(props: { async function buildRelationshipPathSubquery(props: { schemaProvider: ECSchemaProvider; steps: RelationshipStepSpecification[]; - ruleClassName: EC.FullClassName; + ruleClassName: EC.FullClassNameDotNotation; classAlias: string; selectExpression: string; finalTargetAlias: string; diff --git a/packages/shared/src/test/MetadataProviderStub.ts b/packages/shared/src/test/MetadataProviderStub.ts index 75d50b9f2..ac773ed78 100644 --- a/packages/shared/src/test/MetadataProviderStub.ts +++ b/packages/shared/src/test/MetadataProviderStub.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { vi } from "vitest"; -import { parseFullClassName } from "../shared/Utils.js"; import type { Mock } from "vitest"; import type { EC } from "../shared/Metadata.js"; @@ -14,7 +13,7 @@ export interface StubClassFuncProps { className: string; classLabel?: string; properties?: EC.Property[]; - is?: (fullClassName: EC.FullClassName) => Promise; + is?: (fullClassName: EC.FullClassNameDotNotation) => Promise; } export interface StubRelationshipClassFuncProps extends StubClassFuncProps { source?: EC.RelationshipConstraint; @@ -72,11 +71,7 @@ export function createECSchemaProviderStub() { if (typeof targetClassOrClassName === "string") { return props.is(`${schemaName!}.${targetClassOrClassName}`); } - // need this just to make sure `.` is used for separating schema and class names - const { schemaName: parsedSchemaName, className: parsedClassName } = parseFullClassName( - targetClassOrClassName.fullName, - ); - return props.is(`${parsedSchemaName}.${parsedClassName}`); + return props.is(targetClassOrClassName.fullName); }), isEntityClass: () => false, isRelationshipClass: () => false, diff --git a/packages/shared/src/test/Utils.test.ts b/packages/shared/src/test/Utils.test.ts index a04eefd7f..e9caa3688 100644 --- a/packages/shared/src/test/Utils.test.ts +++ b/packages/shared/src/test/Utils.test.ts @@ -5,7 +5,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { - compareFullClassNames, createMainThreadReleaseOnTimePassedHandler, normalizeFullClassName, parseFullClassName, @@ -45,32 +44,6 @@ describe("normalizeFullClassName", () => { }); }); -describe("compareFullClassNames", () => { - it("detects differences", () => { - const differentSchemaCompare = compareFullClassNames("schema.class", "schema2.class"); - const differentSchemaCompareInverted = compareFullClassNames("schema2.class", "schema.class"); - expect(differentSchemaCompare).not.toBe(0); - expect(differentSchemaCompareInverted).not.toBe(0); - expect(differentSchemaCompareInverted).toBe(differentSchemaCompare * -1); - - const differentClassCompare = compareFullClassNames("schema.class", "schema.class2"); - const differentClassCompareInverted = compareFullClassNames("schema.class2", "schema.class"); - expect(differentClassCompare).not.toBe(0); - expect(differentClassCompareInverted).not.toBe(0); - expect(differentClassCompareInverted).toBe(differentClassCompare * -1); - }); - - it("compares full class names in a case-insensitive way", () => { - expect(compareFullClassNames("schema:class", "SCHEMA:CLASS")).toBe(0); - expect(compareFullClassNames("Schema.Class", "schema.class")).toBe(0); - }); - - it("compares full class names, ignoring the separator", () => { - expect(compareFullClassNames("schema:class", "schema.class")).toBe(0); - expect(compareFullClassNames("schema.class", "schema:class")).toBe(0); - }); -}); - describe("trimWhitespace", () => { it("trims whitespace from a string", () => { expect(trimWhitespace(" hello ")).toBe("hello"); diff --git a/packages/shared/src/test/Values.test.ts b/packages/shared/src/test/Values.test.ts index 2e13ea64a..6a238f665 100644 --- a/packages/shared/src/test/Values.test.ts +++ b/packages/shared/src/test/Values.test.ts @@ -17,7 +17,7 @@ describe("InstanceKey", () => { describe("compare", () => { it("compares two keys", () => { expect(InstanceKey.compare({ className: "s.a", id: "1" }, { className: "s.a", id: "1" })).toBe(0); - expect(InstanceKey.compare({ className: "s.a", id: "1" }, { className: "S:A", id: "1" })).toBe(0); + expect(InstanceKey.compare({ className: "s.a", id: "1" }, { className: "S.A", id: "1" })).toBe(0); expect(InstanceKey.compare({ className: "s.a", id: "1" }, { className: "s.b", id: "2" })).toBeLessThan(0); expect(InstanceKey.compare({ className: "s.a", id: "1" }, { className: "s.b", id: "1" })).toBeLessThan(0); expect(InstanceKey.compare({ className: "s.a", id: "1" }, { className: "s.a", id: "2" })).toBeLessThan(0); diff --git a/packages/shared/src/test/ecsql-snippets/ECSqlValueSelectorSnippets.test.ts b/packages/shared/src/test/ecsql-snippets/ECSqlValueSelectorSnippets.test.ts index 56844e06a..587a4677f 100644 --- a/packages/shared/src/test/ecsql-snippets/ECSqlValueSelectorSnippets.test.ts +++ b/packages/shared/src/test/ecsql-snippets/ECSqlValueSelectorSnippets.test.ts @@ -349,15 +349,9 @@ describe("createClassSelector", () => { expect(createClassSelector("TestSchema.TestClass")).to.equal("[TestSchema].[TestClass]"); }); - it("bracket-quotes a colon-separated full class name", () => { - expect(createClassSelector("TestSchema:TestClass")).to.equal("[TestSchema].[TestClass]"); - }); - it("throws on invalid full class name", () => { expect(() => createClassSelector(".c")).to.throw(); - expect(() => createClassSelector(":c")).to.throw(); expect(() => createClassSelector("s.")).to.throw(); - expect(() => createClassSelector("s:")).to.throw(); }); }); diff --git a/packages/test-utilities/src/test-utilities/IModelUtils.ts b/packages/test-utilities/src/test-utilities/IModelUtils.ts index 615a89ae6..156883348 100644 --- a/packages/test-utilities/src/test-utilities/IModelUtils.ts +++ b/packages/test-utilities/src/test-utilities/IModelUtils.ts @@ -29,22 +29,25 @@ import type { // cspell:words ecdbmap -type FullClassNameSeparator = "." | ":"; -type FullClassName = `${string}${FullClassNameSeparator}${string}`; +type FullClassName = `${string}.${string}`; -export interface BaseInstanceInsertProps { +interface InstanceKey { + className: FullClassName; + id: Id64String; +} + +interface BaseInstanceInsertProps { txn: EditTxn; classFullName?: FullClassName; - fullClassNameSeparator?: FullClassNameSeparator; } export function insertSubject( props: BaseInstanceInsertProps & { codeValue: string; parentId?: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, codeValue, parentId, ...subjectProps } = props; - const defaultClassName: FullClassName = `BisCore${props.fullClassNameSeparator ?? "."}Subject`; + const defaultClassName = `BisCore.Subject`; const className = classFullName ?? defaultClassName; const id = txn.insertElement({ classFullName: className, @@ -62,7 +65,7 @@ export function insertSubject( export function insertPhysicalModelWithPartition( props: BaseInstanceInsertProps & { codeValue: string; partitionParentId?: Id64String }, -) { +): InstanceKey { const { codeValue, partitionParentId, ...baseProps } = props; const partitionKey = insertPhysicalPartition({ ...baseProps, @@ -76,9 +79,9 @@ export function insertPhysicalPartition( props: BaseInstanceInsertProps & { codeValue: string; parentId: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, codeValue, parentId, ...partitionProps } = props; - const defaultModelClassName: FullClassName = `BisCore${props.fullClassNameSeparator ?? "."}PhysicalPartition`; + const defaultModelClassName = `BisCore.PhysicalPartition`; const className = classFullName ?? defaultModelClassName; const partitionId = txn.insertElement({ classFullName: className, @@ -88,7 +91,7 @@ export function insertPhysicalPartition( scope: parentId, value: codeValue, }), - parent: { id: parentId, relClassName: `BisCore${props.fullClassNameSeparator ?? "."}SubjectOwnsPartitionElements` }, + parent: { id: parentId, relClassName: `BisCore.SubjectOwnsPartitionElements` }, ...partitionProps, }); return { className, id: partitionId }; @@ -98,9 +101,9 @@ export function insertPhysicalSubModel( props: BaseInstanceInsertProps & { modeledElementId: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, modeledElementId, ...modelProps } = props; - const defaultModelClassName: FullClassName = `BisCore${props.fullClassNameSeparator ?? "."}PhysicalModel`; + const defaultModelClassName = `BisCore.PhysicalModel`; const className = classFullName ?? defaultModelClassName; const modelId = txn.insertModel({ classFullName: className, @@ -112,7 +115,7 @@ export function insertPhysicalSubModel( export function insertDrawingModelWithPartition( props: BaseInstanceInsertProps & { codeValue: string; partitionParentId?: Id64String }, -) { +): InstanceKey { const { codeValue, partitionParentId, ...baseProps } = props; const partitionKey = insertDrawingPartition({ ...baseProps, @@ -126,9 +129,9 @@ export function insertDrawingPartition( props: BaseInstanceInsertProps & { codeValue: string; parentId: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, codeValue, parentId, ...partitionProps } = props; - const defaultModelClassName: FullClassName = `BisCore${props.fullClassNameSeparator ?? "."}Drawing`; + const defaultModelClassName = `BisCore.Drawing`; const className = classFullName ?? defaultModelClassName; const partitionId = txn.insertElement({ classFullName: className, @@ -138,7 +141,7 @@ export function insertDrawingPartition( scope: parentId, value: codeValue, }), - parent: { id: parentId, relClassName: `BisCore${props.fullClassNameSeparator ?? "."}SubjectOwnsPartitionElements` }, + parent: { id: parentId, relClassName: `BisCore.SubjectOwnsPartitionElements` }, ...partitionProps, }); return { className, id: partitionId }; @@ -148,9 +151,9 @@ export function insertDrawingSubModel( props: BaseInstanceInsertProps & { modeledElementId: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, modeledElementId, ...modelProps } = props; - const defaultModelClassName: FullClassName = `BisCore${props.fullClassNameSeparator ?? "."}DrawingModel`; + const defaultModelClassName = `BisCore.DrawingModel`; const className = classFullName ?? defaultModelClassName; const modelId = txn.insertModel({ classFullName: className, @@ -164,9 +167,9 @@ export function insertSpatialCategory( props: BaseInstanceInsertProps & { codeValue: string; modelId?: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, modelId, codeValue, ...categoryProps } = props; - const defaultClassName: FullClassName = `BisCore${props.fullClassNameSeparator ?? "."}SpatialCategory`; + const defaultClassName = `BisCore.SpatialCategory`; const className = classFullName ?? defaultClassName; const model = modelId ?? IModel.dictionaryId; const id = txn.insertElement({ @@ -186,9 +189,9 @@ export function insertDrawingCategory( props: BaseInstanceInsertProps & { codeValue: string; modelId?: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, modelId, codeValue, ...categoryProps } = props; - const defaultClassName: FullClassName = `BisCore${props.fullClassNameSeparator ?? "."}DrawingCategory`; + const defaultClassName = `BisCore.DrawingCategory`; const className = classFullName ?? defaultClassName; const model = modelId ?? IModel.dictionaryId; const id = txn.insertElement({ @@ -204,19 +207,19 @@ export function insertDrawingCategory( return { className, id }; } -export function getDefaultSubcategoryKey(categoryId: Id64String, fullClassNameSeparator?: string) { +export function getDefaultSubcategoryKey(categoryId: Id64String): InstanceKey { const pair = Id64.getUint32Pair(categoryId); pair.lower++; // id of default subcategory is always `category id + 1` - return { className: `BisCore${fullClassNameSeparator ?? "."}SubCategory`, id: Id64.fromUint32PairObject(pair) }; + return { className: `BisCore.SubCategory`, id: Id64.fromUint32PairObject(pair) }; } export function insertSubCategory( props: BaseInstanceInsertProps & { codeValue: string; parentCategoryId: Id64String; modelId?: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, modelId, codeValue, parentCategoryId, ...subCategoryProps } = props; - const defaultClassName: FullClassName = `BisCore${props.fullClassNameSeparator ?? "."}SubCategory`; + const defaultClassName = `BisCore.SubCategory`; const className = classFullName ?? defaultClassName; const model = modelId ?? IModel.dictionaryId; const id = txn.insertElement({ @@ -227,10 +230,7 @@ export function insertSubCategory( scope: model, value: codeValue, }), - parent: { - id: parentCategoryId, - relClassName: `BisCore${props.fullClassNameSeparator ?? "."}CategoryOwnsSubCategories`, - }, + parent: { id: parentCategoryId, relClassName: `BisCore.CategoryOwnsSubCategories` }, ...subCategoryProps, }); return { className, id }; @@ -245,9 +245,9 @@ export function insertPhysicalElement( typeDefinitionId?: Id64String; } & Partial> & TAdditionalProps, -) { +): InstanceKey { const { txn, classFullName, modelId, categoryId, parentId, typeDefinitionId, codeValue, ...elementProps } = props; - const defaultClassName: FullClassName = `Generic${props.fullClassNameSeparator ?? "."}PhysicalObject`; + const defaultClassName = `Generic.PhysicalObject`; const className = classFullName ?? defaultClassName; const id = txn.insertElement({ classFullName: className, @@ -260,21 +260,9 @@ export function insertPhysicalElement( value: codeValue, }) : Code.createEmpty(), - ...(parentId - ? { - parent: { - id: parentId, - relClassName: `BisCore${props.fullClassNameSeparator ?? "."}PhysicalElementAssemblesElements`, - }, - } - : undefined), + ...(parentId ? { parent: { id: parentId, relClassName: `BisCore.PhysicalElementAssemblesElements` } } : undefined), ...(typeDefinitionId - ? { - typeDefinition: { - id: typeDefinitionId, - relClassName: `BisCore${props.fullClassNameSeparator ?? "."}PhysicalElementIsOfType`, - }, - } + ? { typeDefinition: { id: typeDefinitionId, relClassName: `BisCore.PhysicalElementIsOfType` } } : undefined), ...elementProps, } satisfies PhysicalElementProps); @@ -284,9 +272,9 @@ export function insertPhysicalElement( export function insertPhysicalType( props: BaseInstanceInsertProps & { modelId?: Id64String } & Partial> & TAdditionalProps, -) { +): InstanceKey { const { txn, classFullName, modelId, ...elementProps } = props; - const defaultClassName: FullClassName = `Generic${props.fullClassNameSeparator ?? "."}PhysicalType`; + const defaultClassName = `Generic.PhysicalType`; const className = classFullName ?? defaultClassName; const id = txn.insertElement({ classFullName: className, @@ -300,9 +288,9 @@ export function insertPhysicalType( export function insertPhysicalMaterial( props: BaseInstanceInsertProps & { modelId?: Id64String } & Partial> & TAdditionalProps, -) { +): InstanceKey { const { txn, classFullName, modelId, ...elementProps } = props; - const defaultClassName: FullClassName = `Generic${props.fullClassNameSeparator ?? "."}PhysicalMaterial`; + const defaultClassName = `Generic.PhysicalMaterial`; const className = classFullName ?? defaultClassName; const id = txn.insertElement({ classFullName: className, @@ -318,23 +306,16 @@ export function insertDrawingGraphic( Omit > & TAdditionalProps, -) { +): InstanceKey { const { txn, classFullName, modelId, categoryId, parentId, ...elementProps } = props; - const defaultClassName: FullClassName = `BisCore${props.fullClassNameSeparator ?? "."}DrawingGraphic`; + const defaultClassName = `BisCore.DrawingGraphic`; const className = classFullName ?? defaultClassName; const id = txn.insertElement({ classFullName: className, model: modelId, category: categoryId, code: Code.createEmpty(), - ...(parentId - ? { - parent: { - id: parentId, - relClassName: `BisCore${props.fullClassNameSeparator ?? "."}ElementOwnsChildElements`, - }, - } - : undefined), + ...(parentId ? { parent: { id: parentId, relClassName: `BisCore.ElementOwnsChildElements` } } : undefined), ...elementProps, } satisfies GeometricElement2dProps); return { className, id }; @@ -344,9 +325,9 @@ export function insertRepositoryLink( props: BaseInstanceInsertProps & { repositoryUrl?: string; repositoryLabel?: string } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, repositoryUrl, repositoryLabel, ...repoLinkProps } = props; - const defaultClassName: FullClassName = `BisCore${props.fullClassNameSeparator ?? "."}RepositoryLink`; + const defaultClassName = `BisCore.RepositoryLink`; const className = classFullName ?? defaultClassName; const id = txn.insertElement({ classFullName: className, @@ -363,16 +344,16 @@ export function insertExternalSourceAspect( props: BaseInstanceInsertProps & { elementId: Id64String; identifier: string; repositoryId?: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, repositoryId, elementId, identifier, ...externalSourceAspectProps } = props; const externalSourceId = txn.insertElement({ - classFullName: `BisCore${props.fullClassNameSeparator ?? "."}ExternalSource`, + classFullName: `BisCore.ExternalSource`, model: IModel.repositoryModelId, code: Code.createEmpty(), repository: repositoryId ? { id: repositoryId } : undefined, } satisfies ExternalSourceProps as ElementProps); - const className = `BisCore${props.fullClassNameSeparator ?? "."}ExternalSourceAspect`; + const className = `BisCore.ExternalSourceAspect`; const id = txn.insertAspect({ classFullName: className, kind: "ExternalSource", @@ -388,7 +369,7 @@ export function insertExternalSourceAspect( export function insertFunctionalModelWithPartition( props: BaseInstanceInsertProps & { codeValue: string; partitionParentId?: Id64String }, -) { +): InstanceKey { const { codeValue, partitionParentId, ...baseProps } = props; const partitionKey = insertFunctionalPartition({ ...baseProps, @@ -402,9 +383,9 @@ export function insertFunctionalPartition( props: BaseInstanceInsertProps & { codeValue: string; parentId: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, codeValue, parentId, ...partitionProps } = props; - const defaultModelClassName: FullClassName = `Functional${props.fullClassNameSeparator ?? "."}FunctionalPartition`; + const defaultModelClassName = `Functional.FunctionalPartition`; const className = classFullName ?? defaultModelClassName; const partitionId = txn.insertElement({ classFullName: className, @@ -414,7 +395,7 @@ export function insertFunctionalPartition( scope: parentId, value: codeValue, }), - parent: { id: parentId, relClassName: `BisCore${props.fullClassNameSeparator ?? "."}SubjectOwnsPartitionElements` }, + parent: { id: parentId, relClassName: `BisCore.SubjectOwnsPartitionElements` }, ...partitionProps, }); return { className, id: partitionId }; @@ -424,9 +405,9 @@ export function insertFunctionalSubModel( props: BaseInstanceInsertProps & { modeledElementId: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, modeledElementId, ...modelProps } = props; - const defaultModelClassName: FullClassName = `Functional${props.fullClassNameSeparator ?? "."}FunctionalModel`; + const defaultModelClassName = `Functional.FunctionalModel`; const className = classFullName ?? defaultModelClassName; const modelId = txn.insertModel({ classFullName: className, @@ -443,16 +424,14 @@ export function insertFunctionalElement( relationshipName: "DrawingGraphicRepresentsFunctionalElement" | "PhysicalElementFulfillsFunction"; parentId?: string; } & Partial>, -) { +): InstanceKey { const { txn, modelId, representedElementId, relationshipName, parentId, ...elementProps } = props; - const className = `Functional${props.fullClassNameSeparator ?? "."}FunctionalComposite`; + const className = `Functional.FunctionalComposite`; const id = txn.insertElement({ classFullName: className, model: modelId, code: Code.createEmpty(), - parent: parentId - ? { id: parentId, relClassName: `BisCore${props.fullClassNameSeparator ?? "."}ElementOwnsChildElements` } - : undefined, + parent: parentId ? { id: parentId, relClassName: `BisCore.ElementOwnsChildElements` } : undefined, ...elementProps, } satisfies FunctionalElementProps); txn.insertRelationship({ @@ -465,7 +444,7 @@ export function insertFunctionalElement( export function insertGroupInformationModelWithPartition( props: BaseInstanceInsertProps & { codeValue: string; partitionParentId?: Id64String }, -) { +): InstanceKey { const { codeValue, partitionParentId, ...baseProps } = props; const partitionKey = insertGroupInformationPartition({ ...baseProps, @@ -479,9 +458,9 @@ export function insertGroupInformationPartition( props: BaseInstanceInsertProps & { codeValue: string; parentId: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, codeValue, parentId, ...partitionProps } = props; - const defaultModelClassName: FullClassName = `BisCore${props.fullClassNameSeparator ?? "."}GroupInformationPartition`; + const defaultModelClassName = `BisCore.GroupInformationPartition`; const className = classFullName ?? defaultModelClassName; const partitionId = txn.insertElement({ classFullName: className, @@ -491,7 +470,7 @@ export function insertGroupInformationPartition( scope: parentId, value: codeValue, }), - parent: { id: parentId, relClassName: `BisCore${props.fullClassNameSeparator ?? "."}SubjectOwnsPartitionElements` }, + parent: { id: parentId, relClassName: `BisCore.SubjectOwnsPartitionElements` }, ...partitionProps, }); return { className, id: partitionId }; @@ -501,9 +480,9 @@ export function insertGroupInformationSubModel( props: BaseInstanceInsertProps & { modeledElementId: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, classFullName, modeledElementId, ...modelProps } = props; - const defaultModelClassName: FullClassName = `Generic${props.fullClassNameSeparator ?? "."}GroupModel`; + const defaultModelClassName = `Generic.GroupModel`; const className = classFullName ?? defaultModelClassName; const modelId = txn.insertModel({ classFullName: className, @@ -517,18 +496,18 @@ export function insertGroupInformationElement( props: BaseInstanceInsertProps & { modelId: Id64String } & Partial< Omit >, -) { +): InstanceKey { const { txn, modelId, ...elementProps } = props; - const className = `Generic${props.fullClassNameSeparator ?? "."}Group`; + const className = `Generic.Group`; const id = txn.insertElement({ classFullName: className, model: modelId, code: Code.createEmpty(), ...elementProps }); return { className, id }; } export function insertSheetIndexFolder( props: BaseInstanceInsertProps & Partial>, -) { +): InstanceKey { const { txn, ...elementProps } = props; - const className: FullClassName = `BisCore${props.fullClassNameSeparator ?? "."}SheetIndexFolder`; + const className = `BisCore.SheetIndexFolder`; const id = txn.insertElement({ classFullName: className, model: IModel.repositoryModelId, diff --git a/packages/unified-selection/src/unified-selection/HiliteSetProvider.ts b/packages/unified-selection/src/unified-selection/HiliteSetProvider.ts index 383ae9be2..029bbf1ac 100644 --- a/packages/unified-selection/src/unified-selection/HiliteSetProvider.ts +++ b/packages/unified-selection/src/unified-selection/HiliteSetProvider.ts @@ -164,7 +164,7 @@ class HiliteSetProviderImpl implements HiliteSetProvider { return promise; } - private async getTypeImpl(fullClassName: EC.FullClassName): Promise { + private async getTypeImpl(fullClassName: EC.FullClassNameDotNotation): Promise { return ( (await this.checkType(fullClassName, "BisCore.Subject", "subject")) ?? (await this.checkType(fullClassName, "BisCore.Model", "model")) ?? @@ -178,7 +178,11 @@ class HiliteSetProviderImpl implements HiliteSetProvider { ); } - private async checkType(keyClassName: EC.FullClassName, checkClassName: EC.FullClassName, type: InstanceIdType) { + private async checkType( + keyClassName: EC.FullClassNameDotNotation, + checkClassName: EC.FullClassNameDotNotation, + type: InstanceIdType, + ) { try { const res = this._imodelAccess.classDerivesFrom(keyClassName, checkClassName); const isOfType = typeof res === "boolean" ? res : await res;