@itwin/reality-data-client
Version:
HTTP Client for the iTwin Platform Reality Management APIs
1,416 lines (1,266 loc) • 47.2 kB
text/typescript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import * as chai from "chai";
import chaiAsPromised from "chai-as-promised";
import {
type AccessToken,
type GuidString,
Logger,
LogLevel,
} from "@itwin/core-bentley";
import { CartographicRange, type RealityData } from "@itwin/core-common";
import { Point3d, Range3d, Transform } from "@itwin/core-geometry";
import {
ApiVersion,
RealityDataAccessClient,
type RealityDataClientOptions,
type RealityDataQueryCriteria,
} from "../../RealityDataClient";
import { TestConfig } from "../TestConfig";
import { ITwinRealityData } from "../../RealityData";
async function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
chai.config.showDiff = true;
chai.should();
chai.use(chaiAsPromised);
const LOG_CATEGORY: string = "RealityDataClient.Test";
Logger.initializeToConsole();
Logger.setLevel(LOG_CATEGORY, LogLevel.Info);
const realityDataClientConfig: RealityDataClientOptions = {
version: ApiVersion.v1,
baseUrl: "https://api.bentley.com/reality-management/reality-data",
};
const realityDataAccessClientForTest = new RealityDataAccessClient(
realityDataClientConfig,
);
describe("RealityServicesClient Normal (#integration)", () => {
const iTwinId: GuidString = TestConfig.integrationTestsITwinId;
const iTwinId2: GuidString = TestConfig.integrationTestsITwinIdProjects;
const realityDataAccessClient = new RealityDataAccessClient();
let realityDataId: string;
let realityDataIdProjects: string;
// test RealityData for listing tests.
const realityDataList: ITwinRealityData[] = [];
let accessToken: AccessToken;
before(async () => {
// get token
accessToken = await TestConfig.getAccessToken();
chai.assert.isDefined(iTwinId);
chai.assert.isDefined(iTwinId2);
// create a test reality data
let rdTest = new ITwinRealityData(
realityDataAccessClient,
{
displayName: "iTwinjs reality-data-client test, can be deleted",
type: "3MX",
},
iTwinId,
);
rdTest = await realityDataAccessClient.createRealityData(
accessToken,
iTwinId,
rdTest,
);
chai.assert(rdTest, "Failed to create test reality data");
realityDataId = rdTest.id;
// create a test reality data associated to both iTwins
let rdTestProjects = new ITwinRealityData(
realityDataAccessClient,
{
displayName: "iTwinjs reality-data-client projects, can be deleted",
type: "3MX",
},
iTwinId,
);
rdTestProjects = await realityDataAccessClient.createRealityData(
accessToken,
iTwinId,
rdTestProjects,
);
await realityDataAccessClient.associateRealityData(
accessToken,
iTwinId2,
rdTestProjects.id,
);
chai.assert(rdTestProjects, "Failed to create test reality data");
// create 10 reality data for listing tests
for (let i = 0; i < 10; i++) {
let rd = new ITwinRealityData(
realityDataAccessClient,
{
displayName: `iTwinjs reality-data-client listing test ${i}, can be deleted`,
type: "3MX",
},
iTwinId,
);
rd = await realityDataAccessClient.createRealityData(
accessToken,
iTwinId,
rd,
);
chai.assert(rd, `Failed to create test reality data ${i}`);
realityDataList.push(rd);
}
realityDataIdProjects = rdTestProjects.id;
});
after(async () => {
// delete the test reality data
const deleteResult1 = await realityDataAccessClient.deleteRealityData(
accessToken,
realityDataId,
);
chai.assert(deleteResult1, "Failed to delete test reality data");
const dissociateResult =
await realityDataAccessClient.dissociateRealityData(
accessToken,
iTwinId2,
realityDataIdProjects,
);
chai.assert(dissociateResult, "Failed to dissociate test reality data");
const deleteResult2 = await realityDataAccessClient.deleteRealityData(
accessToken,
realityDataIdProjects,
);
chai.assert(deleteResult2, "Failed to delete test reality data");
// delete the test reality data for listing tests
for (let i = 0; i < 10; i++) {
const deleteResult = await realityDataAccessClient.deleteRealityData(
accessToken,
realityDataList[i].id,
);
chai.assert(deleteResult, `Failed to delete test reality data ${i}`);
}
});
it("should properly redirect configured URL to proper Reality Management API URL", async () => {
// dev
const realityDataClientConfigDev: RealityDataClientOptions = {
version: ApiVersion.v1,
baseUrl: "https://dev-api.bentley.com/realitydata",
};
const realityDataAccessClientDev = new RealityDataAccessClient(
realityDataClientConfigDev,
);
chai.assert(
realityDataAccessClientDev.baseUrl ===
"https://dev-api.bentley.com/reality-management/reality-data",
);
// qa
const realityDataClientConfigQa: RealityDataClientOptions = {
version: ApiVersion.v1,
baseUrl: "https://qa-api.bentley.com/www.someotherfakehost.com/",
};
const realityDataAccessClientQa = new RealityDataAccessClient(
realityDataClientConfigQa,
);
chai.assert(
realityDataAccessClientQa.baseUrl ===
"https://qa-api.bentley.com/reality-management/reality-data",
);
// prod
const realityDataClientConfigProd: RealityDataClientOptions = {
version: ApiVersion.v1,
baseUrl: "https://api.bentley.com/realitydata",
};
const realityDataAccessClientProd = new RealityDataAccessClient(
realityDataClientConfigProd,
);
chai.assert(
realityDataAccessClientProd.baseUrl ===
"https://api.bentley.com/reality-management/reality-data",
);
// error test
const realityDataClientConfigError: RealityDataClientOptions = {
version: ApiVersion.v1,
baseUrl: "https://evilwebsite.net",
};
try {
const _realityDataAccessClientError = new RealityDataAccessClient(
realityDataClientConfigError,
);
} catch (errorResponse: any) {
chai.assert(
errorResponse.message === "invalid host",
`Error message should be 'invalid host'. It is '${errorResponse.errorNumber}.`,
);
return;
}
});
it("should return a RealityData URL properly from a given ID", async () => {
try {
// test with iTwinId
let realityDataUrl = await realityDataAccessClient.getRealityDataUrl(
iTwinId,
realityDataId,
);
const expectedUrl = `${realityDataClientConfig.baseUrl}/${realityDataId}?iTwinId=${iTwinId}`;
chai.assert(realityDataUrl === expectedUrl);
// test without iTwinId
realityDataUrl = await realityDataAccessClient.getRealityDataUrl(
undefined,
realityDataId,
);
chai.assert(
realityDataUrl ===
`${realityDataClientConfig.baseUrl}/${realityDataId}`,
);
} catch (errorResponse: any) {
throw Error(`Test error: ${errorResponse}`);
}
});
it("should return a RealityData from a given ID", async () => {
try {
let realityData = await realityDataAccessClientForTest.getRealityData(
accessToken,
iTwinId,
realityDataId,
);
chai.assert(realityData);
chai.assert(realityData.id === realityDataId);
// test without iTwinId
await delay(1000);
realityData = await realityDataAccessClientForTest.getRealityData(
accessToken,
undefined,
realityDataId,
);
chai.assert(realityData);
chai.assert(realityData.id === realityDataId);
} catch (errorResponse: any) {
throw Error(`Test error: ${errorResponse}`);
}
});
it("should return a RealityData from a given ID and respect RealityDataAccessProps interfaces", async () => {
try {
const realityData: RealityData =
await realityDataAccessClientForTest.getRealityData(
accessToken,
iTwinId,
realityDataId,
);
chai.assert(realityData);
chai.assert(realityData.id === realityDataId);
} catch (errorResponse: any) {
throw Error(`Test error: ${errorResponse}`);
}
});
// TODO: remove once getRealityDataProjects method is removed in next major release
it("should be able to get project information from a RealityData", async () => {
// displayName: iTwinjs RealityData Client get projects test, id: d344d5ec-5068-4752-9432-ff1c8f087111
const realityData = await realityDataAccessClientForTest.getRealityData(
accessToken,
iTwinId,
realityDataIdProjects,
);
chai.assert(realityData);
// get all projects information
/* eslint-disable @typescript-eslint/no-deprecated */
const projects =
await realityDataAccessClientForTest.getRealityDataProjects(
accessToken,
realityData.id,
);
/* eslint-enable @typescript-eslint/no-deprecated */
chai.assert(projects);
chai.assert(projects.length === 2);
projects.forEach((value) => {
chai.assert(value.id);
chai.assert(value.projectDetailsLink);
});
});
it("should be able to get iTwin information from a RealityData", async () => {
// displayName: iTwinjs RealityData Client get projects test, id: d344d5ec-5068-4752-9432-ff1c8f087111
const realityData = await realityDataAccessClientForTest.getRealityData(
accessToken,
iTwinId,
realityDataIdProjects,
);
chai.assert(realityData);
// get all projects information
const projects = await realityDataAccessClientForTest.getRealityDataITwins(
accessToken,
realityData.id,
);
chai.assert(projects);
chai.assert(projects.length === 2);
projects.forEach((value) => {
chai.assert(value);
});
});
it("should be able to retrieve the azure blob url", async () => {
const realityData = await realityDataAccessClientForTest.getRealityData(
accessToken,
iTwinId,
realityDataId,
);
const url: URL = await realityData.getBlobUrl(accessToken, "test");
chai.assert(url);
chai.assert(url.toString().includes("test"));
// cache test, wait 1 second and make the same call again, url should be the same.
await delay(1000);
const fakeAccessToken = "fake"; // this ensures that we are not executing a request to the API for a new SAS url, otherwise it would fail
const url2: URL = await realityData.getBlobUrl(fakeAccessToken, "test");
chai.assert(url.href === url2.href);
// cache test, wait 1 second and request the same reality data with WRITE access, url should be different.
await delay(1000);
const url2Write: URL = await realityData.getBlobUrl(
accessToken,
"test",
true,
);
chai.assert(url2.href !== url2Write.href);
// test without iTwinId
await delay(1000);
const realityData2 = await realityDataAccessClientForTest.getRealityData(
accessToken,
undefined,
realityDataId,
);
const url3: URL = await realityData2.getBlobUrl(accessToken, "test");
chai.assert(url3);
chai.assert(url3.toString().includes("test"));
});
it("should be able to retrieve reality data properties for every reality data associated with iTwin", async () => {
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
undefined,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(realityDatas);
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.type);
chai.assert(value.id);
});
});
it("should be able to retrieve reality data properties for every available realitydata", async () => {
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
undefined,
undefined,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(realityDatas);
realityDatas.forEach((value) => {
chai.assert(value.id);
chai.assert(value.type);
chai.assert(value.displayName);
});
});
it("should query the first 10 reality data using the $top=10 parameter", async () => {
const realityDataQueryCriteria: RealityDataQueryCriteria = {
top: 10,
};
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(realityDatas);
chai.assert(realityDatas.length === 10);
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.type);
chai.assert(value.id);
});
// max results > 500 should fail.
const badRealityDataQueryCriteria: RealityDataQueryCriteria = {
top: 501,
};
await chai
.expect(
realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
badRealityDataQueryCriteria,
),
)
.to.eventually.be.rejectedWith(Error);
});
it("should be able to query using continuationToken", async () => {
// get the first 5
const realityDataQueryCriteria: RealityDataQueryCriteria = {
top: 5,
};
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(realityDatas);
chai.assert(realityDatas.length === 5);
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.type);
chai.assert(value.id);
});
chai.assert(realityDataResponse.continuationToken);
// get another 5 with continuation token
const realityDataQueryCriteriaContinuationToken: RealityDataQueryCriteria =
{
top: 5,
continuationToken: realityDataResponse.continuationToken,
};
const realityDataResponseContinuation =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteriaContinuationToken,
);
const realityDatasContinued = realityDataResponseContinuation.realityDatas;
chai.assert(realityDatasContinued);
chai.assert(realityDatasContinued.length === 5);
realityDatasContinued.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.type);
chai.assert(value.id);
});
// test until no more continuation
const realityDataQueryUntilTheEnd: RealityDataQueryCriteria = {
top: 5,
continuationToken: realityDataResponseContinuation.continuationToken,
};
while (realityDataQueryUntilTheEnd.continuationToken) {
const realityDataResponseUntilTheEnd =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryUntilTheEnd,
);
realityDataQueryUntilTheEnd.continuationToken =
realityDataResponseUntilTheEnd.continuationToken;
}
});
it("should query reality data using the $orderby parameter", async () => {
const realityDataQueryCriteria: RealityDataQueryCriteria = {
getFullRepresentation: true,
top: 10,
orderBy: "createdDateTime desc",
};
const realityDataResponse = await realityDataAccessClient.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(realityDatas);
chai.assert(
realityDatas.length > 1,
"Expected at least two reality data results",
);
let lastCreatedTime = Number.MAX_SAFE_INTEGER;
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.type);
chai.assert(value.id);
chai.assert(value?.createdDateTime);
chai.assert(value.createdDateTime.getTime() <= lastCreatedTime);
lastCreatedTime = value.createdDateTime.getTime();
});
});
it("should query reality data using the $search parameter with the search term: This is a fairly simple description.", async () => {
const realityDataQueryCriteria: RealityDataQueryCriteria = {
getFullRepresentation: true,
top: 10,
search: "This is a fairly simple description.",
};
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(
realityDatas && realityDatas.length > 0,
"No reality data found. Please verify test reality data exists with description 'This is a fairly simple description.'.",
);
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.type);
chai.assert(value.id);
chai.assert(
value.description?.includes("This is a fairly simple description."),
);
});
});
it("should query reality data using the types parameter", async () => {
const types = ["3MX", "3SM"];
const realityDataQueryCriteria: RealityDataQueryCriteria = {
top: 10,
types,
};
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(
realityDatas && realityDatas.length > 0,
"No reality data found. Please verify test reality data exists with types 3MX and 3SM.",
);
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.type);
chai.assert(value.id);
chai.assert(value.type === "3MX" || value.type === "3SM");
});
});
it("should query reality data using the acquisitionDateTime parameter", async () => {
const realityDataQueryCriteria: RealityDataQueryCriteria = {
getFullRepresentation: true,
top: 10,
acquisitionDates: {
startDateTime: new Date(2017, 5, 12),
endDateTime: new Date(2023, 5, 12),
},
};
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(
realityDatas && realityDatas.length > 0,
"No reality data found. Please verify test reality data exists with acquisition dates between 2017-05-12 and 2023-05-12.",
);
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.type);
chai.assert(value.id);
chai.assert(value.acquisition);
chai.assert(value.acquisition?.startDateTime);
chai.assert(
value.acquisition?.startDateTime.getTime() >=
new Date(2017, 5, 12).getTime(),
);
chai.assert(
value.acquisition?.startDateTime.getTime() <=
new Date(2023, 5, 12).getTime(),
);
});
});
it("should query reality data using the createdDateTime parameter", async () => {
const realityDataQueryCriteria: RealityDataQueryCriteria = {
getFullRepresentation: true,
top: 10,
createdDateTime: {
startDateTime: new Date(2021, 5, 12),
endDateTime: new Date(2022, 5, 12),
},
};
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(
realityDatas && realityDatas.length > 0,
"No reality data found. Please verify test reality data exists with creation dates between 2021-05-12 and 2022-05-12.",
);
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.type);
chai.assert(value.id);
chai.assert(value.createdDateTime);
chai.assert(
value.createdDateTime.getTime() >= new Date(2021, 5, 12).getTime(),
);
chai.assert(
value.createdDateTime.getTime() <= new Date(2022, 5, 12).getTime(),
);
});
});
it("should query reality data using the modifiedDateTime parameter", async () => {
const realityDataQueryCriteria: RealityDataQueryCriteria = {
getFullRepresentation: true,
top: 10,
modifiedDateTime: {
startDateTime: new Date(2021, 0, 1),
endDateTime: new Date(2030, 0, 1),
},
};
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(
realityDatas && realityDatas.length > 0,
"No reality data found. Please verify test reality data exists with modification dates between 2021-01-01 and 2030-01-01.",
);
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.type);
chai.assert(value.id);
chai.assert(value.modifiedDateTime);
chai.assert(
value.modifiedDateTime.getTime() >= new Date(2021, 0, 1).getTime(),
);
chai.assert(
value.modifiedDateTime.getTime() <= new Date(2030, 0, 1).getTime(),
);
});
});
it("should query reality data using the lastAccessedDateTime parameter", async () => {
const realityDataQueryCriteria: RealityDataQueryCriteria = {
getFullRepresentation: true,
top: 10,
lastAccessedDateTime: {
startDateTime: new Date(2021, 0, 1),
endDateTime: new Date(2030, 0, 1),
},
};
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(
realityDatas && realityDatas.length > 0,
"No reality data found. Please verify test reality data exists with last accessed dates between 2021-01-01 and 2030-01-01.",
);
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.type);
chai.assert(value.id);
chai.assert(value.lastAccessedDateTime);
chai.assert(
value.lastAccessedDateTime.getTime() >= new Date(2021, 0, 1).getTime(),
);
chai.assert(
value.lastAccessedDateTime.getTime() <= new Date(2030, 0, 1).getTime(),
);
});
});
it("should query reality data using the ownerId parameter", async () => {
// Fetch one reality data to get its ownerId, then query by that ownerId
const referenceRd = await realityDataAccessClientForTest.getRealityData(
accessToken,
iTwinId,
realityDataId,
);
chai.assert(
referenceRd.ownerId,
"ownerId must be populated on a fetched reality data",
);
const realityDataQueryCriteria: RealityDataQueryCriteria = {
getFullRepresentation: true,
top: 10,
ownerId: referenceRd.ownerId,
};
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(
realityDatas && realityDatas.length > 0,
"No reality data found when filtering by ownerId.",
);
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.id);
chai.assert(value.type);
chai.assert(value.ownerId === referenceRd.ownerId);
});
});
it("should query reality data using the dataCenter parameter", async () => {
const realityDataQueryCriteria: RealityDataQueryCriteria = {
getFullRepresentation: true,
top: 10,
dataCenter: "East US",
};
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(
realityDatas && realityDatas.length > 0,
"No reality data found in 'East US' data center. Please verify test reality data exists in that data center.",
);
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.id);
chai.assert(value.type);
chai.assert(value.dataCenterLocation === "East US");
});
});
it("should query reality data using the tag parameter", async () => {
const realityDataQueryCriteria: RealityDataQueryCriteria = {
getFullRepresentation: true,
top: 10,
tag: "tag1",
};
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
const realityDatas = realityDataResponse.realityDatas;
chai.assert(
realityDatas && realityDatas.length > 0,
"No reality data found. Please verify test reality data exists with tag 'tag1'.",
);
realityDatas.forEach((value) => {
chai.assert(value.iTwinId === iTwinId);
chai.assert(value.type);
chai.assert(value.id);
chai.assert(value.tags);
chai.assert(value.tags.includes("tag1"));
});
});
it("should be able to retrieve reality data properties for every reality data associated with iTwin within an extent", async () => {
// arrange
let rdWithExtent = new ITwinRealityData(
realityDataAccessClientForTest,
{
displayName:
"iTwinjs reality-data-client test with extent, can be deleted",
type: "3SM",
extent: {
southWest: { latitude: 40.6706, longitude: -80.3455 },
northEast: { latitude: 40.6716, longitude: -80.3359 },
},
},
iTwinId,
);
rdWithExtent = await realityDataAccessClientForTest.createRealityData(
accessToken,
iTwinId,
rdWithExtent,
);
chai.assert(rdWithExtent, "Failed to create test reality data");
const rdWithExtentId = rdWithExtent.id;
// act
const cornerSpatial = new Array<Point3d>();
cornerSpatial.push(new Point3d(813907, -4775048, 4135438));
cornerSpatial.push(new Point3d(814123, -4776318, 4135438));
cornerSpatial.push(new Point3d(812222, -4776642, 4135438));
cornerSpatial.push(new Point3d(812007, -4775372, 4135438));
const rdRange = Range3d.createArray(cornerSpatial);
const rdCartographicRange = new CartographicRange(
rdRange,
Transform.createIdentity(),
);
const realityDataQueryCriteria: RealityDataQueryCriteria = {
top: 10,
extent: rdCartographicRange,
};
const realityDataResponse =
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
// assert
chai.assert(realityDataResponse);
chai.assert(
realityDataResponse.realityDatas.length > 0,
"No reality data found. Please verify test reality data exists within the given extent.",
);
chai.assert(
realityDataResponse.realityDatas.filter((rd) => rd.id === rdWithExtentId)
.length > 0,
);
// delete the test reality data
const deleteResult = await realityDataAccessClientForTest.deleteRealityData(
accessToken,
rdWithExtentId,
);
chai.assert(deleteResult, "Failed to delete test reality data");
});
it("should be able to create, modify, then delete a Reality Data", async () => {
// this test also serves as a property test.
const realityData = new ITwinRealityData(realityDataAccessClientForTest);
realityData.displayName =
"iTwinjs RealityData Client create and delete test";
realityData.dataset = "Test Dataset for iTwinjs";
realityData.group = "Test group";
realityData.description = "Dummy description for a test reality data";
realityData.attribution = "Attribution text";
realityData.termsOfUse = "Terms of use text";
realityData.rootDocument = "RootDocumentFile.txt";
realityData.classification = "Undefined";
realityData.type = "LAS";
realityData.acquisition = {
startDateTime: new Date("2019-05-10T09:46:16Z"),
endDateTime: new Date("2019-05-10T09:46:16Z"),
acquirer: "John Doe Surveying using Leico model 123A Point Cloud Scanner",
};
realityData.extent = {
southWest: {
latitude: 1.0,
longitude: 2.0,
},
northEast: {
latitude: 1.1,
longitude: 2.1,
},
};
realityData.crs = {
id: "EPSG:4326",
verticalId: "EPSG:5773",
};
realityData.tags = ["tag1", "tag2"];
realityData.authoring = false;
const realityDataAdded =
await realityDataAccessClientForTest.createRealityData(
accessToken,
iTwinId,
realityData,
);
chai.assert(realityDataAdded.id);
chai.assert(realityDataAdded.displayName === realityData.displayName);
chai.assert(realityDataAdded.group === realityData.group);
chai.assert(realityDataAdded.dataset === realityData.dataset);
chai.assert(realityDataAdded.description === realityData.description);
chai.assert(realityDataAdded.attribution === realityData.attribution);
chai.assert(realityDataAdded.termsOfUse === realityData.termsOfUse);
chai.assert(realityDataAdded.rootDocument === realityData.rootDocument);
chai.assert(realityDataAdded.classification === realityData.classification);
chai.assert(
realityDataAdded.type?.toLowerCase() === realityData.type.toLowerCase(),
);
chai.assert(
realityDataAdded.acquisition?.acquirer ===
realityData.acquisition.acquirer,
);
chai.assert(
realityDataAdded.acquisition?.startDateTime.getTime() ===
realityData.acquisition.startDateTime.getTime(),
);
chai.assert(realityData.acquisition?.endDateTime);
chai.assert(realityDataAdded.acquisition?.endDateTime);
chai.assert(
realityDataAdded.acquisition.endDateTime.getTime() ===
realityData.acquisition.endDateTime.getTime(),
);
chai.assert(realityDataAdded.extent);
chai.assert(realityDataAdded.extent.southWest.latitude === 1.0);
chai.assert(realityDataAdded.extent.southWest.longitude === 2.0);
chai.assert(realityDataAdded.extent.northEast.latitude === 1.1);
chai.assert(realityDataAdded.extent.northEast.longitude === 2.1);
chai.assert(realityDataAdded.crs);
chai.assert(realityDataAdded.crs.id === "EPSG:4326");
chai.assert(realityDataAdded.crs.verticalId === "EPSG:5773");
chai.assert(realityDataAdded.tags);
chai.assert(realityDataAdded.tags.includes("tag1"));
chai.assert(realityDataAdded.tags.includes("tag2"));
chai.assert(realityDataAdded.authoring === false);
chai.assert(realityDataAdded.dataCenterLocation === "East US");
chai.assert(realityDataAdded.modifiedDateTime);
chai.assert(realityDataAdded.modifiedDateTime.getTime());
chai.assert(realityDataAdded.lastAccessedDateTime);
chai.assert(realityDataAdded.lastAccessedDateTime.getTime());
chai.assert(realityDataAdded.createdDateTime);
chai.assert(realityDataAdded.createdDateTime.getTime());
chai.assert(realityDataAdded.ownerId);
realityDataAdded.displayName =
"MODIFIED iTwinjs RealityData Client create and delete test";
realityDataAdded.group = "MODIFIED Test group";
realityDataAdded.dataset = "MODIFIED Test Dataset for iTwinjs";
realityDataAdded.description =
"MODIFIED Dummy description for a test reality data";
realityDataAdded.rootDocument = "MODIFIED RootDocumentFile.txt";
realityDataAdded.classification = "Model";
realityDataAdded.type = "LAZ";
realityDataAdded.acquisition = {
startDateTime: new Date("2021-05-10T09:46:16Z"),
endDateTime: new Date("2021-05-10T10:46:16Z"),
acquirer:
"MODIFIED John Doe Surveying using Leico model 123A Point Cloud Scanner",
};
realityDataAdded.extent = {
southWest: { latitude: 1.1, longitude: 2.0 },
northEast: { latitude: 1.2, longitude: 2.1 },
};
realityDataAdded.tags = ["tag1", "tag2", "tag3"];
realityDataAdded.crs = {
id: "EPSG:3857",
verticalId: "EPSG:5703",
};
realityDataAdded.attribution = "MODIFIED Attribution text";
realityDataAdded.termsOfUse = "MODIFIED Terms of use text";
const realityDataModified =
await realityDataAccessClientForTest.modifyRealityData(
accessToken,
iTwinId,
realityDataAdded,
);
chai.assert(
realityDataModified.displayName === realityDataAdded.displayName,
);
chai.assert(realityDataModified.group === realityDataAdded.group);
chai.assert(realityDataModified.dataset === realityDataAdded.dataset);
chai.assert(
realityDataModified.description === realityDataAdded.description,
);
chai.assert(
realityDataModified.rootDocument === realityDataAdded.rootDocument,
);
chai.assert(
realityDataModified.classification === realityDataAdded.classification,
);
chai.assert(
realityDataModified.type?.toLowerCase() ===
realityDataAdded.type.toLowerCase(),
);
chai.assert(
realityDataModified.acquisition?.acquirer ===
realityDataAdded.acquisition.acquirer,
);
chai.assert(
realityDataModified.acquisition?.startDateTime.getTime() ===
realityDataAdded.acquisition.startDateTime.getTime(),
);
chai.assert(realityDataModified.acquisition?.endDateTime);
chai.assert(realityDataAdded.acquisition?.endDateTime);
chai.assert(
realityDataModified.acquisition.endDateTime.getTime() ===
realityDataAdded.acquisition.endDateTime.getTime(),
);
chai.assert(realityDataModified.extent);
chai.assert(realityDataModified.extent.southWest.latitude === 1.1);
chai.assert(realityDataModified.extent.southWest.longitude === 2.0);
chai.assert(realityDataModified.extent.northEast.latitude === 1.2);
chai.assert(realityDataModified.extent.northEast.longitude === 2.1);
chai.assert(realityDataModified.crs);
chai.assert(realityDataModified.crs.id === realityDataAdded.crs.id);
chai.assert(
realityDataModified.crs.verticalId === realityDataAdded.crs.verticalId,
);
chai.assert(
realityDataModified.attribution === realityDataAdded.attribution,
);
chai.assert(realityDataModified.termsOfUse === realityDataAdded.termsOfUse);
chai.assert(realityDataModified.tags);
chai.assert(realityDataModified.tags.includes("tag1"));
chai.assert(realityDataModified.tags.includes("tag2"));
chai.assert(realityDataModified.tags.includes("tag3"));
chai.assert(
await realityDataAccessClientForTest.deleteRealityData(
accessToken,
realityDataAdded.id,
),
);
});
it("should be able to create, associate, dissociate, and delete RealityData", async () => {
const realityData = new ITwinRealityData(realityDataAccessClientForTest);
realityData.displayName =
"iTwinjs RealityData Client associate and dissociate test";
realityData.classification = "Undefined";
realityData.type = "3MX";
const realityDataAdded =
await realityDataAccessClientForTest.createRealityData(
accessToken,
iTwinId,
realityData,
);
chai.assert(
await realityDataAccessClientForTest.associateRealityData(
accessToken,
iTwinId2,
realityDataAdded.id,
),
);
chai.assert(
await realityDataAccessClientForTest.dissociateRealityData(
accessToken,
iTwinId2,
realityDataAdded.id,
),
);
chai.assert(
await realityDataAccessClientForTest.deleteRealityData(
accessToken,
realityDataAdded.id,
),
);
});
it("should be able to create modify, and delete Reality Data, (Without iTwinId)", async () => {
const realityData = new ITwinRealityData(realityDataAccessClientForTest);
realityData.displayName =
"iTwinjs RealityData Client CRUD test without iTwinId";
realityData.classification = "Undefined";
realityData.type = "3MX";
// current test user belongs to no organization and needs a iTwin to create realityData. However, the modify without iTwinId can be tested.
const realityDataAdded =
await realityDataAccessClientForTest.createRealityData(
accessToken,
iTwinId,
realityData,
);
realityDataAdded.displayName =
"MODIFIED iTwinjs RealityData Client CRUD test without iTwinId";
const realityDataModified =
await realityDataAccessClientForTest.modifyRealityData(
accessToken,
undefined,
realityDataAdded,
);
chai.assert(
realityDataModified.displayName === realityDataAdded.displayName,
);
chai.assert(
await realityDataAccessClientForTest.deleteRealityData(
accessToken,
realityDataAdded.id,
),
);
});
it("should be able to move Reality Data", async () => {
const realityData = new ITwinRealityData(realityDataAccessClientForTest);
realityData.displayName = "iTwinjs RealityData Client move test";
realityData.classification = "Undefined";
realityData.type = "3MX";
const realityDataAdded =
await realityDataAccessClientForTest.createRealityData(
accessToken,
iTwinId,
realityData,
);
chai.assert(realityDataAdded.id);
try {
// move the reality data to another iTwin
const movedRealityData =
await realityDataAccessClientForTest.moveRealityData(
accessToken,
realityDataAdded.id,
iTwinId2,
);
chai.assert(movedRealityData);
// get itwins
const itwins = await realityDataAccessClientForTest.getRealityDataITwins(
accessToken,
realityDataAdded.id,
);
chai.assert(itwins.length === 1);
chai.assert(itwins[0] === iTwinId2);
} finally {
// delete the moved reality data
chai.assert(
await realityDataAccessClientForTest.deleteRealityData(
accessToken,
realityDataAdded.id,
),
);
}
});
});
describe("RealityServicesClient Errors (#integration)", () => {
let iTwinId: GuidString;
const nonExistentRealityDataId: string =
"f2065aea-5dcd-49e2-9077-000000000000";
let accessToken: AccessToken;
before(async () => {
accessToken = await TestConfig.getAccessToken();
iTwinId = TestConfig.integrationTestsITwinId;
chai.assert.isDefined(iTwinId);
});
it("should throw a 422 error when Reality Data ID is not a valid Guid", async () => {
const rdId = "f2065aea-5dcd-49e2-9077-xxxxxxxxxxxx";
try {
await realityDataAccessClientForTest.getRealityData(
accessToken,
iTwinId,
rdId,
);
} catch (errorResponse: any) {
chai.assert(
errorResponse.errorNumber === 422,
`Error code should be 422. It is ${errorResponse.errorNumber}.`,
);
return;
}
chai.assert(false, "getRealityData should throw an error.");
});
it("should throw a 404 error when Reality Data ID does not exist.", async () => {
try {
await realityDataAccessClientForTest.getRealityData(
accessToken,
iTwinId,
nonExistentRealityDataId,
);
} catch (errorResponse: any) {
chai.assert(
errorResponse.errorNumber === 404,
`Error code should be 404. It is ${errorResponse.errorNumber}.`,
);
return;
}
chai.assert(false, "getRealityData should throw an error.");
});
it("should throw a 422 error when the iTwinId is invalid (getting all reality data in a iTwin)", async () => {
const invalidITwinId = "xxxxxxxx-cc9f-4de9-af87-f834002ca19e";
try {
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
invalidITwinId,
undefined,
);
} catch (errorResponse: any) {
chai.assert(
errorResponse.errorNumber === 422,
`Error message should be 422. It is ${errorResponse.errorNumber}.`,
);
return;
}
chai.assert(false, "getRealityDatas should throw an error.");
});
it("should throw a 422 error when the top parameter is > 500 (getting all reality data in a iTwin)", async () => {
const realityDataQueryCriteria: RealityDataQueryCriteria = {
top: 501,
};
try {
await realityDataAccessClientForTest.getRealityDatas(
accessToken,
iTwinId,
realityDataQueryCriteria,
);
} catch (errorResponse: any) {
chai.assert(
errorResponse.errorNumber === 422,
`Error code should be 422. It is ${errorResponse.errorNumber}.`,
);
return;
}
chai.assert(
false,
"getRealityDatas should throw an error when top parameter is > 500.",
);
});
it("should throw a 422 error when required properties are missing (creating a reality data)", async () => {
// displayName is missing
const realityData = new ITwinRealityData(realityDataAccessClientForTest);
realityData.dataset = "Test Dataset for iTwinjs";
realityData.description = "Dummy description for a test reality data";
realityData.type = "3MX";
realityData.classification = "Undefined";
try {
await realityDataAccessClientForTest.createRealityData(
accessToken,
iTwinId,
realityData,
);
} catch (errorResponse: any) {
chai.assert(
errorResponse.errorNumber === 422,
`Error code should be 422. It is ${errorResponse.errorNumber}.`,
);
return;
}
chai.assert(false, "createRealityData should throw an error.");
});
it("should throw a 404 error when reality data id does not exist (modifying a reality data)", async () => {
const realityData = new ITwinRealityData(realityDataAccessClientForTest);
realityData.id = nonExistentRealityDataId;
realityData.displayName = "MODIFIED iTwinjs RealityData";
realityData.dataset = "Test Dataset for iTwinjs";
realityData.description = "Dummy description for a test reality data";
realityData.type = "3MX";
realityData.classification = "Undefined";
try {
await realityDataAccessClientForTest.modifyRealityData(
accessToken,
iTwinId,
realityData,
);
} catch (errorResponse: any) {
chai.assert(
errorResponse.errorNumber === 404,
`Error code should be 404. It is ${errorResponse.errorNumber}.`,
);
return;
}
chai.assert(false, "createRealityData should throw an error.");
});
it("should throw a 404 error when reality data id does not exist (deleting a reality data)", async () => {
try {
await realityDataAccessClientForTest.deleteRealityData(
accessToken,
nonExistentRealityDataId,
);
} catch (errorResponse: any) {
chai.assert(
errorResponse.errorNumber === 404,
`Error code should be 404. It is ${errorResponse.errorNumber}.`,
);
return;
}
chai.assert(false, "deleteRealityData should throw an error.");
});
it("should throw a 404 error when reality data id does not exist (associate a reality data)", async () => {
try {
await realityDataAccessClientForTest.associateRealityData(
accessToken,
iTwinId,
nonExistentRealityDataId,
);
} catch (errorResponse: any) {
chai.assert(
errorResponse.errorNumber === 404,
`Error code should be 404. It is ${errorResponse.errorNumber}.`,
);
return;
}
chai.assert(false, "associateRealityData should throw an error.");
});
it("should throw a 404 error when reality data id does not exist (dissociate a reality data)", async () => {
try {
await realityDataAccessClientForTest.dissociateRealityData(
accessToken,
iTwinId,
nonExistentRealityDataId,
);
} catch (errorResponse: any) {
chai.assert(
errorResponse.errorNumber === 404,
`Error code should be 404. It is ${errorResponse.errorNumber}.`,
);
return;
}
chai.assert(false, "dissociateRealityData should throw an error.");
});
it("should throw a 404 error when reality data id does not exist (moving a reality data)", async () => {
try {
await realityDataAccessClientForTest.moveRealityData(
accessToken,
nonExistentRealityDataId,
iTwinId,
);
} catch (errorResponse: any) {
chai.assert(
errorResponse.errorNumber === 404,
`Error code should be 404. It is ${errorResponse.errorNumber}.`,
);
return;
}
chai.assert(false, "moveRealityData should throw an error.");
});
});