UNPKG

@itwin/reality-data-client

Version:

HTTP Client for the iTwin Platform Reality Management APIs

1,416 lines (1,266 loc) 47.2 kB
/*--------------------------------------------------------------------------------------------- * 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."); }); });