diff --git a/common/api/core-backend.api.md b/common/api/core-backend.api.md index bf2ea7e59cd..c005f4c721f 100644 --- a/common/api/core-backend.api.md +++ b/common/api/core-backend.api.md @@ -2592,6 +2592,13 @@ export class ElementMultiAspect extends ElementAspect { static get className(): string; } +// @public +export class ElementOwnsChannelRootAspect extends ElementOwnsUniqueAspect { + constructor(elementId: Id64String, relClassName?: string); + // (undocumented) + static classFullName: string; +} + // @public export class ElementOwnsChildElements extends RelatedElement { constructor(parentId: Id64String, relClassName?: string); diff --git a/common/api/summary/core-backend.exports.csv b/common/api/summary/core-backend.exports.csv index 1f49448fb98..bd9c2cdb349 100644 --- a/common/api/summary/core-backend.exports.csv +++ b/common/api/summary/core-backend.exports.csv @@ -190,6 +190,7 @@ public;class;ElementGroupsMembers public;interface;ElementGroupsMembersProps public;class;ElementMultiAspect preview;class;ElementMultiAspect +public;class;ElementOwnsChannelRootAspect public;class;ElementOwnsChildElements public;class;ElementOwnsExternalSourceAspects public;class;ElementOwnsMultiAspects diff --git a/common/changes/@itwin/core-backend/rohitptnkr-use-correct-relclass-for-channelroot-aspect_2025-11-24-10-24.json b/common/changes/@itwin/core-backend/rohitptnkr-use-correct-relclass-for-channelroot-aspect_2025-11-24-10-24.json new file mode 100644 index 00000000000..06f0e0d3061 --- /dev/null +++ b/common/changes/@itwin/core-backend/rohitptnkr-use-correct-relclass-for-channelroot-aspect_2025-11-24-10-24.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/core-backend", + "comment": "ChannelRoot gets created with the correct aspect relationship class.", + "type": "none" + } + ], + "packageName": "@itwin/core-backend" +} \ No newline at end of file diff --git a/core/backend/src/NavigationRelationship.ts b/core/backend/src/NavigationRelationship.ts index fe0a3fbf1fe..de5e887e624 100644 --- a/core/backend/src/NavigationRelationship.ts +++ b/core/backend/src/NavigationRelationship.ts @@ -110,7 +110,7 @@ export class FolderContainsRepositories extends ElementOwnsChildElements { export class SheetIndexFolderOwnsEntries extends ElementOwnsChildElements { public static override classFullName = "BisCore:SheetIndexFolderOwnsEntries"; public constructor(parentId: Id64String, relClassName: string = SheetIndexFolderOwnsEntries.classFullName) { - super(parentId, relClassName ); + super(parentId, relClassName); } } @@ -120,7 +120,7 @@ export class SheetIndexFolderOwnsEntries extends ElementOwnsChildElements { export class SheetIndexOwnsEntries extends ElementOwnsChildElements { public static override classFullName = "BisCore:SheetIndexOwnsEntries"; public constructor(parentId: Id64String, relClassName: string = SheetIndexOwnsEntries.classFullName) { - super(parentId, relClassName ); + super(parentId, relClassName); } } @@ -287,3 +287,15 @@ export class SheetOwnsSheetInformationAspect extends ElementOwnsUniqueAspect { super(sheetId, relClassName); } } + +/** Relates an [[Element]] to the [[ChannelRootAspect]] that identifies it as a channel root. + * @see [[ChannelControl.makeChannelRoot]] to create an instance of this relationship. + * @public + */ +export class ElementOwnsChannelRootAspect extends ElementOwnsUniqueAspect { + public static override classFullName = "BisCore:ElementOwnsChannelRootAspect"; + + public constructor(elementId: Id64String, relClassName = ElementOwnsChannelRootAspect.classFullName) { + super(elementId, relClassName); + } +} diff --git a/core/backend/src/internal/ChannelAdmin.ts b/core/backend/src/internal/ChannelAdmin.ts index 01fc0a2eace..314de77c95b 100644 --- a/core/backend/src/internal/ChannelAdmin.ts +++ b/core/backend/src/internal/ChannelAdmin.ts @@ -12,6 +12,7 @@ import { ChannelControl, ChannelKey } from "../ChannelControl"; import { Subject } from "../Element"; import { IModelDb } from "../IModelDb"; import { IModelHost } from "../IModelHost"; +import { ElementOwnsChannelRootAspect } from "../NavigationRelationship"; import { _implementationProhibited, _nativeDb, _verifyChannel } from "./Symbols"; class ChannelAdmin implements ChannelControl { @@ -94,7 +95,14 @@ class ChannelAdmin implements ChannelControl { if (this.queryChannelRoot(args.channelKey) !== undefined) ChannelControlError.throwError("root-exists", `Channel ${args.channelKey} root already exist`, channelKey); - const props: ChannelRootAspectProps = { classFullName: ChannelAdmin.channelClassName, element: { id: args.elementId }, owner: args.channelKey }; + const props: ChannelRootAspectProps = { + classFullName: ChannelAdmin.channelClassName, + element: { + id: args.elementId, + relClassName: ElementOwnsChannelRootAspect.classFullName, + }, + owner: args.channelKey, + }; this._iModel.elements.insertAspect(props); } diff --git a/core/backend/src/test/element/ElementAspect.test.ts b/core/backend/src/test/element/ElementAspect.test.ts index 54d64a5a390..1c8c07368ea 100644 --- a/core/backend/src/test/element/ElementAspect.test.ts +++ b/core/backend/src/test/element/ElementAspect.test.ts @@ -411,4 +411,39 @@ describe("ElementAspect", () => { assert.equal(ExternalSourceAspect.findAllBySource(iModelDb, scopeId1, kind, "").length, 0); }); + it("should create ChannelRootAspect with correct relationship class", async () => { + const iModelDb = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile("ElementAspect", "ChannelRootAspectTest.bim"), { rootSubject: { name: "ChannelRootAspectTest" } }); + + const testChannelKey = "test-channel"; + + // Enable the test channel + iModelDb.channels.addAllowedChannel(testChannelKey); + + // Create a channel subject using insertChannelSubject + const subjectId = iModelDb.channels.insertChannelSubject({ + subjectName: "Test Channel Subject", + channelKey: testChannelKey, + }); + iModelDb.saveChanges(); + assert.isTrue(Id64.isValidId64(subjectId), "Subject ID should be valid"); + + // Get the ChannelRootAspect + const aspects = iModelDb.elements.getAspects(subjectId, "BisCore:ChannelRootAspect"); + assert.equal(aspects.length, 1, "Should be exactly one as it's a unique aspect"); + + const aspect = aspects[0]; + assert.exists(aspect); + assert.equal(aspect.classFullName, "BisCore:ChannelRootAspect", "Aspect class should be ChannelRootAspect"); + + // Verify the relationship class + expect(aspect.element.relClassName).to.equal("BisCore.ElementOwnsChannelRootAspect"); + assert.equal((aspect as any).owner, testChannelKey, "Channel owner should match the channel key"); + + // Query the db to confirm the relationship class + const reader = iModelDb.createQueryReader("select ec_classname(Element.RelECClassId) as relClassName from BisCore.ChannelRootAspect"); + expect(await reader.step()).to.be.true; + expect(reader.current.relClassName).to.equal("BisCore:ElementOwnsChannelRootAspect"); + + iModelDb.close(); + }); });