-
Notifications
You must be signed in to change notification settings - Fork 222
Description
Describe the bug
When calling entity.getMetaData on a relationship, throws an error 'Cannot get metadata'.
This is because getMetaData only expects an EntityClass to be queried, not an EntityRelationship. getMetaData() should support both.
Entity._metadata is also restricted to be an EntityClass when it could potentially be an EntityRelationship as well.
To Reproduce
Test to reproduce the behavior:
it("should get metadata for a relationship", async () => {
const imodelPath = IModelTestUtils.prepareOutputFile("IModel", "relationshipMetadata.bim");
const imodel = SnapshotDb.createEmpty(imodelPath, { rootSubject: { name: "relationshipMetadata" } });
const partitionId = imodel.elements.insertElement({
classFullName: "BisCore:PhysicalPartition",
model: IModel.repositoryModelId,
parent: {
relClassName: "BisCore:SubjectOwnsPartitionElements",
id: IModel.rootSubjectId,
},
code: new Code({
spec: imodel.codeSpecs.getByName(BisCodeSpec.informationPartitionElement).id,
scope: IModel.rootSubjectId,
value: "physical model",
}),
});
for await (const row of imodel.createQueryReader(`SELECT * FROM bis.Element LIMIT ${1}`)) {
const relId = imodel.relationships.insertInstance({
classFullName: "BisCore:ElementHasLinks",
sourceId: partitionId,
targetId: row.ECInstanceId,
});
const relationship = imodel.relationships.getInstance("BisCore:ElementHasLinks", relId);
const metadata = relationship.getMetaData();
assert.isDefined(metadata, metadata should be defined);
}
imodel.close();
});
Expected behavior
Should not error.
Additional context
The issue cause is similar to: #8231.
Additional discussion on this topic can be found here: #8277
Discussion from PR
After some discussion with Affan, I feel we should do something like this:
export class Entity {
public async getMetaData(): Promise<EntityClass | RelationshipClass> {
const ecClass = await this.iModel.schemaContext.getSchemaItem(this.schemaItemKey, ECClass);
if (EntityClass.isEntityClass(ecClass) || RelationshipClass.isRelationshipClass(ecClass)) {
return ecClass;
}
throw new Error(Cannot get metadata for ${this.classFullName}
);
}
}export class Element extends Entity {
public async override getMetaData(): Promise {
const entity = await this.iModel.schemaContext.getSchemaItem(this.schemaItemKey, EntityClass);
if (entity !== undefined) {
return entity;
}
throw new Error(Cannot get metadata for ${this.classFullName}
);
}
}export class Relationship extends Entity {
public async override getMetaData(): Promise {
const relationship = await this.iModel.schemaContext.getSchemaItem(this.schemaItemKey, RelationshipClass);
if (relationship !== undefined) {
return relationship;
}
throw new Error(Cannot get metadata for ${this.classFullName}
);
}
}
However, that is a breaking change. It will probably not affect anybody because everybody is working with subclasses of Entity. Still, we need to make sure we override this properly on every subclass.The problem stems from the fact that the class hierarchy between ecschema-metadata and core-backend are different. ecschema uses ECClass as a common base class for entity and relationship while backend has relationship directly derive from entity.
It does kind of look like the only way to fix getMetaData is to break the API.
It's a real shame that we've capitalized the D in MetaData
I guess we could deprecate getMetaData and replace with a correct getMetadata function.