From 3b6bd6799e9a4e91f71a68580cbef70ba12beeea Mon Sep 17 00:00:00 2001 From: christophermlawson <32881725+christophermlawson@users.noreply.github.com> Date: Tue, 26 Nov 2024 08:01:13 -0500 Subject: [PATCH 1/4] Derived class caching --- .../ecschema-editing/src/Editing/ECClasses.ts | 6 +- .../src/Editing/RelationshipClasses.ts | 2 +- .../src/test/Editing/Entities.test.ts | 30 +++++- .../Validation/ECRules/ClassRules.test.ts | 8 +- .../Validation/ECRules/MixinRules.test.ts | 4 +- .../Validation/ECRules/PropertyRules.test.ts | 8 +- .../test/Validation/SchemaValidater.test.ts | 4 +- core/ecschema-metadata/src/Metadata/Class.ts | 101 ++++++++++++++++-- .../src/test/Metadata/Class.test.ts | 39 +++++-- .../src/test/Metadata/EntityClass.test.ts | 6 +- 10 files changed, 172 insertions(+), 36 deletions(-) diff --git a/core/ecschema-editing/src/Editing/ECClasses.ts b/core/ecschema-editing/src/Editing/ECClasses.ts index 2762a4715cb4..6b7cbe06a45d 100644 --- a/core/ecschema-editing/src/Editing/ECClasses.ts +++ b/core/ecschema-editing/src/Editing/ECClasses.ts @@ -66,7 +66,7 @@ export class ECClasses extends SchemaItems{ if (baseClassKey !== undefined) { const baseClassSchema = !baseClassKey.schemaKey.matches(newClass.schema.schemaKey) ? await this.getSchema(baseClassKey.schemaKey) : newClass.schema as MutableSchema; const baseClassItem = await this.lookupSchemaItem(baseClassSchema, baseClassKey); - newClass.baseClass = new DelayedPromiseWithProps(baseClassKey, async () => baseClassItem as T); + await newClass.setBaseClass(new DelayedPromiseWithProps(baseClassKey, async () => baseClassItem as T)); } return newClass; @@ -275,7 +275,7 @@ export class ECClasses extends SchemaItems{ try { const classItem = await this.getSchemaItem(itemKey); if (!baseClassKey) { - classItem.baseClass = undefined; + await classItem.setBaseClass(undefined); return; } @@ -284,7 +284,7 @@ export class ECClasses extends SchemaItems{ if (classItem.baseClass !== undefined && !await baseClassItem.is(await classItem.baseClass)) throw new SchemaEditingError(ECEditingStatus.InvalidBaseClass, new ClassId(this.schemaItemType, baseClassKey), undefined, undefined, `Base class ${baseClassKey.fullName} must derive from ${(await classItem.baseClass).fullName}.`); - classItem.baseClass = new DelayedPromiseWithProps(baseClassKey, async () => baseClassItem); + await classItem.setBaseClass(new DelayedPromiseWithProps(baseClassKey, async () => baseClassItem)); } catch(e: any) { throw new SchemaEditingError(ECEditingStatus.SetBaseClass, new ClassId(this.schemaItemType, itemKey), e); } diff --git a/core/ecschema-editing/src/Editing/RelationshipClasses.ts b/core/ecschema-editing/src/Editing/RelationshipClasses.ts index 14581961aa46..aa406e966cde 100644 --- a/core/ecschema-editing/src/Editing/RelationshipClasses.ts +++ b/core/ecschema-editing/src/Editing/RelationshipClasses.ts @@ -115,7 +115,7 @@ export class RelationshipClasses extends ECClasses { try { await this.validate(relClass!); } catch(e: any) { - relClass!.baseClass = baseClass; + await relClass!.setBaseClass(baseClass); throw new SchemaEditingError(ECEditingStatus.SetBaseClass, new ClassId(SchemaItemType.RelationshipClass, itemKey), e); } } diff --git a/core/ecschema-editing/src/test/Editing/Entities.test.ts b/core/ecschema-editing/src/test/Editing/Entities.test.ts index 4553dfbe662e..9e560927b9d1 100644 --- a/core/ecschema-editing/src/test/Editing/Entities.test.ts +++ b/core/ecschema-editing/src/test/Editing/Entities.test.ts @@ -60,8 +60,13 @@ describe("Entities tests", () => { const result = await testEditor.entities.create(testKey, "testEntity", ECClassModifier.None, "testLabel", testEntityBaseRes); const testEntity = await testEditor.schemaContext.getSchemaItem(result); - expect(await testEntity?.baseClass).to.eql(await testEditor.schemaContext.getSchemaItem(testEntityBaseRes)); + const baseEntity = await testEditor.schemaContext.getSchemaItem(testEntityBaseRes); + expect(await testEntity?.baseClass).to.eql(baseEntity); expect(testEntity?.label).to.eql("testLabel"); + const derivedClasses = await baseEntity?.getDerivedClasses(); + expect(derivedClasses).to.not.be.undefined; + expect(derivedClasses![0]).to.eql(testEntity); + expect(derivedClasses?.length).to.eql(1); }); it("should create a new entity class with a base class from different schema", async () => { @@ -85,8 +90,13 @@ describe("Entities tests", () => { const result = await testEditor.entities.create(testKey, "testEntity", ECClassModifier.None, "testLabel", baseClassKey); const testEntity = await testEditor.schemaContext.getSchemaItem(result); + const baseEntity = await testEditor.schemaContext.getSchemaItem(baseClassKey); expect(await testEntity?.baseClass).to.eql(await testEditor.schemaContext.getSchemaItem(baseClassKey)); expect(testEntity?.label).to.eql("testLabel"); + const derivedClasses = await baseEntity?.getDerivedClasses(); + expect(derivedClasses).to.not.be.undefined; + expect(derivedClasses![0]).to.eql(testEntity); + expect(derivedClasses?.length).to.eql(1); }); it("try creating a new entity class with base class from unknown schema, returns error", async () => { @@ -157,7 +167,12 @@ describe("Entities tests", () => { await testEditor.entities.setBaseClass(result, testEntityBaseRes); const testEntity = await testEditor.schemaContext.getSchemaItem(result); + const baseEntity = await testEditor.schemaContext.getSchemaItem(testEntityBaseRes); expect(await testEntity?.baseClass).to.eql(await testEditor.schemaContext.getSchemaItem(testEntityBaseRes)); + const derivedClasses = await baseEntity?.getDerivedClasses(); + expect(derivedClasses).to.not.be.undefined; + expect(derivedClasses![0]).to.eql(testEntity); + expect(derivedClasses?.length).to.eql(1); }); it("should add base class to existing entity class where base class is from a different schema", async () => { @@ -182,7 +197,12 @@ describe("Entities tests", () => { await testEditor.entities.setBaseClass(result, baseClassKey); const testEntity = await testEditor.schemaContext.getSchemaItem(result); + const baseEntity = await testEditor.schemaContext.getSchemaItem(baseClassKey); expect(await testEntity?.baseClass).to.eql(await testEditor.schemaContext.getSchemaItem(baseClassKey)); + const derivedClasses = await baseEntity?.getDerivedClasses(); + expect(derivedClasses).to.not.be.undefined; + expect(derivedClasses![0]).to.eql(testEntity); + expect(derivedClasses?.length).to.eql(1); }); it("should change base class of existing entity class with different base class.", async () => { @@ -215,8 +235,13 @@ describe("Entities tests", () => { expect(await testEntity?.baseClass).to.eql(await testEditor.schemaContext.getSchemaItem(firstBaseClassKey)); const secondBaseClassKey = new SchemaItemKey("testEntityBase2", refSchema.schemaKey); + const secondBaseClass = await testEditor.schemaContext.getSchemaItem(secondBaseClassKey); await testEditor.entities.setBaseClass(testEntityResult, secondBaseClassKey); expect(await testEntity?.baseClass).to.eql(await testEditor.schemaContext.getSchemaItem(secondBaseClassKey)); + const derivedClasses = await secondBaseClass?.getDerivedClasses(); + expect(derivedClasses).to.not.be.undefined; + expect(derivedClasses![0]).to.eql(testEntity); + expect(derivedClasses?.length).to.eql(1); }); it("should remove base class from existing entity class.", async () => { @@ -228,6 +253,9 @@ describe("Entities tests", () => { await testEditor.entities.setBaseClass(result, undefined); expect(await testEntity?.baseClass).to.eql(undefined); + const baseEntity = await testEditor.schemaContext.getSchemaItem(testEntityBaseRes); + const derivedClasses = await baseEntity?.getDerivedClasses(); + expect(derivedClasses).to.be.undefined; }); it("try adding base class with unknown schema to existing entity class, returns error", async () => { diff --git a/core/ecschema-editing/src/test/Validation/ECRules/ClassRules.test.ts b/core/ecschema-editing/src/test/Validation/ECRules/ClassRules.test.ts index 8fdeacc5ad9b..6ccb46b1faee 100644 --- a/core/ecschema-editing/src/test/Validation/ECRules/ClassRules.test.ts +++ b/core/ecschema-editing/src/test/Validation/ECRules/ClassRules.test.ts @@ -22,7 +22,7 @@ describe("ClassRule tests", () => { it("BaseClassIsSealed, rule violated.", async () => { const baseClass = new EntityClass(schema, "TestBase", ECClassModifier.Sealed); const entityClass = new EntityClass(schema, "TestClass"); - entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); const result = Rules.baseClassIsSealed(entityClass); @@ -42,7 +42,7 @@ describe("ClassRule tests", () => { it("BaseClassIsSealed, base is not sealed, rule passes.", async () => { const baseClass = new EntityClass(schema, "TestBase"); const entityClass = new EntityClass(schema, "TestClass"); - entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); const result = Rules.baseClassIsSealed(entityClass); for await (const _diagnostic of result) { @@ -62,7 +62,7 @@ describe("ClassRule tests", () => { it("BaseClassIsOfDifferentType, rule violated.", async () => { const baseClass = new RelationshipClass(schema, "TestBase"); const entityClass = new EntityClass(schema, "TestClass"); - entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); const baseType = schemaItemTypeToString(baseClass.schemaItemType); const result = Rules.baseClassIsOfDifferentType(entityClass); @@ -82,7 +82,7 @@ describe("ClassRule tests", () => { it("BaseClassIsOfDifferentType, same type, rule passes.", async () => { const baseClass = new EntityClass(schema, "TestBase"); const entityClass = new EntityClass(schema, "TestClass"); - entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); const result = Rules.baseClassIsOfDifferentType(entityClass); for await (const _diagnostic of result) { diff --git a/core/ecschema-editing/src/test/Validation/ECRules/MixinRules.test.ts b/core/ecschema-editing/src/test/Validation/ECRules/MixinRules.test.ts index 8754e0af44db..7e3f7058a1a8 100644 --- a/core/ecschema-editing/src/test/Validation/ECRules/MixinRules.test.ts +++ b/core/ecschema-editing/src/test/Validation/ECRules/MixinRules.test.ts @@ -30,7 +30,7 @@ describe("Mixin Rule Tests", () => { const mixin = new TestMixin(schema, "TestMixin", constraintClass); const entityClass = new EntityClass(schema, "TestClass"); (entityClass as MutableEntityClass).addMixin(mixin); - entityClass.baseClass = new DelayedPromiseWithProps(constraintClass.key, async () => constraintClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(constraintClass.key, async () => constraintClass)); const result = Rules.mixinAppliedToClassMustDeriveFromConstraint(entityClass); for await (const _diagnostic of result) { @@ -75,7 +75,7 @@ describe("Mixin Rule Tests", () => { const mixin = new TestMixin(schema, "TestMixin", constraintClass); const entityClass = new EntityClass(schema, "TestClass"); (entityClass as MutableEntityClass).addMixin(mixin); - entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); const result = Rules.mixinAppliedToClassMustDeriveFromConstraint(entityClass); diff --git a/core/ecschema-editing/src/test/Validation/ECRules/PropertyRules.test.ts b/core/ecschema-editing/src/test/Validation/ECRules/PropertyRules.test.ts index 0f7275111f00..eef77ccc3c62 100644 --- a/core/ecschema-editing/src/test/Validation/ECRules/PropertyRules.test.ts +++ b/core/ecschema-editing/src/test/Validation/ECRules/PropertyRules.test.ts @@ -30,7 +30,7 @@ describe("PropertyRule tests", () => { testBaseClass = await mutable.createEntityClass("TestBaseClass"); testKindOfQuantity = await mutable.createKindOfQuantity("TestKoQ"); testBaseKindOfQuantity = await mutable.createKindOfQuantity("TestBaseKoQ"); - testClass.baseClass = new DelayedPromiseWithProps(testBaseClass.key, async () => testBaseClass); + await testClass.setBaseClass(new DelayedPromiseWithProps(testBaseClass.key, async () => testBaseClass)); }); describe("IncompatibleValueTypePropertyOverride Tests", () => { @@ -59,7 +59,7 @@ describe("PropertyRule tests", () => { const rootBaseClass = new EntityClass(schema, "RootBaseClass"); await (rootBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); - testBaseClass.baseClass = new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass); + await testBaseClass.setBaseClass(new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass)); const properties = [...testClass.properties!]; const results = Rules.incompatibleValueTypePropertyOverride(properties[0] as PrimitiveProperty); @@ -155,7 +155,7 @@ describe("PropertyRule tests", () => { const rootBaseClass = new EntityClass(schema, "RootBaseClass"); await (rootBaseClass as ECClass as MutableClass).createPrimitiveArrayProperty("TestProperty", PrimitiveType.String); await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); - testBaseClass.baseClass = new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass); + await testBaseClass.setBaseClass(new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass)); const properties = [...testClass.properties!]; const results = Rules.incompatibleTypePropertyOverride(properties[0] as PrimitiveProperty); @@ -257,7 +257,7 @@ describe("PropertyRule tests", () => { const rootBaseClass = new EntityClass(schema, "RootBaseClass"); await (rootBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); - testBaseClass.baseClass = new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass); + await testBaseClass.setBaseClass(new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass)); const basePropJson = { name: "TestProperty", diff --git a/core/ecschema-editing/src/test/Validation/SchemaValidater.test.ts b/core/ecschema-editing/src/test/Validation/SchemaValidater.test.ts index 9ca839ab7281..06e695de4db6 100644 --- a/core/ecschema-editing/src/test/Validation/SchemaValidater.test.ts +++ b/core/ecschema-editing/src/test/Validation/SchemaValidater.test.ts @@ -19,7 +19,7 @@ describe("SchemaValidater tests", () => { it("validateSchema, rule violation reported correctly", async () => { const baseClass = new EntityClass(schema, "TestBase", ECClassModifier.Sealed); const entityClass = new EntityClass(schema, "TestClass"); - entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); (schema as MutableSchema).addItem(entityClass); const result = await SchemaValidater.validateSchema(schema); @@ -32,7 +32,7 @@ describe("SchemaValidater tests", () => { const ruleSet = new TestRuleSet(); const baseClass = new EntityClass(schema, "TestBase", ECClassModifier.Sealed); const entityClass = new EntityClass(schema, "TestClass"); - entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); (schema as MutableSchema).addItem(entityClass); const result = await SchemaValidater.validateSchema(schema, ruleSet); diff --git a/core/ecschema-metadata/src/Metadata/Class.ts b/core/ecschema-metadata/src/Metadata/Class.ts index 28bc40a52ec6..59b3acfda339 100644 --- a/core/ecschema-metadata/src/Metadata/Class.ts +++ b/core/ecschema-metadata/src/Metadata/Class.ts @@ -29,13 +29,12 @@ import { SchemaItem } from "./SchemaItem"; export abstract class ECClass extends SchemaItem implements CustomAttributeContainerProps { protected _modifier: ECClassModifier; protected _baseClass?: LazyLoadedECClass; + protected _derivedClasses?: Map; protected _properties?: Map; private _customAttributes?: Map; private _mergedPropertyCache?: Property[]; public get modifier() { return this._modifier; } - public get baseClass(): LazyLoadedECClass | undefined { return this._baseClass; } - public set baseClass(baseClass: LazyLoadedECClass | undefined) { this._baseClass = baseClass; } public get properties(): IterableIterator | undefined { return this._properties?.values(); } public get customAttributes(): CustomAttributeSet | undefined { return this._customAttributes; } @@ -48,6 +47,37 @@ export abstract class ECClass extends SchemaItem implements CustomAttributeConta this._modifier = ECClassModifier.None; } + /** + * Gets the base class if it exists, otherwise returns undefined. + */ + public get baseClass(): LazyLoadedECClass | undefined { + return this._baseClass; + } + + /** + * Sets the base class of the ECClass. Pass undefined to 'remove' the base class. + */ + public async setBaseClass(baseClass: LazyLoadedECClass | undefined) { + const oldBaseClass = this._baseClass; + this._baseClass = baseClass; + + if (baseClass) + this.addDerivedClass(await baseClass, this); + else if (oldBaseClass) + this.removeDerivedClass(await oldBaseClass, this); + } + + /** + * Gets the derived classes belonging to this class. + * @returns An array of ECClasses or undefined if no derived classes exist. + */ + public async getDerivedClasses(): Promise { + if (!this._derivedClasses || this._derivedClasses.size === 0) + return undefined; + + return Array.from(await Promise.all(this._derivedClasses.values())); + } + /** * Convenience method for adding an already loaded ECProperty used by create*Property methods. * @param prop The property to add. @@ -418,13 +448,31 @@ export abstract class ECClass extends SchemaItem implements CustomAttributeConta const ecClassSchemaItemKey = this.schema.getSchemaItemKey(classProps.baseClass); if (!ecClassSchemaItemKey) throw new ECObjectsError(ECObjectsStatus.InvalidECJson, `Unable to locate the baseClass ${classProps.baseClass}.`); - this._baseClass = new DelayedPromiseWithProps(ecClassSchemaItemKey, - async () => { - const baseClass = await this.schema.lookupItem(ecClassSchemaItemKey); - if (undefined === baseClass) - throw new ECObjectsError(ECObjectsStatus.InvalidECJson, `Unable to locate the baseClass ${classProps.baseClass}.`); - return baseClass; - }); + + const baseClass = this.schema.lookupItemSync(ecClassSchemaItemKey); + + let lazyBase: LazyLoadedECClass; + if (!baseClass) { + lazyBase = new DelayedPromiseWithProps(ecClassSchemaItemKey, + async () => { + const baseItem = await this.schema.lookupItem(ecClassSchemaItemKey); + if (undefined === baseItem) + throw new ECObjectsError(ECObjectsStatus.InvalidECJson, `Unable to locate the baseClass ${classProps.baseClass}.`); + return baseItem; + }); + } else { + lazyBase = new DelayedPromiseWithProps(ecClassSchemaItemKey, + async () => { + return baseClass as ECClass; + }); + } + + this._baseClass = lazyBase; + + if (!baseClass) + return; + + this.addDerivedClass(baseClass as ECClass, this); } } @@ -683,6 +731,41 @@ export abstract class ECClass extends SchemaItem implements CustomAttributeConta protected setModifier(modifier: ECClassModifier) { this._modifier = modifier; } + + /** + * Adds an ECClass to the derived class collection. This method is only intended to update the local + * cache of derived classes. For adding a class to the hierarchy, use the baseClass setter method. + * @param prop The property to add. + * @return The property that was added. + */ + private addDerivedClass(baseClass: ECClass, derivedClass: ECClass) { + if (!baseClass._derivedClasses) + baseClass._derivedClasses = new Map(); + + if (baseClass._derivedClasses.has(derivedClass.fullName)) + return; + + if (derivedClass.isSync(baseClass)) { + const promise = new DelayedPromiseWithProps(derivedClass.key, async () => derivedClass); + baseClass._derivedClasses.set(derivedClass.fullName, promise); + } + } + + /** + * Removes an ECClass from the derived class collection. This method is only intended to update the local + * cache of derived classes. For updating the class hierarchy, use the baseClass setter method. + * @param prop The property to add. + * @return The property that was added. + */ + private removeDerivedClass(baseClass: ECClass, derivedClass: ECClass) { + if (!baseClass._derivedClasses) + return; + + if (!baseClass._derivedClasses.has(derivedClass.fullName)) + return; + + baseClass._derivedClasses.delete(derivedClass.fullName); + } } /** diff --git a/core/ecschema-metadata/src/test/Metadata/Class.test.ts b/core/ecschema-metadata/src/test/Metadata/Class.test.ts index 2f74cc889132..0fb18e28e864 100644 --- a/core/ecschema-metadata/src/test/Metadata/Class.test.ts +++ b/core/ecschema-metadata/src/test/Metadata/Class.test.ts @@ -50,7 +50,7 @@ describe("ECClass", () => { const entityClass = new EntityClass(schema, "TestClass"); await (entityClass as ECClass as MutableClass).createPrimitiveProperty("PrimProp"); - entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); expect(await entityClass.getProperty("BasePrimProp")).to.be.undefined; expect(await entityClass.getProperty("BasePrimProp", false)).to.be.undefined; @@ -59,13 +59,13 @@ describe("ECClass", () => { expect(await entityClass.getInheritedProperty("PrimProp")).to.be.undefined; }); - it("inherited properties from base class synchronously", () => { + it("inherited properties from base class synchronously", async () => { const baseClass = (schema as MutableSchema).createEntityClassSync("TestBase"); const basePrimProp = (baseClass as ECClass as MutableClass).createPrimitivePropertySync("BasePrimProp"); const entityClass = (schema as MutableSchema).createEntityClassSync("TestClass"); (entityClass as ECClass as MutableClass).createPrimitivePropertySync("PrimProp"); - entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); expect(entityClass.getPropertySync("BasePrimProp")).to.be.undefined; expect(entityClass.getPropertySync("BasePrimProp", false)).to.be.undefined; @@ -88,7 +88,7 @@ describe("ECClass", () => { const primProp = await (baseClass as ECClass as MutableClass).createPrimitiveProperty("TestProp"); const entityClass = new EntityClass(schema, "TestClass"); - entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); expect(await entityClass.getProperty("TESTPROP", true)).equal(primProp); expect(await entityClass.getProperty("testprop", true)).equal(primProp); @@ -528,6 +528,10 @@ describe("ECClass", () => { const baseClass = await schema.getItem("testBaseClass"); assert.isDefined(baseClass); assert.isTrue(baseClass === await testClass!.baseClass); + const derivedClasses = await baseClass?.getDerivedClasses(); + assert.isDefined(derivedClasses); + assert.isTrue(derivedClasses?.length === 1); + assert.isTrue(derivedClasses![0] === testClass); }); it("class with base class in reference schema", async () => { @@ -562,6 +566,10 @@ describe("ECClass", () => { assert.isDefined(testClass); assert.isDefined(await testClass!.baseClass); assert.isTrue(await testClass!.baseClass === refBaseClass); + const derivedClasses = await refBaseClass?.getDerivedClasses(); + assert.isDefined(derivedClasses); + assert.isTrue(derivedClasses?.length === 1); + assert.isTrue(derivedClasses![0] === testClass); }); it("should throw for missing base class", async () => { @@ -771,7 +779,7 @@ describe("ECClass", () => { }); describe("deserialization sync", () => { - it("class with base class", () => { + it("Multiple classes with same base class, derived classes set properly", async () => { const schemaJson = { $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", @@ -784,6 +792,10 @@ describe("ECClass", () => { schemaItemType: "EntityClass", baseClass: "TestSchema.testBaseClass", }, + testClass2: { + schemaItemType: "EntityClass", + baseClass: "TestSchema.testBaseClass", + }, }, }; @@ -794,12 +806,21 @@ describe("ECClass", () => { assert.isDefined(testClass); assert.isDefined(testClass!.getBaseClassSync()); + const testClass2 = schema.getItemSync("testClass2"); + assert.isDefined(testClass2); + assert.isDefined(testClass2!.getBaseClassSync()); + const baseClass = schema.getItemSync("testBaseClass"); assert.isDefined(baseClass); assert.isTrue(baseClass === testClass!.getBaseClassSync()); + const derivedClasses = await baseClass?.getDerivedClasses(); + assert.isDefined(derivedClasses); + assert.isTrue(derivedClasses?.length === 2); + assert.isTrue(derivedClasses![0] === testClass); + assert.isTrue(derivedClasses![1] === testClass2); }); - it("class with base class in reference schema", () => { + it("class with base class in reference schema", async () => { const schemaJson = { $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", @@ -831,6 +852,10 @@ describe("ECClass", () => { assert.isDefined(testClass); assert.isDefined(testClass!.getBaseClassSync()); assert.isTrue(testClass!.getBaseClassSync() === refBaseClass); + const derivedClasses = await refBaseClass?.getDerivedClasses(); + assert.isDefined(derivedClasses); + assert.isTrue(derivedClasses?.length === 1); + assert.isTrue(derivedClasses![0] === testClass); }); // Used to test that all property types are deserialized correctly. For failure and other tests look at the property // specific test files. @@ -1226,7 +1251,7 @@ describe("ECClass", () => { const testSchema = new Schema(context, "ChildSchema", "child", 1, 0, 5); const childClass = new EntityClass(testSchema, "TestClass"); - childClass.baseClass = new DelayedPromiseWithProps(testClass.key, async () => testClass); + await childClass.setBaseClass(new DelayedPromiseWithProps(testClass.key, async () => testClass)); (testSchema as MutableSchema).addItem(testClass); const serialized = await childClass.toXml(newDom); diff --git a/core/ecschema-metadata/src/test/Metadata/EntityClass.test.ts b/core/ecschema-metadata/src/test/Metadata/EntityClass.test.ts index d382fb90f87c..feb4612d9fb8 100644 --- a/core/ecschema-metadata/src/test/Metadata/EntityClass.test.ts +++ b/core/ecschema-metadata/src/test/Metadata/EntityClass.test.ts @@ -40,7 +40,7 @@ describe("EntityClass", () => { const entityClass = new EntityClass(schema, "TestClass"); await (entityClass as ECClass as MutableClass).createPrimitiveProperty("PrimProp"); - entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); (entityClass as MutableEntityClass).addMixin(mixin); expect(await entityClass.getProperty("MixinPrimProp")).to.be.undefined; @@ -54,7 +54,7 @@ describe("EntityClass", () => { expect(await entityClass.getInheritedProperty("PrimProp")).to.be.undefined; }); - it("from mixins synchronously", () => { + it("from mixins synchronously", async () => { const baseClass = (schema as MutableSchema).createEntityClassSync("TestBase"); const basePrimProp = (baseClass as ECClass as MutableClass).createPrimitivePropertySync("BasePrimProp"); @@ -63,7 +63,7 @@ describe("EntityClass", () => { const entityClass = (schema as MutableSchema).createEntityClassSync("TestClass"); (entityClass as ECClass as MutableClass).createPrimitivePropertySync("PrimProp"); - entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); (entityClass as MutableEntityClass).addMixin(mixin); expect(entityClass.getPropertySync("MixinPrimProp")).to.be.undefined; From 1ca352c01e68f1c626c7fa285a793b163dc9ae2f Mon Sep 17 00:00:00 2001 From: christophermlawson <32881725+christophermlawson@users.noreply.github.com> Date: Tue, 26 Nov 2024 08:16:33 -0500 Subject: [PATCH 2/4] change log and api extract --- common/api/ecschema-editing.api.md | 8 ++++---- common/api/ecschema-metadata.api.md | 6 ++++-- .../derived-class-caching_2024-11-26-13-11.json | 10 ++++++++++ .../derived-class-caching_2024-11-26-13-11.json | 10 ++++++++++ 4 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 common/changes/@itwin/ecschema-editing/derived-class-caching_2024-11-26-13-11.json create mode 100644 common/changes/@itwin/ecschema-metadata/derived-class-caching_2024-11-26-13-11.json diff --git a/common/api/ecschema-editing.api.md b/common/api/ecschema-editing.api.md index d2084a88ba2b..732a950e19e0 100644 --- a/common/api/ecschema-editing.api.md +++ b/common/api/ecschema-editing.api.md @@ -1928,25 +1928,25 @@ export const SchemaCompareDiagnostics: { diagnosticType: DiagnosticType; }; FormatUnitMissing: { - new (ecDefinition: SchemaItem, messageArgs: [Unit | InvertedUnit], category?: DiagnosticCategory): { + new (ecDefinition: SchemaItem, messageArgs: [InvertedUnit | Unit], category?: DiagnosticCategory): { readonly code: string; readonly messageText: string; readonly schema: Schema; readonly diagnosticType: DiagnosticType; ecDefinition: Format; - messageArgs?: [Unit | InvertedUnit] | undefined; + messageArgs?: [InvertedUnit | Unit] | undefined; category: DiagnosticCategory; }; diagnosticType: DiagnosticType; }; UnitLabelOverrideDelta: { - new (ecDefinition: SchemaItem, messageArgs: [Unit | InvertedUnit, string | undefined, string | undefined], category?: DiagnosticCategory): { + new (ecDefinition: SchemaItem, messageArgs: [InvertedUnit | Unit, string | undefined, string | undefined], category?: DiagnosticCategory): { readonly code: string; readonly messageText: string; readonly schema: Schema; readonly diagnosticType: DiagnosticType; ecDefinition: Format; - messageArgs?: [Unit | InvertedUnit, string | undefined, string | undefined] | undefined; + messageArgs?: [InvertedUnit | Unit, string | undefined, string | undefined] | undefined; category: DiagnosticCategory; }; diagnosticType: DiagnosticType; diff --git a/common/api/ecschema-metadata.api.md b/common/api/ecschema-metadata.api.md index ad4521c32d47..91ef43e729ba 100644 --- a/common/api/ecschema-metadata.api.md +++ b/common/api/ecschema-metadata.api.md @@ -257,9 +257,7 @@ export abstract class ECClass extends SchemaItem implements CustomAttributeConta // (undocumented) protected addCustomAttribute(customAttribute: CustomAttribute): void; protected addProperty(prop: T): T; - // (undocumented) get baseClass(): LazyLoadedECClass | undefined; - set baseClass(baseClass: LazyLoadedECClass | undefined); // (undocumented) protected _baseClass?: LazyLoadedECClass; // (undocumented) @@ -293,6 +291,8 @@ export abstract class ECClass extends SchemaItem implements CustomAttributeConta // @alpha protected deletePropertySync(name: string): void; // (undocumented) + protected _derivedClasses?: Map; + // (undocumented) fromJSON(classProps: ClassProps): Promise; // (undocumented) fromJSONSync(classProps: ClassProps): void; @@ -303,6 +303,7 @@ export abstract class ECClass extends SchemaItem implements CustomAttributeConta getBaseClassSync(): ECClass | undefined; getCustomAttributes(): Promise; getCustomAttributesSync(): CustomAttributeSet; + getDerivedClasses(): Promise; getInheritedProperty(name: string): Promise; getInheritedPropertySync(name: string): Property | undefined; getProperties(resetCache?: boolean): Promise; @@ -333,6 +334,7 @@ export abstract class ECClass extends SchemaItem implements CustomAttributeConta get properties(): IterableIterator | undefined; // (undocumented) protected _properties?: Map; + setBaseClass(baseClass: LazyLoadedECClass | undefined): Promise; // @alpha protected setModifier(modifier: ECClassModifier): void; toJSON(standalone?: boolean, includeSchemaVersion?: boolean): ClassProps; diff --git a/common/changes/@itwin/ecschema-editing/derived-class-caching_2024-11-26-13-11.json b/common/changes/@itwin/ecschema-editing/derived-class-caching_2024-11-26-13-11.json new file mode 100644 index 000000000000..802f5d3dd81d --- /dev/null +++ b/common/changes/@itwin/ecschema-editing/derived-class-caching_2024-11-26-13-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/ecschema-editing", + "comment": "Replaced ECClass.baseClass setter with setBaseClass method due to underlying changes in the ecschema-metadata package.", + "type": "none" + } + ], + "packageName": "@itwin/ecschema-editing" +} \ No newline at end of file diff --git a/common/changes/@itwin/ecschema-metadata/derived-class-caching_2024-11-26-13-11.json b/common/changes/@itwin/ecschema-metadata/derived-class-caching_2024-11-26-13-11.json new file mode 100644 index 000000000000..69700e754a16 --- /dev/null +++ b/common/changes/@itwin/ecschema-metadata/derived-class-caching_2024-11-26-13-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/ecschema-metadata", + "comment": "ECClass.baseClass setter has been replaced with setBaseClass method to enable derived class caching support.", + "type": "none" + } + ], + "packageName": "@itwin/ecschema-metadata" +} \ No newline at end of file From cca08199cc869c12038794bad296d682a0ddc8a2 Mon Sep 17 00:00:00 2001 From: christophermlawson <32881725+christophermlawson@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:54:30 -0500 Subject: [PATCH 3/4] PR updates --- core/ecschema-editing/src/Editing/ECClasses.ts | 6 +++--- .../src/Editing/Mutable/MutableClass.ts | 3 ++- .../src/Editing/RelationshipClasses.ts | 10 +++++++--- .../src/test/Validation/ECRules/ClassRules.test.ts | 11 ++++++----- .../src/test/Validation/ECRules/MixinRules.test.ts | 7 ++++--- .../src/test/Validation/ECRules/PropertyRules.test.ts | 8 ++++---- .../src/test/Validation/SchemaValidater.test.ts | 7 ++++--- core/ecschema-metadata/src/Metadata/Class.ts | 3 ++- .../ecschema-metadata/src/test/Metadata/Class.test.ts | 8 ++++---- .../src/test/Metadata/EntityClass.test.ts | 4 ++-- 10 files changed, 38 insertions(+), 29 deletions(-) diff --git a/core/ecschema-editing/src/Editing/ECClasses.ts b/core/ecschema-editing/src/Editing/ECClasses.ts index 6b7cbe06a45d..2d211c4f9acd 100644 --- a/core/ecschema-editing/src/Editing/ECClasses.ts +++ b/core/ecschema-editing/src/Editing/ECClasses.ts @@ -66,7 +66,7 @@ export class ECClasses extends SchemaItems{ if (baseClassKey !== undefined) { const baseClassSchema = !baseClassKey.schemaKey.matches(newClass.schema.schemaKey) ? await this.getSchema(baseClassKey.schemaKey) : newClass.schema as MutableSchema; const baseClassItem = await this.lookupSchemaItem(baseClassSchema, baseClassKey); - await newClass.setBaseClass(new DelayedPromiseWithProps(baseClassKey, async () => baseClassItem as T)); + await (newClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClassKey, async () => baseClassItem as T)); } return newClass; @@ -275,7 +275,7 @@ export class ECClasses extends SchemaItems{ try { const classItem = await this.getSchemaItem(itemKey); if (!baseClassKey) { - await classItem.setBaseClass(undefined); + await (classItem as MutableClass).setBaseClass(undefined); return; } @@ -284,7 +284,7 @@ export class ECClasses extends SchemaItems{ if (classItem.baseClass !== undefined && !await baseClassItem.is(await classItem.baseClass)) throw new SchemaEditingError(ECEditingStatus.InvalidBaseClass, new ClassId(this.schemaItemType, baseClassKey), undefined, undefined, `Base class ${baseClassKey.fullName} must derive from ${(await classItem.baseClass).fullName}.`); - await classItem.setBaseClass(new DelayedPromiseWithProps(baseClassKey, async () => baseClassItem)); + await (classItem as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClassKey, async () => baseClassItem)); } catch(e: any) { throw new SchemaEditingError(ECEditingStatus.SetBaseClass, new ClassId(this.schemaItemType, itemKey), e); } diff --git a/core/ecschema-editing/src/Editing/Mutable/MutableClass.ts b/core/ecschema-editing/src/Editing/Mutable/MutableClass.ts index 6743a26fe449..7d2e48e9671b 100644 --- a/core/ecschema-editing/src/Editing/Mutable/MutableClass.ts +++ b/core/ecschema-editing/src/Editing/Mutable/MutableClass.ts @@ -3,7 +3,7 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { - CustomAttribute, ECClass, ECClassModifier, Enumeration, EnumerationArrayProperty, EnumerationProperty, PrimitiveArrayProperty, + CustomAttribute, ECClass, ECClassModifier, Enumeration, EnumerationArrayProperty, EnumerationProperty, LazyLoadedECClass, PrimitiveArrayProperty, PrimitiveProperty, PrimitiveType, Property, StructArrayProperty, StructClass, StructProperty, } from "@itwin/ecschema-metadata"; @@ -20,6 +20,7 @@ export abstract class MutableStructClass extends StructClass { * @internal */ export abstract class MutableClass extends ECClass { + public abstract override setBaseClass(baseClass: LazyLoadedECClass | undefined): Promise; public abstract override addCustomAttribute(customAttribute: CustomAttribute): void; public abstract override setModifier(modifier: ECClassModifier): void; public abstract override setName(name: string): void; diff --git a/core/ecschema-editing/src/Editing/RelationshipClasses.ts b/core/ecschema-editing/src/Editing/RelationshipClasses.ts index aa406e966cde..742d4733ab98 100644 --- a/core/ecschema-editing/src/Editing/RelationshipClasses.ts +++ b/core/ecschema-editing/src/Editing/RelationshipClasses.ts @@ -7,7 +7,7 @@ */ import { - CustomAttribute, DelayedPromiseWithProps, ECClassModifier, EntityClass, LazyLoadedRelationshipConstraintClass, Mixin, NavigationPropertyProps, + CustomAttribute, DelayedPromiseWithProps, ECClass, ECClassModifier, EntityClass, LazyLoadedRelationshipConstraintClass, Mixin, NavigationPropertyProps, RelationshipClass, RelationshipClassProps, RelationshipConstraint, RelationshipEnd, RelationshipMultiplicity, SchemaItemKey, SchemaItemType, SchemaKey, StrengthDirection, StrengthType, } from "@itwin/ecschema-metadata"; @@ -18,6 +18,7 @@ import * as Rules from "../Validation/ECRules"; import { AnyDiagnostic, RelationshipConstraintDiagnostic, SchemaItemDiagnostic } from "../Validation/Diagnostic"; import { NavigationProperties } from "./Properties"; import { ClassId, CustomAttributeId, ECEditingStatus, RelationshipConstraintId, SchemaEditingError } from "./Exception"; +import { MutableClass } from "./Mutable/MutableClass"; /** * @alpha @@ -107,7 +108,10 @@ export class RelationshipClasses extends ECClasses { * @param baseClassKey The SchemaItemKey of the base class. Specifying 'undefined' removes the base class. */ public override async setBaseClass(itemKey: SchemaItemKey, baseClassKey?: SchemaItemKey): Promise { - const relClass = await this.schemaEditor.schemaContext.getSchemaItem(itemKey); + const relClass = await this.schemaEditor.schemaContext.getSchemaItem(itemKey) + .catch((e) => { + throw new SchemaEditingError(ECEditingStatus.SetBaseClass, new ClassId(this.schemaItemType, itemKey), e); + }); const baseClass = relClass?.baseClass; await super.setBaseClass(itemKey, baseClassKey); @@ -115,7 +119,7 @@ export class RelationshipClasses extends ECClasses { try { await this.validate(relClass!); } catch(e: any) { - await relClass!.setBaseClass(baseClass); + await (relClass! as ECClass as MutableClass).setBaseClass(baseClass); throw new SchemaEditingError(ECEditingStatus.SetBaseClass, new ClassId(SchemaItemType.RelationshipClass, itemKey), e); } } diff --git a/core/ecschema-editing/src/test/Validation/ECRules/ClassRules.test.ts b/core/ecschema-editing/src/test/Validation/ECRules/ClassRules.test.ts index c6f8c8ae9354..c750bcdd3910 100644 --- a/core/ecschema-editing/src/test/Validation/ECRules/ClassRules.test.ts +++ b/core/ecschema-editing/src/test/Validation/ECRules/ClassRules.test.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { DelayedPromiseWithProps, ECClassModifier, EntityClass, +import { DelayedPromiseWithProps, ECClass, ECClassModifier, EntityClass, RelationshipClass, Schema, SchemaContext, schemaItemTypeToString, } from "@itwin/ecschema-metadata"; import * as Rules from "../../../Validation/ECRules"; import { DiagnosticCategory, DiagnosticType } from "../../../Validation/Diagnostic"; +import { MutableClass } from "../../../Editing/Mutable/MutableClass"; /* eslint-disable @typescript-eslint/no-deprecated */ @@ -22,7 +23,7 @@ describe("ClassRule tests", () => { it("BaseClassIsSealed, rule violated.", async () => { const baseClass = new EntityClass(schema, "TestBase", ECClassModifier.Sealed); const entityClass = new EntityClass(schema, "TestClass"); - await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); const result = Rules.baseClassIsSealed(entityClass); @@ -42,7 +43,7 @@ describe("ClassRule tests", () => { it("BaseClassIsSealed, base is not sealed, rule passes.", async () => { const baseClass = new EntityClass(schema, "TestBase"); const entityClass = new EntityClass(schema, "TestClass"); - await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); const result = Rules.baseClassIsSealed(entityClass); for await (const _diagnostic of result) { @@ -62,7 +63,7 @@ describe("ClassRule tests", () => { it("BaseClassIsOfDifferentType, rule violated.", async () => { const baseClass = new RelationshipClass(schema, "TestBase"); const entityClass = new EntityClass(schema, "TestClass"); - await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); const baseType = schemaItemTypeToString(baseClass.schemaItemType); const result = Rules.baseClassIsOfDifferentType(entityClass); @@ -82,7 +83,7 @@ describe("ClassRule tests", () => { it("BaseClassIsOfDifferentType, same type, rule passes.", async () => { const baseClass = new EntityClass(schema, "TestBase"); const entityClass = new EntityClass(schema, "TestClass"); - await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); const result = Rules.baseClassIsOfDifferentType(entityClass); for await (const _diagnostic of result) { diff --git a/core/ecschema-editing/src/test/Validation/ECRules/MixinRules.test.ts b/core/ecschema-editing/src/test/Validation/ECRules/MixinRules.test.ts index 4076f7990b46..67b78311960f 100644 --- a/core/ecschema-editing/src/test/Validation/ECRules/MixinRules.test.ts +++ b/core/ecschema-editing/src/test/Validation/ECRules/MixinRules.test.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { DelayedPromiseWithProps, EntityClass, Mixin, Schema, SchemaContext } from "@itwin/ecschema-metadata"; +import { DelayedPromiseWithProps, ECClass, EntityClass, Mixin, Schema, SchemaContext } from "@itwin/ecschema-metadata"; import { MutableEntityClass } from "../../../Editing/Mutable/MutableEntityClass"; import { DiagnosticCategory, DiagnosticType } from "../../../Validation/Diagnostic"; import * as Rules from "../../../Validation/ECRules"; +import { MutableClass } from "../../../Editing/Mutable/MutableClass"; describe("Mixin Rule Tests", () => { let schema: Schema; @@ -28,7 +29,7 @@ describe("Mixin Rule Tests", () => { const mixin = new TestMixin(schema, "TestMixin", constraintClass); const entityClass = new EntityClass(schema, "TestClass"); (entityClass as MutableEntityClass).addMixin(mixin); - await entityClass.setBaseClass(new DelayedPromiseWithProps(constraintClass.key, async () => constraintClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(constraintClass.key, async () => constraintClass)); const result = Rules.mixinAppliedToClassMustDeriveFromConstraint(entityClass); for await (const _diagnostic of result) { @@ -73,7 +74,7 @@ describe("Mixin Rule Tests", () => { const mixin = new TestMixin(schema, "TestMixin", constraintClass); const entityClass = new EntityClass(schema, "TestClass"); (entityClass as MutableEntityClass).addMixin(mixin); - await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); const result = Rules.mixinAppliedToClassMustDeriveFromConstraint(entityClass); diff --git a/core/ecschema-editing/src/test/Validation/ECRules/PropertyRules.test.ts b/core/ecschema-editing/src/test/Validation/ECRules/PropertyRules.test.ts index eef77ccc3c62..7e5245b17215 100644 --- a/core/ecschema-editing/src/test/Validation/ECRules/PropertyRules.test.ts +++ b/core/ecschema-editing/src/test/Validation/ECRules/PropertyRules.test.ts @@ -30,7 +30,7 @@ describe("PropertyRule tests", () => { testBaseClass = await mutable.createEntityClass("TestBaseClass"); testKindOfQuantity = await mutable.createKindOfQuantity("TestKoQ"); testBaseKindOfQuantity = await mutable.createKindOfQuantity("TestBaseKoQ"); - await testClass.setBaseClass(new DelayedPromiseWithProps(testBaseClass.key, async () => testBaseClass)); + await (testClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(testBaseClass.key, async () => testBaseClass)); }); describe("IncompatibleValueTypePropertyOverride Tests", () => { @@ -59,7 +59,7 @@ describe("PropertyRule tests", () => { const rootBaseClass = new EntityClass(schema, "RootBaseClass"); await (rootBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); - await testBaseClass.setBaseClass(new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass)); + await (testClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass)); const properties = [...testClass.properties!]; const results = Rules.incompatibleValueTypePropertyOverride(properties[0] as PrimitiveProperty); @@ -155,7 +155,7 @@ describe("PropertyRule tests", () => { const rootBaseClass = new EntityClass(schema, "RootBaseClass"); await (rootBaseClass as ECClass as MutableClass).createPrimitiveArrayProperty("TestProperty", PrimitiveType.String); await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); - await testBaseClass.setBaseClass(new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass)); + await (testBaseClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass)); const properties = [...testClass.properties!]; const results = Rules.incompatibleTypePropertyOverride(properties[0] as PrimitiveProperty); @@ -257,7 +257,7 @@ describe("PropertyRule tests", () => { const rootBaseClass = new EntityClass(schema, "RootBaseClass"); await (rootBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); - await testBaseClass.setBaseClass(new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass)); + await (testBaseClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass)); const basePropJson = { name: "TestProperty", diff --git a/core/ecschema-editing/src/test/Validation/SchemaValidater.test.ts b/core/ecschema-editing/src/test/Validation/SchemaValidater.test.ts index 06e695de4db6..0fda9573284a 100644 --- a/core/ecschema-editing/src/test/Validation/SchemaValidater.test.ts +++ b/core/ecschema-editing/src/test/Validation/SchemaValidater.test.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { DelayedPromiseWithProps, ECClassModifier, EntityClass, Schema, SchemaContext } from "@itwin/ecschema-metadata"; +import { DelayedPromiseWithProps, ECClass, ECClassModifier, EntityClass, Schema, SchemaContext } from "@itwin/ecschema-metadata"; import { MutableSchema } from "../../Editing/Mutable/MutableSchema"; import { SchemaValidater } from "../../Validation/SchemaValidater"; import { TestRuleSet } from "../TestUtils/DiagnosticHelpers"; +import { MutableClass } from "../../Editing/Mutable/MutableClass"; describe("SchemaValidater tests", () => { let schema: Schema; @@ -19,7 +20,7 @@ describe("SchemaValidater tests", () => { it("validateSchema, rule violation reported correctly", async () => { const baseClass = new EntityClass(schema, "TestBase", ECClassModifier.Sealed); const entityClass = new EntityClass(schema, "TestClass"); - await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); (schema as MutableSchema).addItem(entityClass); const result = await SchemaValidater.validateSchema(schema); @@ -32,7 +33,7 @@ describe("SchemaValidater tests", () => { const ruleSet = new TestRuleSet(); const baseClass = new EntityClass(schema, "TestBase", ECClassModifier.Sealed); const entityClass = new EntityClass(schema, "TestClass"); - await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); (schema as MutableSchema).addItem(entityClass); const result = await SchemaValidater.validateSchema(schema, ruleSet); diff --git a/core/ecschema-metadata/src/Metadata/Class.ts b/core/ecschema-metadata/src/Metadata/Class.ts index e8f045c5c476..692becc6c049 100644 --- a/core/ecschema-metadata/src/Metadata/Class.ts +++ b/core/ecschema-metadata/src/Metadata/Class.ts @@ -58,7 +58,7 @@ export abstract class ECClass extends SchemaItem implements CustomAttributeConta /** * Sets the base class of the ECClass. Pass undefined to 'remove' the base class. */ - public async setBaseClass(baseClass: LazyLoadedECClass | undefined) { + protected async setBaseClass(baseClass: LazyLoadedECClass | undefined) { const oldBaseClass = this._baseClass; this._baseClass = baseClass; @@ -800,6 +800,7 @@ export abstract class MutableStructClass extends StructClass { * @internal */ export abstract class MutableClass extends ECClass { + public abstract override setBaseClass(baseClass: LazyLoadedECClass | undefined): Promise; public abstract override addCustomAttribute(customAttribute: CustomAttribute): void; public abstract override setModifier(modifier: ECClassModifier): void; public abstract override setName(name: string): void; diff --git a/core/ecschema-metadata/src/test/Metadata/Class.test.ts b/core/ecschema-metadata/src/test/Metadata/Class.test.ts index 0fb18e28e864..3fe7aa074f9d 100644 --- a/core/ecschema-metadata/src/test/Metadata/Class.test.ts +++ b/core/ecschema-metadata/src/test/Metadata/Class.test.ts @@ -50,7 +50,7 @@ describe("ECClass", () => { const entityClass = new EntityClass(schema, "TestClass"); await (entityClass as ECClass as MutableClass).createPrimitiveProperty("PrimProp"); - await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); expect(await entityClass.getProperty("BasePrimProp")).to.be.undefined; expect(await entityClass.getProperty("BasePrimProp", false)).to.be.undefined; @@ -65,7 +65,7 @@ describe("ECClass", () => { const entityClass = (schema as MutableSchema).createEntityClassSync("TestClass"); (entityClass as ECClass as MutableClass).createPrimitivePropertySync("PrimProp"); - await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); expect(entityClass.getPropertySync("BasePrimProp")).to.be.undefined; expect(entityClass.getPropertySync("BasePrimProp", false)).to.be.undefined; @@ -88,7 +88,7 @@ describe("ECClass", () => { const primProp = await (baseClass as ECClass as MutableClass).createPrimitiveProperty("TestProp"); const entityClass = new EntityClass(schema, "TestClass"); - await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); expect(await entityClass.getProperty("TESTPROP", true)).equal(primProp); expect(await entityClass.getProperty("testprop", true)).equal(primProp); @@ -1251,7 +1251,7 @@ describe("ECClass", () => { const testSchema = new Schema(context, "ChildSchema", "child", 1, 0, 5); const childClass = new EntityClass(testSchema, "TestClass"); - await childClass.setBaseClass(new DelayedPromiseWithProps(testClass.key, async () => testClass)); + await (childClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(testClass.key, async () => testClass)); (testSchema as MutableSchema).addItem(testClass); const serialized = await childClass.toXml(newDom); diff --git a/core/ecschema-metadata/src/test/Metadata/EntityClass.test.ts b/core/ecschema-metadata/src/test/Metadata/EntityClass.test.ts index feb4612d9fb8..ba461b249fde 100644 --- a/core/ecschema-metadata/src/test/Metadata/EntityClass.test.ts +++ b/core/ecschema-metadata/src/test/Metadata/EntityClass.test.ts @@ -40,7 +40,7 @@ describe("EntityClass", () => { const entityClass = new EntityClass(schema, "TestClass"); await (entityClass as ECClass as MutableClass).createPrimitiveProperty("PrimProp"); - await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); (entityClass as MutableEntityClass).addMixin(mixin); expect(await entityClass.getProperty("MixinPrimProp")).to.be.undefined; @@ -63,7 +63,7 @@ describe("EntityClass", () => { const entityClass = (schema as MutableSchema).createEntityClassSync("TestClass"); (entityClass as ECClass as MutableClass).createPrimitivePropertySync("PrimProp"); - await entityClass.setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); + await (entityClass as ECClass as MutableClass).setBaseClass(new DelayedPromiseWithProps(baseClass.key, async () => baseClass)); (entityClass as MutableEntityClass).addMixin(mixin); expect(entityClass.getPropertySync("MixinPrimProp")).to.be.undefined; From c9dab88629fb3c7b9651b645dd49b89449b53345 Mon Sep 17 00:00:00 2001 From: christophermlawson <32881725+christophermlawson@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:16:32 -0500 Subject: [PATCH 4/4] rush extract --- common/api/ecschema-editing.api.md | 8 ++++---- common/api/ecschema-metadata.api.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/api/ecschema-editing.api.md b/common/api/ecschema-editing.api.md index dbcf475e7429..2a3af03de685 100644 --- a/common/api/ecschema-editing.api.md +++ b/common/api/ecschema-editing.api.md @@ -1934,25 +1934,25 @@ export const SchemaCompareDiagnostics: { diagnosticType: DiagnosticType; }; FormatUnitMissing: { - new (ecDefinition: SchemaItem, messageArgs: [InvertedUnit | Unit], category?: DiagnosticCategory): { + new (ecDefinition: SchemaItem, messageArgs: [Unit | InvertedUnit], category?: DiagnosticCategory): { readonly code: string; readonly messageText: string; readonly schema: Schema; readonly diagnosticType: DiagnosticType; ecDefinition: Format; - messageArgs?: [InvertedUnit | Unit] | undefined; + messageArgs?: [Unit | InvertedUnit] | undefined; category: DiagnosticCategory; }; diagnosticType: DiagnosticType; }; UnitLabelOverrideDelta: { - new (ecDefinition: SchemaItem, messageArgs: [InvertedUnit | Unit, string | undefined, string | undefined], category?: DiagnosticCategory): { + new (ecDefinition: SchemaItem, messageArgs: [Unit | InvertedUnit, string | undefined, string | undefined], category?: DiagnosticCategory): { readonly code: string; readonly messageText: string; readonly schema: Schema; readonly diagnosticType: DiagnosticType; ecDefinition: Format; - messageArgs?: [InvertedUnit | Unit, string | undefined, string | undefined] | undefined; + messageArgs?: [Unit | InvertedUnit, string | undefined, string | undefined] | undefined; category: DiagnosticCategory; }; diagnosticType: DiagnosticType; diff --git a/common/api/ecschema-metadata.api.md b/common/api/ecschema-metadata.api.md index 6ac617701445..2936681a0b50 100644 --- a/common/api/ecschema-metadata.api.md +++ b/common/api/ecschema-metadata.api.md @@ -334,7 +334,7 @@ export abstract class ECClass extends SchemaItem implements CustomAttributeConta get properties(): IterableIterator | undefined; // (undocumented) protected _properties?: Map; - setBaseClass(baseClass: LazyLoadedECClass | undefined): Promise; + protected setBaseClass(baseClass: LazyLoadedECClass | undefined): Promise; // @alpha protected setModifier(modifier: ECClassModifier): void; toJSON(standalone?: boolean, includeSchemaVersion?: boolean): ClassProps;