Skip to content
8 changes: 7 additions & 1 deletion packages/engine/Source/Core/ITwinPlatform.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,12 @@ ITwinPlatform.apiEndpoint = new Resource({
*
* @throws {RuntimeError} If the iTwin API request is not successful
*/
ITwinPlatform.getExports = async function (iModelId) {
ITwinPlatform.getExports = async function (iModelId, changesetId) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.string("iModelId", iModelId);
if (defined(changesetId)) {
Check.typeOf.string("changesetId", changesetId);
}
if (
!defined(ITwinPlatform.defaultAccessToken) &&
!defined(ITwinPlatform.defaultShareKey)
Expand Down Expand Up @@ -191,6 +194,9 @@ ITwinPlatform.getExports = async function (iModelId) {
if (typeof CESIUM_VERSION !== "undefined") {
resource.appendQueryParameters({ clientVersion: CESIUM_VERSION });
}
if (defined(changesetId)) {
resource.appendQueryParameters({ changesetId: changesetId });
}

try {
const response = await resource.fetchJson();
Expand Down
10 changes: 8 additions & 2 deletions packages/engine/Source/Scene/ITwinData.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,19 @@ const ITwinData = {};
*
* @param {string} iModelId The id of the iModel to load
* @param {Cesium3DTileset.ConstructorOptions} [options] Object containing options to pass to the internally created {@link Cesium3DTileset}.
* @param {string} [changesetId] The id of the changeset to load, if not provided the latest changesets will be used
* @returns {Promise<Cesium3DTileset | undefined>} A promise that will resolve to the created 3D tileset or <code>undefined</code> if there is no completed export for the given iModel id
*
* @throws {RuntimeError} If no exports for the given iModel are found
* @throws {RuntimeError} If all exports for the given iModel are Invalid
* @throws {RuntimeError} If the iTwin API request is not successful
*/
ITwinData.createTilesetFromIModelId = async function (iModelId, options) {
const { exports } = await ITwinPlatform.getExports(iModelId);
ITwinData.createTilesetFromIModelId = async function (
iModelId,
options,
changesetId,
) {
Copy link
Contributor

@jjspace jjspace Jul 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been thinking back and forth on this. Especially with the recent (and somewhat lengthy) discussion around handling of arguments for this API as a whole in #12709

With optional arguments, there's an argument (😉) to be made that they should be ordered from most to least used. I also think it's valid to think about the "relation" between arguments. My gut says this should go ( iModelId, [changesetId], [tilesetOptions] ) as the imodel id and changeset id are the most related. However I think it's more likely that people will want to use the iModelId and the tilesetOptions and only rarely the changesetId which means the current arrangement is fine.

From the discussion in #12709 I almost think the best solution is ( iTwinOptions, [tilesetOptions] ) where the iTwinOptions is { iModelId, [changesetId] }. Or maybe even better a single ({ iModelId, [changesetId], [tilesetOptions] }) argument is actually best?

Future updates and "backwards compatibility" (even if this is experimental) are also worth taking into account. This order means all existing calls will "just work".

Open to some discussion on this (maybe thoughts from @javagl?). However if we need to include this for the release I'm ok with this solution and addressing all the functions with #12709

CC @ggetz

Copy link
Contributor Author

@r-veenstra r-veenstra Jul 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To help add context to this decision, another parameter I would want to be able to define is a custom iTwin Share Key on a per tileset basis.

A common request from existing iTwin users is to be able to visualise multiple iTwins in one application. Currently a user can only define Cesium.ITwinPlatform.defaultShareKey which won't work if multiple iTwins are required.

It's also possible we would want to define a custom Cesium.ITwinPlatform.defaultAccessToken too if I'm using a different authentication method. And if the Mesh Export API that this wraps grows in capability, we'd want to easily support that in the future.

So I think the structure should lend itself to having a set of iTwinOptions that are easily expandable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this offline and decided to go with the route of a single options object for this API. I've updated this PR to change all the functions signatures (the core part of discussion in #12709) including the new changeset id option.

const { exports } = await ITwinPlatform.getExports(iModelId, changesetId);

if (
exports.length > 0 &&
Expand Down
11 changes: 11 additions & 0 deletions packages/engine/Specs/Core/ITwinPlatformSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,17 @@ describe("ITwinPlatform", () => {
expect(resource).toBeDefined();
expect(resource.url).toContain("imodel-id-1");
});

it("uses the changeset in the API request", async () => {
let resource;
requestSpy.and.callFake(function () {
resource = this;
return JSON.stringify({ exports: [] });
});
await ITwinPlatform.getExports("imodel-id-1", "changeset-id-1");
expect(resource).toBeDefined();
expect(resource.url).toContain("changeset-id-1");
});
});

describe("getRealityDataMetadata", () => {
Expand Down