import { AccessToken , BentleyError , BentleyStatus , GuidString } from "@itwin/core-bentley" ; import { Project as ITwin } from "@itwin/projects-client" ; import { AuthorizationClient , BriefcaseId , ChangesetIndexAndId , IModelVersion } from "@itwin/core-common" ; import { FrontendHubAccess , IModelIdArg } from "@itwin/core-frontend" ; import { BriefcaseQuery , ChangeSet , ChangeSetQuery , IModelBankClient , IModelBankFileSystemITwinClient , IModelQuery , VersionQuery } from "@bentley/imodelbank-client" ; import { AccessTokenAdapter , FrontendIModelsAccess } from "@itwin/imodels-access-frontend" ; import { IModelsClient as AuthorIModelsClient , Briefcase , GetBriefcaseListParams , GetIModelListParams , IModelScopedOperationParams , MinimalIModel , ReleaseBriefcaseParams , SPECIAL_VALUES_ME , toArray } from "@itwin/imodels-client-authoring" ; import { IModelsClient as FrontendIModelsClient } from "@itwin/imodels-client-management" ; import { ITwinAccessClientWrapper } from "../../common/ITwinAccessClientWrapper" ; export interface IModelNameArg { readonly iModelName : string ; readonly iTwinId : GuidString ; readonly accessToken : AccessToken ; } export interface BriefcaseIdArg extends IModelIdArg { readonly briefcaseId : BriefcaseId ; } export interface TestFrontendHubAccess extends FrontendHubAccess { /** Get the iModelId of an iModel by name. Undefined if no iModel with that name exists.  */ queryIModelByName ( arg ) : Promise < GuidString | undefined > ; getMyBriefcaseIds ( arg ) : Promise < number [ ] > ; releaseBriefcase ( arg ) : Promise < void > ; } export class TestHubFrontend extends FrontendIModelsAccess { private getScopedOperationParams ( arg ) { return { authorization : AccessTokenAdapter . toAuthorizationCallback ( arg . accessToken ) , iModelId : arg . iModelId } ; } public async queryIModelByName ( arg ) { const getIModelListParams = { authorization : AccessTokenAdapter . toAuthorizationCallback ( arg . accessToken ) , urlParams : { projectId : arg . iTwinId , name : arg . iModelName } } ; const iModelsIterator = this . _iModelsClient . iModels . getMinimalList ( getIModelListParams ) ; const iModels = await toArray ( iModelsIterator ) ; return iModels . length === 0 ? undefined : iModels [ 0 ] . id ; } public async getMyBriefcaseIds ( arg ) { const getBriefcaseListParams = { ... this . getScopedOperationParams ( arg ) , urlParams : { ownerId : SPECIAL_VALUES_ME } } ; const briefcasesIterator = this . _iModelsClient . briefcases . getRepresentationList ( getBriefcaseListParams ) ; const briefcases = await toArray ( briefcasesIterator ) ; const briefcaseIds = briefcases . map ( ( briefcase ) => briefcase . briefcaseId ) ; return briefcaseIds ; } public async releaseBriefcase ( arg ) { const releaseBriefcaseParams = { ... this . getScopedOperationParams ( arg ) , briefcaseId : arg . briefcaseId } ; const iModelClient = new AuthorIModelsClient ( { api : { baseUrl : `https://${ process . env . IMJS_URL_PREFIX ?? "" }api.bentley.com/imodels` } } ) ; return iModelClient . briefcases . release ( releaseBriefcaseParams ) ; } } export class IModelBankFrontend implements TestFrontendHubAccess { private _hubClient : IModelBankClient ; constructor ( orchestratorUrl ) { this . _hubClient = new IModelBankClient ( orchestratorUrl , undefined ) ; } private async _getChangesetFromId ( arg ) { const changesets = await this . _hubClient . changeSets . get ( arg . accessToken , arg . iModelId , new ChangeSetQuery ( ) . byId ( arg . changesetId ) ) ; if ( ! changesets [ 0 ] || ! changesets [ 0 ] . index || ! changesets [ 0 ] . id ) throw new BentleyError ( BentleyStatus . ERROR , `Changeset ${ arg . changesetId } not found` ) ; return { index : + changesets [ 0 ] . index , id : changesets [ 0 ] . id } ; } public async getLatestChangeset ( arg ) { const changeSets = await this . _hubClient . changeSets . get ( arg . accessToken , arg . iModelId , new ChangeSetQuery ( ) . top ( 1 ) . latest ( ) ) ; if ( ! changeSets [ 0 ] || ! changeSets [ 0 ] . index || ! changeSets [ 0 ] . id ) return { index : 0 , id : "" } ; return { index : + changeSets [ 0 ] . index , id : changeSets [ 0 ] . id } ; } public async getChangesetFromVersion ( arg ) { const version = arg . version ; if ( version . isFirst ) return { index : 0 , id : "" } ; const asOfChangeSetId = version . getAsOfChangeSet ( ) ; if ( asOfChangeSetId ) return this . _getChangesetFromId ( { ... arg , changesetId : asOfChangeSetId } ) ; const versionName = version . getName ( ) ; if ( versionName ) return this . getChangesetFromNamedVersion ( { ... arg , versionName } ) ; return this . getLatestChangeset ( arg ) ; } public async getChangesetFromNamedVersion ( arg ) { const versionQuery = arg . versionName ? new VersionQuery ( ) . select ( "ChangeSetId" ) . byName ( arg . versionName ) : new VersionQuery ( ) . top ( 1 ) ; const versions = await this . _hubClient . versions . get ( arg . accessToken , arg . iModelId , versionQuery ) ; if ( ! versions [ 0 ] || ! versions [ 0 ] . changeSetIndex || ! versions [ 0 ] . changeSetId ) throw new BentleyError ( BentleyStatus . ERROR , `Named version ${ arg . versionName ?? "" } not found` ) ; return { index : versions [ 0 ] . changeSetIndex , id : versions [ 0 ] . changeSetId } ; } /** Get the iModelId of an iModel by name. Undefined if no iModel with that name exists.  */ public async queryIModelByName ( arg ) { const iModels = await this . _hubClient . iModels . get ( arg . accessToken , arg . iTwinId , new IModelQuery ( ) . byName ( arg . iModelName ) ) ; return iModels . length === 0 ? undefined : iModels [ 0 ] . id ! ; } public async getMyBriefcaseIds ( arg ) { const myHubBriefcases = await this . _hubClient . briefcases . get ( arg . accessToken , arg . iModelId , new BriefcaseQuery ( ) . ownedByMe ( ) . selectDownloadUrl ( ) ) ; const myBriefcaseIds = [ ] ; for ( const hubBc of myHubBriefcases ) myBriefcaseIds . push ( hubBc . briefcaseId ! ) ; return myBriefcaseIds ; } public async releaseBriefcase ( arg ) { await this . _hubClient . briefcases . delete ( arg . accessToken , arg . iModelId , arg . briefcaseId ) ; } } /** Defines a base set of the set of Simple base interface for the client implementations that will be passed to the IModelApp. */ export interface ITwinPlatformAbstraction { readonly hubAccess : TestFrontendHubAccess ; readonly iTwinMgr : ITwinAccessClientWrapper ; readonly authClient ? : AuthorizationClient ; } /** A convenient wrapper that includes a default set of clients necessary to configure an iTwin.js application for the iTwin Platform. */ export class ITwinPlatformCloudEnv implements ITwinPlatformAbstraction { public readonly iTwinMgr = new ITwinAccessClientWrapper ( ) ; public readonly hubAccess : TestFrontendHubAccess ; public readonly authClient ? : AuthorizationClient ; public constructor ( authClient ? ) { const iModelClient = new FrontendIModelsClient ( { api : { baseUrl : `https://${ process . env . IMJS_URL_PREFIX ?? "" }api.bentley.com/imodels` } } ) ; this . hubAccess = new TestHubFrontend ( iModelClient ) ; this . authClient = authClient ; } } /** A convenient wrapper that includes a default set of clients necessary to configure an iTwin.js application for the iTwin Stack. */ export class ITwinStackCloudEnv implements ITwinPlatformAbstraction { public readonly iTwinMgr : IModelBankFileSystemITwinClient ; public readonly hubAccess : TestFrontendHubAccess ; public readonly authClient ? : AuthorizationClient ; public constructor ( orchestratorUrl , authClient ? ) { this . hubAccess = new IModelBankFrontend ( orchestratorUrl ) ; this . iTwinMgr = new IModelBankFileSystemITwinClient ( orchestratorUrl ) ; this . authClient = authClient ; } public async bootstrapIModelBankProject ( token , iTwinName ) { const iTwin = await this . iTwinMgr . getITwinByName ( token , iTwinName ) ; if ( iTwin !== undefined ) await this . iTwinMgr . deleteITwin ( token , iTwin . id ) ; await this . iTwinMgr . createITwin ( token , iTwinName ) ; } }