import { assert , expect } from "chai" ; import * as path from "path" ; import * as Semver from "semver" ; import * as sinon from "sinon" ; import { DbResult , Guid , Id64 , Id64String , Logger , LogLevel , OpenMode } from "@itwin/core-bentley" ; import { Point3d , Range3d , StandardViewIndex , Transform , YawPitchRollAngles } from "@itwin/core-geometry" ; import { CategorySelector , DisplayStyle3d , DocumentListModel , Drawing , DrawingCategory , DrawingGraphic , DrawingModel , ECSqlStatement , Element , ElementMultiAspect , ElementOwnsChildElements , ElementOwnsExternalSourceAspects , ElementOwnsUniqueAspect , ElementRefersToElements , ElementUniqueAspect , ExternalSourceAspect , GenericPhysicalMaterial , GeometricElement , IModelCloneContext , IModelDb , IModelHost , IModelJsFs , IModelSchemaLoader , InformationRecordModel , InformationRecordPartition , LinkElement , Model , ModelSelector , OrthographicViewDefinition , PhysicalModel , PhysicalObject , PhysicalPartition , PhysicalType , Relationship , RepositoryLink , Schema , SnapshotDb , SpatialCategory , StandaloneDb , SubCategory , Subject , } from "@itwin/core-backend" ; import { ExtensiveTestScenario , IModelTestUtils , KnownTestLocations } from "@itwin/core-backend/lib/cjs/test" ; import { AxisAlignedBox3d , BriefcaseIdValue , Code , CodeScopeSpec , CodeSpec , ColorDef , CreateIModelProps , DefinitionElementProps , ExternalSourceAspectProps , IModel , IModelError , PhysicalElementProps , Placement3d , QueryRowFormat , } from "@itwin/core-common" ; import { IModelExporter , IModelExportHandler , IModelTransformer , TransformerLoggerCategory } from "../../core-transformer" ; import { ClassCounter , FilterByViewTransformer , IModelToTextFileExporter , IModelTransformer3d , IModelTransformerTestUtils , PhysicalModelConsolidator , RecordingIModelImporter , TestIModelTransformer , TransformerExtensiveTestScenario , } from "../IModelTransformerUtils" ; describe ( "IModelTransformer" , ( ) => { const outputDir = path . join ( KnownTestLocations . outputDir , "IModelTransformer" ) ; before ( async ( ) => { if ( ! IModelJsFs . existsSync ( KnownTestLocations . outputDir ) ) { IModelJsFs . mkdirSync ( KnownTestLocations . outputDir ) ; } if ( ! IModelJsFs . existsSync ( outputDir ) ) { IModelJsFs . mkdirSync ( outputDir ) ; } if ( false ) { Logger . initializeToConsole ( ) ; Logger . setLevelDefault ( LogLevel . Error ) ; Logger . setLevel ( TransformerLoggerCategory . IModelExporter , LogLevel . Trace ) ; Logger . setLevel ( TransformerLoggerCategory . IModelImporter , LogLevel . Trace ) ; Logger . setLevel ( TransformerLoggerCategory . IModelTransformer , LogLevel . Trace ) ; } } ) ; it ( "should transform changes from source to target" , async ( ) => { const sourceDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "TestIModelTransformer-Source.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbFile , { rootSubject : { name : "TestIModelTransformer-Source" } } ) ; await ExtensiveTestScenario . prepareDb ( sourceDb ) ; ExtensiveTestScenario . populateDb ( sourceDb ) ; sourceDb . saveChanges ( ) ; const targetDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "TestIModelTransformer-Target.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbFile , { rootSubject : { name : "TestIModelTransformer-Target" } } ) ; await TransformerExtensiveTestScenario . prepareTargetDb ( targetDb ) ; targetDb . saveChanges ( ) ; const numSourceUniqueAspects = count ( sourceDb , ElementUniqueAspect . classFullName ) ; const numSourceMultiAspects = count ( sourceDb , ElementMultiAspect . classFullName ) ; const numSourceRelationships = count ( sourceDb , ElementRefersToElements . classFullName ) ; assert . isAtLeast ( numSourceUniqueAspects , 1 ) ; assert . isAtLeast ( numSourceMultiAspects , 1 ) ; assert . isAtLeast ( numSourceRelationships , 1 ) ; if ( true ) { Logger . logInfo ( TransformerLoggerCategory . IModelTransformer , "==============" ) ; Logger . logInfo ( TransformerLoggerCategory . IModelTransformer , "Initial Import" ) ; Logger . logInfo ( TransformerLoggerCategory . IModelTransformer , "==============" ) ; const targetImporter = new RecordingIModelImporter ( targetDb ) ; const transformer = new TestIModelTransformer ( sourceDb , targetImporter ) ; assert . isTrue ( transformer . context . isBetweenIModels ) ; await transformer . processAll ( ) ; assert . isAtLeast ( targetImporter . numModelsInserted , 1 ) ; assert . equal ( targetImporter . numModelsUpdated , 0 ) ; assert . isAtLeast ( targetImporter . numElementsInserted , 1 ) ; assert . isAtLeast ( targetImporter . numElementsUpdated , 1 ) ; assert . equal ( targetImporter . numElementsDeleted , 0 ) ; assert . isAtLeast ( targetImporter . numElementAspectsInserted , 1 ) ; assert . equal ( targetImporter . numElementAspectsUpdated , 0 ) ; assert . isAtLeast ( targetImporter . numRelationshipsInserted , 1 ) ; assert . equal ( targetImporter . numRelationshipsUpdated , 0 ) ; assert . isAtLeast ( count ( targetDb , ElementRefersToElements . classFullName ) , 1 ) ; assert . isAtLeast ( count ( targetDb , InformationRecordPartition . classFullName ) , 1 ) ; assert . isAtLeast ( count ( targetDb , InformationRecordModel . classFullName ) , 1 ) ; assert . isAtLeast ( count ( targetDb , "ExtensiveTestScenarioTarget:PhysicalPartitionIsTrackedByRecords" ) , 1 ) ; assert . isAtLeast ( count ( targetDb , "ExtensiveTestScenarioTarget:AuditRecord" ) , 1 ) ; assert . equal ( 3 , count ( targetDb , "ExtensiveTestScenarioTarget:TargetInformationRecord" ) ) ; targetDb . saveChanges ( ) ; TransformerExtensiveTestScenario . assertTargetDbContents ( sourceDb , targetDb ) ; transformer . context . dump ( `${ targetDbFile }.context.txt` ) ; transformer . dispose ( ) ; } const numTargetElements = count ( targetDb , Element . classFullName ) ; const numTargetUniqueAspects = count ( targetDb , ElementUniqueAspect . classFullName ) ; const numTargetMultiAspects = count ( targetDb , ElementMultiAspect . classFullName ) ; const numTargetExternalSourceAspects = count ( targetDb , ExternalSourceAspect . classFullName ) ; const numTargetRelationships = count ( targetDb , ElementRefersToElements . classFullName ) ; assert . isAtLeast ( numTargetUniqueAspects , 1 ) ; assert . isAtLeast ( numTargetMultiAspects , 1 ) ; assert . isAtLeast ( numTargetRelationships , 1 ) ; if ( true ) { const exportFileName = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "TestIModelTransformer-Source-Export.txt" ) ; assert . isFalse ( IModelJsFs . existsSync ( exportFileName ) ) ; const exporter = new IModelToTextFileExporter ( sourceDb , exportFileName ) ; await exporter . export ( ) ; assert . isTrue ( IModelJsFs . existsSync ( exportFileName ) ) ; const classCountsFileName = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "TestIModelTransformer-Source-Counts.txt" ) ; assert . isFalse ( IModelJsFs . existsSync ( classCountsFileName ) ) ; const classCounter = new ClassCounter ( sourceDb , classCountsFileName ) ; await classCounter . count ( ) ; assert . isTrue ( IModelJsFs . existsSync ( classCountsFileName ) ) ; } if ( true ) { Logger . logInfo ( TransformerLoggerCategory . IModelTransformer , "" ) ; Logger . logInfo ( TransformerLoggerCategory . IModelTransformer , "=================" ) ; Logger . logInfo ( TransformerLoggerCategory . IModelTransformer , "Reimport (no-op)" ) ; Logger . logInfo ( TransformerLoggerCategory . IModelTransformer , "=================" ) ; const targetImporter = new RecordingIModelImporter ( targetDb ) ; const transformer = new TestIModelTransformer ( sourceDb , targetImporter ) ; await transformer . processAll ( ) ; assert . equal ( targetImporter . numModelsInserted , 0 ) ; assert . equal ( targetImporter . numModelsUpdated , 0 ) ; assert . equal ( targetImporter . numElementsInserted , 0 ) ; assert . equal ( targetImporter . numElementsUpdated , 0 ) ; assert . equal ( targetImporter . numElementsDeleted , 0 ) ; assert . equal ( targetImporter . numElementAspectsInserted , 0 ) ; assert . equal ( targetImporter . numElementAspectsUpdated , 0 ) ; assert . equal ( targetImporter . numRelationshipsInserted , 0 ) ; assert . equal ( targetImporter . numRelationshipsUpdated , 0 ) ; assert . equal ( targetImporter . numRelationshipsDeleted , 0 ) ; assert . equal ( numTargetElements , count ( targetDb , Element . classFullName ) , "Second import should not add elements" ) ; assert . equal ( numTargetExternalSourceAspects , count ( targetDb , ExternalSourceAspect . classFullName ) , "Second import should not add aspects" ) ; assert . equal ( numTargetRelationships , count ( targetDb , ElementRefersToElements . classFullName ) , "Second import should not add relationships" ) ; assert . equal ( 3 , count ( targetDb , "ExtensiveTestScenarioTarget:TargetInformationRecord" ) ) ; transformer . dispose ( ) ; } if ( true ) { ExtensiveTestScenario . updateDb ( sourceDb ) ; sourceDb . saveChanges ( ) ; Logger . logInfo ( TransformerLoggerCategory . IModelTransformer , "" ) ; Logger . logInfo ( TransformerLoggerCategory . IModelTransformer , "===============================" ) ; Logger . logInfo ( TransformerLoggerCategory . IModelTransformer , "Reimport after sourceDb update" ) ; Logger . logInfo ( TransformerLoggerCategory . IModelTransformer , "===============================" ) ; const targetImporter = new RecordingIModelImporter ( targetDb ) ; const transformer = new TestIModelTransformer ( sourceDb , targetImporter ) ; await transformer . processAll ( ) ; assert . equal ( targetImporter . numModelsInserted , 0 ) ; assert . equal ( targetImporter . numModelsUpdated , 0 ) ; assert . equal ( targetImporter . numElementsInserted , 1 ) ; assert . equal ( targetImporter . numElementsUpdated , 5 ) ; assert . equal ( targetImporter . numElementsDeleted , 2 ) ; assert . equal ( targetImporter . numElementAspectsInserted , 0 ) ; assert . equal ( targetImporter . numElementAspectsUpdated , 2 ) ; assert . equal ( targetImporter . numRelationshipsInserted , 2 ) ; assert . equal ( targetImporter . numRelationshipsUpdated , 1 ) ; assert . equal ( targetImporter . numRelationshipsDeleted , 1 ) ; targetDb . saveChanges ( ) ; ExtensiveTestScenario . assertUpdatesInDb ( targetDb ) ; assert . equal ( numTargetRelationships + targetImporter . numRelationshipsInserted - targetImporter . numRelationshipsDeleted , count ( targetDb , ElementRefersToElements . classFullName ) ) ; assert . equal ( 2 , count ( targetDb , "ExtensiveTestScenarioTarget:TargetInformationRecord" ) ) ; transformer . dispose ( ) ; } IModelTestUtils . dumpIModelInfo ( sourceDb ) ; IModelTestUtils . dumpIModelInfo ( targetDb ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it ( "should synchronize changes from master to branch and back" , async ( ) => { const masterDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "Master.bim" ) ; const masterDb = SnapshotDb . createEmpty ( masterDbFile , { rootSubject : { name : "Branching Workflow" } , createClassViews : true } ) ; await ExtensiveTestScenario . prepareDb ( masterDb ) ; ExtensiveTestScenario . populateDb ( masterDb ) ; masterDb . saveChanges ( ) ; const branchDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "Branch.bim" ) ; const branchDb = SnapshotDb . createFrom ( masterDb , branchDbFile , { createClassViews : true } ) ; const numMasterElements = count ( masterDb , Element . classFullName ) ; const numMasterRelationships = count ( masterDb , ElementRefersToElements . classFullName ) ; assert . isAtLeast ( numMasterElements , 12 ) ; assert . isAtLeast ( numMasterRelationships , 1 ) ; assert . equal ( numMasterElements , count ( branchDb , Element . classFullName ) ) ; assert . equal ( numMasterRelationships , count ( branchDb , ElementRefersToElements . classFullName ) ) ; assert . equal ( 0 , count ( branchDb , ExternalSourceAspect . classFullName ) ) ; const masterToBranchTransformer = new IModelTransformer ( masterDb , branchDb , { wasSourceIModelCopiedToTarget : true } ) ; await masterToBranchTransformer . processAll ( ) ; masterToBranchTransformer . dispose ( ) ; branchDb . saveChanges ( ) ; assert . equal ( numMasterElements , count ( branchDb , Element . classFullName ) ) ; assert . equal ( numMasterRelationships , count ( branchDb , ElementRefersToElements . classFullName ) ) ; assert . isAtLeast ( count ( branchDb , ExternalSourceAspect . classFullName ) , numMasterElements + numMasterRelationships - 1 ) ; const sql = `SELECT aspect.Identifier,aspect.Element.Id FROM ${ ExternalSourceAspect . classFullName } aspect WHERE aspect.Kind=:kind` ; branchDb . withPreparedStatement ( sql , ( statement ) : void => { statement . bindString ( "kind" , ExternalSourceAspect . Kind . Element ) ; while ( DbResult . BE_SQLITE_ROW === statement . step ( ) ) { const masterElementId = statement . getValue ( 0 ) . getString ( ) ; const branchElementId = statement . getValue ( 1 ) . getId ( ) ; assert . equal ( masterElementId , branchElementId ) ; } } ) ; ExtensiveTestScenario . updateDb ( branchDb ) ; ExtensiveTestScenario . assertUpdatesInDb ( branchDb ) ; branchDb . saveChanges ( ) ; const numBranchElements = count ( branchDb , Element . classFullName ) ; const numBranchRelationships = count ( branchDb , ElementRefersToElements . classFullName ) ; assert . notEqual ( numBranchElements , numMasterElements ) ; assert . notEqual ( numBranchRelationships , numMasterRelationships ) ; const branchToMasterTransformer = new IModelTransformer ( branchDb , masterDb , { isReverseSynchronization : true , noProvenance : true } ) ; await branchToMasterTransformer . processAll ( ) ; branchToMasterTransformer . dispose ( ) ; masterDb . saveChanges ( ) ; ExtensiveTestScenario . assertUpdatesInDb ( masterDb , false ) ; assert . equal ( numBranchElements , count ( masterDb , Element . classFullName ) - 2 ) ; assert . equal ( numBranchRelationships , count ( masterDb , ElementRefersToElements . classFullName ) - 1 ) ; assert . equal ( 0 , count ( masterDb , ExternalSourceAspect . classFullName ) ) ; masterDb . close ( ) ; branchDb . close ( ) ; } ) ; function count ( iModelDb , classFullName ) { return iModelDb . withPreparedStatement ( `SELECT COUNT(*) FROM ${ classFullName }` , ( statement ) : number => { return DbResult . BE_SQLITE_ROW === statement . step ( ) ? statement . getValue ( 0 ) . getInteger ( ) : 0 ; } ) ; } it ( "should import everything below a Subject" , async ( ) => { const sourceDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "SourceImportSubject.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbFile , { rootSubject : { name : "SourceImportSubject" } } ) ; await ExtensiveTestScenario . prepareDb ( sourceDb ) ; ExtensiveTestScenario . populateDb ( sourceDb ) ; const sourceSubjectId = sourceDb . elements . queryElementIdByCode ( Subject . createCode ( sourceDb , IModel . rootSubjectId , "Subject" ) ) ! ; assert . isTrue ( Id64 . isValidId64 ( sourceSubjectId ) ) ; sourceDb . saveChanges ( ) ; const targetDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "TargetImportSubject.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbFile , { rootSubject : { name : "TargetImportSubject" } } ) ; await TransformerExtensiveTestScenario . prepareTargetDb ( targetDb ) ; const targetSubjectId = Subject . insert ( targetDb , IModel . rootSubjectId , "Target Subject" , "Target Subject Description" ) ; assert . isTrue ( Id64 . isValidId64 ( targetSubjectId ) ) ; targetDb . saveChanges ( ) ; const transformer = new TestIModelTransformer ( sourceDb , targetDb ) ; await transformer . processFonts ( ) ; await transformer . processSubject ( sourceSubjectId , targetSubjectId ) ; await transformer . processRelationships ( ElementRefersToElements . classFullName ) ; transformer . dispose ( ) ; targetDb . saveChanges ( ) ; TransformerExtensiveTestScenario . assertTargetDbContents ( sourceDb , targetDb , "Target Subject" ) ; const targetSubject = targetDb . elements . getElement < Subject > ( targetSubjectId ) ; assert . equal ( targetSubject . description , "Target Subject Description" ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it . skip ( "should clone Model within same iModel" , async ( ) => { const iModelFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "CloneModel.bim" ) ; const iModelDb = SnapshotDb . createEmpty ( iModelFile , { rootSubject : { name : "CloneModel" } } ) ; await ExtensiveTestScenario . prepareDb ( iModelDb ) ; ExtensiveTestScenario . populateDb ( iModelDb ) ; const sourceSubjectId = iModelDb . elements . queryElementIdByCode ( Subject . createCode ( iModelDb , IModel . rootSubjectId , "Subject" ) ) ! ; assert . isTrue ( Id64 . isValidId64 ( sourceSubjectId ) ) ; const targetSubjectId = Subject . insert ( iModelDb , IModel . rootSubjectId , "Target Subject" ) ; assert . isTrue ( Id64 . isValidId64 ( targetSubjectId ) ) ; iModelDb . saveChanges ( ) ; const transformer = new IModelTransformer ( iModelDb , iModelDb ) ; await transformer . processSubject ( sourceSubjectId , targetSubjectId ) ; transformer . dispose ( ) ; iModelDb . saveChanges ( ) ; iModelDb . close ( ) ; } ) ; note it ( "should clone test file" , async ( ) => { const sourceFileName = IModelTestUtils . resolveAssetFile ( "CompatibilityTestSeed.bim" ) ; const sourceDb = SnapshotDb . openFile ( sourceFileName ) ; const numSourceElements = count ( sourceDb , Element . classFullName ) ; assert . exists ( sourceDb ) ; assert . isAtLeast ( numSourceElements , 12 ) ; const targetDbFile = path . join ( KnownTestLocations . outputDir , "IModelTransformer" , "Clone-Target.bim" ) ; if ( IModelJsFs . existsSync ( targetDbFile ) ) { IModelJsFs . removeSync ( targetDbFile ) ; } const targetDbProps = { rootSubject : { name : `Cloned target of ${ sourceDb . elements . getRootSubject ( ) . code . value }` } , ecefLocation : sourceDb . ecefLocation } ; const targetDb = SnapshotDb . createEmpty ( targetDbFile , targetDbProps ) ; assert . exists ( targetDb ) ; const transformer = new IModelTransformer ( sourceDb , targetDb ) ; await transformer . processSchemas ( ) ; await transformer . processAll ( ) ; transformer . dispose ( ) ; const numTargetElements = count ( targetDb , Element . classFullName ) ; assert . isAtLeast ( numTargetElements , numSourceElements ) ; assert . deepEqual ( sourceDb . ecefLocation , targetDb . ecefLocation ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it ( "should include source provenance" , async ( ) => { const sourceDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "SourceProvenance.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbFile , { rootSubject : { name : "Source Provenance Test" } } ) ; const sourceRepositoryId = IModelTestUtils . insertRepositoryLink ( sourceDb , "master.dgn" , "https://test.bentley.com/folder/master.dgn" , "DGN" ) ; const sourceExternalSourceId = IModelTestUtils . insertExternalSource ( sourceDb , sourceRepositoryId , "Default Model" ) ; const sourceCategoryId = SpatialCategory . insert ( sourceDb , IModel . dictionaryId , "SpatialCategory" , { color : ColorDef . green . toJSON ( ) } ) ; const sourceModelId = PhysicalModel . insert ( sourceDb , IModel . rootSubjectId , "Physical" ) ; for ( const x of [ 1 , 2 , 3 ] ) { const physicalObjectProps = { classFullName : PhysicalObject . classFullName , model : sourceModelId , category : sourceCategoryId , code : Code . createEmpty ( ) , userLabel : `PhysicalObject(${ x })` , geom : IModelTestUtils . createBox ( Point3d . create ( 1 , 1 , 1 ) ) , placement : Placement3d . fromJSON ( { origin : { x } , angles : { } } ) } ; const physicalObjectId = sourceDb . elements . insertElement ( physicalObjectProps ) ; const aspectProps = { classFullName : ExternalSourceAspect . classFullName , element : { id : physicalObjectId , relClassName : ElementOwnsExternalSourceAspects . classFullName } , scope : { id : sourceExternalSourceId } , source : { id : sourceExternalSourceId } , identifier : `ID${ x }` , kind : ExternalSourceAspect . Kind . Element } ; sourceDb . elements . insertAspect ( aspectProps ) ; } sourceDb . saveChanges ( ) ; const targetDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "SourceProvenance-Target.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbFile , { rootSubject : { name : "Source Provenance Test (Target)" } } ) ; const transformer = new IModelTransformer ( sourceDb , targetDb , { includeSourceProvenance : true } ) ; await transformer . processAll ( ) ; targetDb . saveChanges ( ) ; assert . equal ( 1 , count ( sourceDb , RepositoryLink . classFullName ) ) ; const targetRepositoryId = targetDb . elements . queryElementIdByCode ( LinkElement . createCode ( targetDb , IModel . repositoryModelId , "master.dgn" ) ) ! ; assert . isTrue ( Id64 . isValidId64 ( targetRepositoryId ) ) ; const targetExternalSourceId = IModelTestUtils . queryByUserLabel ( targetDb , "Default Model" ) ; assert . isTrue ( Id64 . isValidId64 ( targetExternalSourceId ) ) ; const targetCategoryId = targetDb . elements . queryElementIdByCode ( SpatialCategory . createCode ( targetDb , IModel . dictionaryId , "SpatialCategory" ) ) ! ; assert . isTrue ( Id64 . isValidId64 ( targetCategoryId ) ) ; const targetPhysicalObjectIds = [ IModelTestUtils . queryByUserLabel ( targetDb , "PhysicalObject(1)" ) , IModelTestUtils . queryByUserLabel ( targetDb , "PhysicalObject(2)" ) , IModelTestUtils . queryByUserLabel ( targetDb , "PhysicalObject(3)" ) , ] ; for ( const targetPhysicalObjectId of targetPhysicalObjectIds ) { assert . isTrue ( Id64 . isValidId64 ( targetPhysicalObjectId ) ) ; const physicalObject = targetDb . elements . getElement < PhysicalObject > ( targetPhysicalObjectId , PhysicalObject ) ; assert . equal ( physicalObject . category , targetCategoryId ) ; const aspects = targetDb . elements . getAspects ( targetPhysicalObjectId , ExternalSourceAspect . classFullName ) ; assert . equal ( 2 , aspects . length , "Expect original source provenance + provenance generated by IModelTransformer" ) ; for ( const aspect of aspects ) { const externalSourceAspect = aspect as ExternalSourceAspect ; if ( externalSourceAspect . scope . id === transformer . targetScopeElementId ) { assert . equal ( externalSourceAspect . kind , ExternalSourceAspect . Kind . Element ) ; } else { assert . equal ( externalSourceAspect . scope . id , targetExternalSourceId ) ; assert . equal ( externalSourceAspect . source ! . id , targetExternalSourceId ) ; assert . isTrue ( externalSourceAspect . identifier . startsWith ( "ID" ) ) ; assert . isTrue ( physicalObject . userLabel ! . includes ( externalSourceAspect . identifier [ 2 ] ) ) ; assert . equal ( externalSourceAspect . kind , ExternalSourceAspect . Kind . Element ) ; } } } transformer . dispose ( ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it ( "should transform 3d elements in target iModel" , async ( ) => { const sourceDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "Transform3d-Source.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbFile , { rootSubject : { name : "Transform3d-Source" } } ) ; const categoryId = SpatialCategory . insert ( sourceDb , IModel . dictionaryId , "SpatialCategory" , { color : ColorDef . green . toJSON ( ) } ) ; const sourceModelId = PhysicalModel . insert ( sourceDb , IModel . rootSubjectId , "Physical" ) ; const xArray = [ 1 , 3 , 5 , 7 , 9 ] ; const yArray = [ 0 , 2 , 4 , 6 , 8 ] ; for ( const x of xArray ) { for ( const y of yArray ) { const physicalObjectProps1 = { classFullName : PhysicalObject . classFullName , model : sourceModelId , category : categoryId , code : Code . createEmpty ( ) , userLabel : `PhysicalObject(${ x },${ y })` , geom : IModelTestUtils . createBox ( Point3d . create ( 1 , 1 , 1 ) ) , placement : Placement3d . fromJSON ( { origin : { x , y } , angles : { } } ) } ; sourceDb . elements . insertElement ( physicalObjectProps1 ) ; } } const sourceModel = sourceDb . models . getModel < PhysicalModel > ( sourceModelId ) ; const sourceModelExtents = sourceModel . queryExtents ( ) ; assert . deepEqual ( sourceModelExtents , new Range3d ( 1 , 0 , 0 , 10 , 9 , 1 ) ) ; const targetDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "Transform3d-Target.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbFile , { rootSubject : { name : "Transform3d-Target" } } ) ; const transform3d = Transform . createTranslation ( new Point3d ( 100 , 200 ) ) ; const transformer = new IModelTransformer3d ( sourceDb , targetDb , transform3d ) ; await transformer . processAll ( ) ; const targetModelId = transformer . context . findTargetElementId ( sourceModelId ) ; const targetModel = targetDb . models . getModel < PhysicalModel > ( targetModelId ) ; const targetModelExtents = targetModel . queryExtents ( ) ; assert . deepEqual ( targetModelExtents , new Range3d ( 101 , 200 , 0 , 110 , 209 , 1 ) ) ; assert . deepEqual ( targetModelExtents , transform3d . multiplyRange ( sourceModelExtents ) ) ; transformer . dispose ( ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it ( "should combine models" , async ( ) => { const sourceDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "source-separate-models.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbFile , { rootSubject : { name : "Separate Models" } } ) ; const sourceCategoryId = SpatialCategory . insert ( sourceDb , IModel . dictionaryId , "Category" , { } ) ; const sourceModelId1 = PhysicalModel . insert ( sourceDb , IModel . rootSubjectId , "M1" ) ; const sourceModelId2 = PhysicalModel . insert ( sourceDb , IModel . rootSubjectId , "M2" ) ; const elementProps11 = { classFullName : PhysicalObject . classFullName , model : sourceModelId1 , code : Code . createEmpty ( ) , userLabel : "PhysicalObject-M1-E1" , category : sourceCategoryId , geom : IModelTestUtils . createBox ( new Point3d ( 1 , 1 , 1 ) ) , placement : Placement3d . fromJSON ( { origin : { x : 1 , y : 1 } , angles : { } } ) } ; const sourceElementId11 = sourceDb . elements . insertElement ( elementProps11 ) ; const elementProps21 = { classFullName : PhysicalObject . classFullName , model : sourceModelId2 , code : Code . createEmpty ( ) , userLabel : "PhysicalObject-M2-E1" , category : sourceCategoryId , geom : IModelTestUtils . createBox ( new Point3d ( 2 , 2 , 2 ) ) , placement : Placement3d . fromJSON ( { origin : { x : 2 , y : 2 } , angles : { } } ) } ; const sourceElementId21 = sourceDb . elements . insertElement ( elementProps21 ) ; sourceDb . saveChanges ( ) ; assert . equal ( count ( sourceDb , PhysicalPartition . classFullName ) , 2 ) ; assert . equal ( count ( sourceDb , PhysicalModel . classFullName ) , 2 ) ; assert . equal ( count ( sourceDb , PhysicalObject . classFullName ) , 2 ) ; const targetDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "target-combined-model.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbFile , { rootSubject : { name : "Combined Model" } } ) ; const targetModelId = PhysicalModel . insert ( targetDb , IModel . rootSubjectId , "PhysicalModel-Combined" ) ; const transformer = new PhysicalModelConsolidator ( sourceDb , targetDb , targetModelId ) ; await transformer . processAll ( ) ; targetDb . saveChanges ( ) ; const targetElement11 = targetDb . elements . getElement ( transformer . context . findTargetElementId ( sourceElementId11 ) ) ; assert . equal ( targetElement11 . userLabel , "PhysicalObject-M1-E1" ) ; assert . equal ( targetElement11 . model , targetModelId ) ; const targetElement21 = targetDb . elements . getElement ( transformer . context . findTargetElementId ( sourceElementId21 ) ) ; assert . equal ( targetElement21 . userLabel , "PhysicalObject-M2-E1" ) ; assert . equal ( targetElement21 . model , targetModelId ) ; const targetPartition = targetDb . elements . getElement ( targetModelId ) ; assert . equal ( targetPartition . code . value , "PhysicalModel-Combined" , "Original CodeValue should be retained" ) ; assert . equal ( count ( targetDb , PhysicalPartition . classFullName ) , 1 ) ; assert . equal ( count ( targetDb , PhysicalModel . classFullName ) , 1 ) ; assert . equal ( count ( targetDb , PhysicalObject . classFullName ) , 2 ) ; transformer . dispose ( ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it ( "should consolidate PhysicalModels" , async ( ) => { const sourceDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "MultiplePhysicalModels.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbFile , { rootSubject : { name : "Multiple PhysicalModels" } } ) ; const categoryId = SpatialCategory . insert ( sourceDb , IModel . dictionaryId , "SpatialCategory" , { color : ColorDef . green . toJSON ( ) } ) ; for ( let i = 0 ; i < 5 ; i ++ ) { const sourceModelId = PhysicalModel . insert ( sourceDb , IModel . rootSubjectId , `PhysicalModel${ i }` ) ; const xArray = [ 20 * i + 1 , 20 * i + 3 , 20 * i + 5 , 20 * i + 7 , 20 * i + 9 ] ; const yArray = [ 0 , 2 , 4 , 6 , 8 ] ; for ( const x of xArray ) { for ( const y of yArray ) { const physicalObjectProps1 = { classFullName : PhysicalObject . classFullName , model : sourceModelId , category : categoryId , code : Code . createEmpty ( ) , userLabel : `M${ i }-PhysicalObject(${ x },${ y })` , geom : IModelTestUtils . createBox ( Point3d . create ( 1 , 1 , 1 ) ) , placement : Placement3d . fromJSON ( { origin : { x , y } , angles : { } } ) } ; sourceDb . elements . insertElement ( physicalObjectProps1 ) ; } } } sourceDb . saveChanges ( ) ; assert . equal ( 5 , count ( sourceDb , PhysicalModel . classFullName ) ) ; assert . equal ( 125 , count ( sourceDb , PhysicalObject . classFullName ) ) ; const targetDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "OnePhysicalModel.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbFile , { rootSubject : { name : "One PhysicalModel" } , createClassViews : true } ) ; const targetModelId = PhysicalModel . insert ( targetDb , IModel . rootSubjectId , "PhysicalModel" ) ; assert . isTrue ( Id64 . isValidId64 ( targetModelId ) ) ; targetDb . saveChanges ( ) ; const consolidator = new PhysicalModelConsolidator ( sourceDb , targetDb , targetModelId ) ; await consolidator . processAll ( ) ; consolidator . dispose ( ) ; assert . equal ( 1 , count ( targetDb , PhysicalModel . classFullName ) ) ; const targetPartition = targetDb . elements . getElement < PhysicalPartition > ( targetModelId ) ; assert . equal ( targetPartition . code . value , "PhysicalModel" , "Target PhysicalModel name should not be overwritten during consolidation" ) ; assert . equal ( 125 , count ( targetDb , PhysicalObject . classFullName ) ) ; const aspects = targetDb . elements . getAspects ( targetPartition . id , ExternalSourceAspect . classFullName ) ; assert . isAtLeast ( aspects . length , 5 , "Provenance should be recorded for each source PhysicalModel" ) ; const sql = `SELECT ECInstanceId FROM ${ PhysicalObject . classFullName }` ; targetDb . withPreparedStatement ( sql , ( statement ) => { while ( DbResult . BE_SQLITE_ROW === statement . step ( ) ) { const targetElementId = statement . getValue ( 0 ) . getId ( ) ; const targetElement = targetDb . elements . getElement < PhysicalObject > ( { id : targetElementId , wantGeometry : true } ) ; assert . exists ( targetElement . geom ) ; assert . isFalse ( targetElement . calculateRange3d ( ) . isNull ) ; } } ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it ( "should sync Team iModels into Shared" , async ( ) => { const iModelShared = IModelTransformerTestUtils . createSharedIModel ( outputDir , [ "A" , "B" ] ) ; if ( true ) { const iModelA = IModelTransformerTestUtils . createTeamIModel ( outputDir , "A" , Point3d . create ( 0 , 0 , 0 ) , ColorDef . green ) ; IModelTransformerTestUtils . assertTeamIModelContents ( iModelA , "A" ) ; const iModelExporterA = new IModelExporter ( iModelA ) ; iModelExporterA . excludeElement ( iModelA . elements . queryElementIdByCode ( Subject . createCode ( iModelA , IModel . rootSubjectId , "Context" ) ) ! ) ; const subjectId = IModelTestUtils . querySubjectId ( iModelShared , "A" ) ; const transformerA2S = new IModelTransformer ( iModelExporterA , iModelShared , { targetScopeElementId : subjectId } ) ; transformerA2S . context . remapElement ( IModel . rootSubjectId , subjectId ) ; await transformerA2S . processAll ( ) ; transformerA2S . dispose ( ) ; IModelTestUtils . dumpIModelInfo ( iModelA ) ; iModelA . close ( ) ; iModelShared . saveChanges ( "Imported A" ) ; IModelTransformerTestUtils . assertSharedIModelContents ( iModelShared , [ "A" ] ) ; } if ( true ) { const iModelB = IModelTransformerTestUtils . createTeamIModel ( outputDir , "B" , Point3d . create ( 0 , 10 , 0 ) , ColorDef . blue ) ; IModelTransformerTestUtils . assertTeamIModelContents ( iModelB , "B" ) ; const iModelExporterB = new IModelExporter ( iModelB ) ; iModelExporterB . excludeElement ( iModelB . elements . queryElementIdByCode ( Subject . createCode ( iModelB , IModel . rootSubjectId , "Context" ) ) ! ) ; const subjectId = IModelTestUtils . querySubjectId ( iModelShared , "B" ) ; const transformerB2S = new IModelTransformer ( iModelExporterB , iModelShared , { targetScopeElementId : subjectId } ) ; transformerB2S . context . remapElement ( IModel . rootSubjectId , subjectId ) ; await transformerB2S . processAll ( ) ; transformerB2S . dispose ( ) ; IModelTestUtils . dumpIModelInfo ( iModelB ) ; iModelB . close ( ) ; iModelShared . saveChanges ( "Imported B" ) ; IModelTransformerTestUtils . assertSharedIModelContents ( iModelShared , [ "A" , "B" ] ) ; } if ( true ) { const iModelConsolidated = IModelTransformerTestUtils . createConsolidatedIModel ( outputDir , "Consolidated" ) ; const transformerS2C = new IModelTransformer ( iModelShared , iModelConsolidated ) ; const subjectA = IModelTestUtils . querySubjectId ( iModelShared , "A" ) ; const subjectB = IModelTestUtils . querySubjectId ( iModelShared , "B" ) ; const definitionA = IModelTestUtils . queryDefinitionPartitionId ( iModelShared , subjectA , "A" ) ; const definitionB = IModelTestUtils . queryDefinitionPartitionId ( iModelShared , subjectB , "B" ) ; const definitionC = IModelTestUtils . queryDefinitionPartitionId ( iModelConsolidated , IModel . rootSubjectId , "Consolidated" ) ; transformerS2C . context . remapElement ( definitionA , definitionC ) ; transformerS2C . context . remapElement ( definitionB , definitionC ) ; const physicalA = IModelTestUtils . queryPhysicalPartitionId ( iModelShared , subjectA , "A" ) ; const physicalB = IModelTestUtils . queryPhysicalPartitionId ( iModelShared , subjectB , "B" ) ; const physicalC = IModelTestUtils . queryPhysicalPartitionId ( iModelConsolidated , IModel . rootSubjectId , "Consolidated" ) ; transformerS2C . context . remapElement ( physicalA , physicalC ) ; transformerS2C . context . remapElement ( physicalB , physicalC ) ; await transformerS2C . processModel ( definitionA ) ; await transformerS2C . processModel ( definitionB ) ; await transformerS2C . processModel ( physicalA ) ; await transformerS2C . processModel ( physicalB ) ; await transformerS2C . processDeferredElements ( ) ; await transformerS2C . processRelationships ( ElementRefersToElements . classFullName ) ; transformerS2C . dispose ( ) ; IModelTransformerTestUtils . assertConsolidatedIModelContents ( iModelConsolidated , "Consolidated" ) ; IModelTestUtils . dumpIModelInfo ( iModelConsolidated ) ; iModelConsolidated . close ( ) ; } IModelTestUtils . dumpIModelInfo ( iModelShared ) ; iModelShared . close ( ) ; } ) ; it ( "should detect conflicting provenance scopes" , async ( ) => { const sourceDb1 = IModelTransformerTestUtils . createTeamIModel ( outputDir , "S1" , Point3d . create ( 0 , 0 , 0 ) , ColorDef . green ) ; const sourceDb2 = IModelTransformerTestUtils . createTeamIModel ( outputDir , "S2" , Point3d . create ( 0 , 10 , 0 ) , ColorDef . blue ) ; assert . notEqual ( sourceDb1 . iModelId , sourceDb2 . iModelId ) ; const targetDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "ConflictingScopes.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbFile , { rootSubject : { name : "Conflicting Scopes Test" } } ) ; const transformer1 = new IModelTransformer ( sourceDb1 , targetDb ) ; const transformer2 = new IModelTransformer ( sourceDb2 , targetDb ) ; await transformer1 . processAll ( ) ; try { await transformer2 . processAll ( ) ; assert . fail ( "Expected provenance scope conflict" ) ; } catch ( e ) { assert . isTrue ( e instanceof IModelError ) ; } finally { transformer1 . dispose ( ) ; transformer2 . dispose ( ) ; sourceDb1 . close ( ) ; sourceDb2 . close ( ) ; targetDb . close ( ) ; } } ) ; it ( "IModelCloneContext remap tests" , async ( ) => { const iModelDb = IModelTransformerTestUtils . createTeamIModel ( outputDir , "Test" , Point3d . create ( 0 , 0 , 0 ) , ColorDef . green ) ; const cloneContext = new IModelCloneContext ( iModelDb ) ; const sourceId = Id64 . fromLocalAndBriefcaseIds ( 1 , 1 ) ; const targetId = Id64 . fromLocalAndBriefcaseIds ( 1 , 2 ) ; cloneContext . remapElement ( sourceId , targetId ) ; assert . equal ( targetId , cloneContext . findTargetElementId ( sourceId ) ) ; assert . equal ( Id64 . invalid , cloneContext . findTargetElementId ( targetId ) ) ; assert . equal ( Id64 . invalid , cloneContext . findTargetCodeSpecId ( targetId ) ) ; assert . throws ( ( ) => cloneContext . remapCodeSpec ( "SourceNotFound" , "TargetNotFound" ) ) ; cloneContext . dispose ( ) ; iModelDb . close ( ) ; } ) ; it ( "should clone across schema versions" , async ( ) => { const cloneTestSchema100 = path . join ( KnownTestLocations . assetsDir , "CloneTest.01.00.00.ecschema.xml" ) ; const cloneTestSchema101 = path . join ( KnownTestLocations . assetsDir , "CloneTest.01.00.01.ecschema.xml" ) ; const seedDb = SnapshotDb . openFile ( IModelTestUtils . resolveAssetFile ( "CompatibilityTestSeed.bim" ) ) ; const sourceDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "CloneWithSchemaChanges-Source.bim" ) ; const sourceDb = SnapshotDb . createFrom ( seedDb , sourceDbFile ) ; await sourceDb . importSchemas ( [ cloneTestSchema100 ] ) ; const sourceElementProps = { classFullName : "CloneTest:PhysicalType" , model : IModel . dictionaryId , code : PhysicalType . createCode ( sourceDb , IModel . dictionaryId , "Type1" ) , string1 : "a" , string2 : "b" } ; const sourceElementId = sourceDb . elements . insertElement ( sourceElementProps ) ; const sourceElement = sourceDb . elements . getElement ( sourceElementId ) ; assert . equal ( sourceElement . asAny . string1 , "a" ) ; assert . equal ( sourceElement . asAny . string2 , "b" ) ; sourceDb . saveChanges ( ) ; const targetDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "CloneWithSchemaChanges-Target.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbFile , { rootSubject : { name : "CloneWithSchemaChanges-Target" } } ) ; await targetDb . importSchemas ( [ cloneTestSchema101 ] ) ; const transformer = new IModelTransformer ( sourceDb , targetDb ) ; await transformer . processElement ( sourceElementId ) ; targetDb . saveChanges ( ) ; const targetElementId = transformer . context . findTargetElementId ( sourceElementId ) ; const targetElement = targetDb . elements . getElement ( targetElementId ) ; assert . equal ( targetElement . asAny . string1 , "a" ) ; assert . equal ( targetElement . asAny . string2 , "b" ) ; seedDb . close ( ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it ( "Should not visit elements or relationships" , async ( ) => { class TestExporter extends IModelExportHandler { public iModelExporter : IModelExporter ; public modelCount = 0 ; public constructor ( iModelDb ) { super ( ) ; this . iModelExporter = new IModelExporter ( iModelDb ) ; this . iModelExporter . registerHandler ( this ) ; } public override onExportModel ( _model , _isUpdate ) { ++ this . modelCount ; } public override onExportElement ( _element , _isUpdate ) { assert . fail ( "Should not visit element when visitElements=false" ) ; } public override onExportRelationship ( _relationship , _isUpdate ) { assert . fail ( "Should not visit relationship when visitRelationship=false" ) ; } } const sourceFileName = IModelTestUtils . resolveAssetFile ( "CompatibilityTestSeed.bim" ) ; const sourceDb = SnapshotDb . openFile ( sourceFileName ) ; const exporter = new TestExporter ( sourceDb ) ; exporter . iModelExporter . visitElements = false ; exporter . iModelExporter . visitRelationships = false ; await exporter . iModelExporter . exportAll ( ) ; await exporter . iModelExporter . exportElement ( IModel . rootSubjectId ) ; await exporter . iModelExporter . exportChildElements ( IModel . rootSubjectId ) ; await exporter . iModelExporter . exportModelContents ( IModel . repositoryModelId ) ; await exporter . iModelExporter . exportRelationships ( ElementRefersToElements . classFullName ) ; assert . isAtLeast ( exporter . modelCount , 4 ) ; sourceDb . close ( ) ; } ) ; it ( "Should filter by ViewDefinition" , async ( ) => { const sourceDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "FilterByView-Source.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbFile , { rootSubject : { name : "FilterByView-Source" } } ) ; const categoryNames = [ "C1" , "C2" , "C3" , "C4" , "C5" ] ; categoryNames . forEach ( ( categoryName ) => { const categoryId = SpatialCategory . insert ( sourceDb , IModel . dictionaryId , categoryName , { } ) ; CategorySelector . insert ( sourceDb , IModel . dictionaryId , categoryName , [ categoryId ] ) ; } ) ; const modelNames = [ "MA" , "MB" , "MC" , "MD" ] ; modelNames . forEach ( ( modelName ) => { const modelId = PhysicalModel . insert ( sourceDb , IModel . rootSubjectId , modelName ) ; ModelSelector . insert ( sourceDb , IModel . dictionaryId , modelName , [ modelId ] ) ; } ) ; const projectExtents = new Range3d ( ) ; const displayStyleId = DisplayStyle3d . insert ( sourceDb , IModel . dictionaryId , "DisplayStyle" ) ; for ( let x = 0 ; x < categoryNames . length ; x ++ ) { const categoryId = sourceDb . elements . queryElementIdByCode ( SpatialCategory . createCode ( sourceDb , IModel . dictionaryId , categoryNames [ x ] ) ) ! ; const categorySelectorId = sourceDb . elements . queryElementIdByCode ( CategorySelector . createCode ( sourceDb , IModel . dictionaryId , categoryNames [ x ] ) ) ! ; for ( let y = 0 ; y < modelNames . length ; y ++ ) { const modelId = sourceDb . elements . queryElementIdByCode ( PhysicalPartition . createCode ( sourceDb , IModel . rootSubjectId , modelNames [ y ] ) ) ! ; const modelSelectorId = sourceDb . elements . queryElementIdByCode ( ModelSelector . createCode ( sourceDb , IModel . dictionaryId , modelNames [ y ] ) ) ! ; const physicalObjectProps = { classFullName : PhysicalObject . classFullName , model : modelId , category : categoryId , code : Code . createEmpty ( ) , userLabel : `${ PhysicalObject . className }-${ categoryNames [ x ] }-${ modelNames [ y ] }` , geom : IModelTestUtils . createBox ( Point3d . create ( 1 , 1 , 1 ) , categoryId ) , placement : { origin : Point3d . create ( x * 2 , y * 2 , 0 ) , angles : YawPitchRollAngles . createDegrees ( 0 , 0 , 0 ) } } ; const physicalObjectId = sourceDb . elements . insertElement ( physicalObjectProps ) ; const physicalObject = sourceDb . elements . getElement < PhysicalObject > ( physicalObjectId , PhysicalObject ) ; const viewExtents = physicalObject . placement . calculateRange ( ) ; OrthographicViewDefinition . insert ( sourceDb , IModel . dictionaryId , `View-${ categoryNames [ x ] }-${ modelNames [ y ] }` , modelSelectorId , categorySelectorId , displayStyleId , viewExtents , StandardViewIndex . Iso ) ; projectExtents . extendRange ( viewExtents ) ; } } sourceDb . updateProjectExtents ( projectExtents ) ; const exportCategorySelectorId = CategorySelector . insert ( sourceDb , IModel . dictionaryId , "Export" , [ sourceDb . elements . queryElementIdByCode ( SpatialCategory . createCode ( sourceDb , IModel . dictionaryId , categoryNames [ 0 ] ) ) ! , sourceDb . elements . queryElementIdByCode ( SpatialCategory . createCode ( sourceDb , IModel . dictionaryId , categoryNames [ 2 ] ) ) ! , sourceDb . elements . queryElementIdByCode ( SpatialCategory . createCode ( sourceDb , IModel . dictionaryId , categoryNames [ 4 ] ) ) ! , ] ) ; const exportModelSelectorId = ModelSelector . insert ( sourceDb , IModel . dictionaryId , "Export" , [ sourceDb . elements . queryElementIdByCode ( PhysicalPartition . createCode ( sourceDb , IModel . rootSubjectId , modelNames [ 1 ] ) ) ! , sourceDb . elements . queryElementIdByCode ( PhysicalPartition . createCode ( sourceDb , IModel . rootSubjectId , modelNames [ 3 ] ) ) ! , ] ) ; const exportViewId = OrthographicViewDefinition . insert ( sourceDb , IModel . dictionaryId , "Export" , exportModelSelectorId , exportCategorySelectorId , displayStyleId , projectExtents , StandardViewIndex . Iso ) ; sourceDb . saveChanges ( ) ; const targetDbFile = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "FilterByView-Target.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbFile , { rootSubject : { name : "FilterByView-Target" } } ) ; targetDb . updateProjectExtents ( sourceDb . projectExtents ) ; const transformer = new FilterByViewTransformer ( sourceDb , targetDb , exportViewId ) ; await transformer . processSchemas ( ) ; await transformer . processAll ( ) ; transformer . dispose ( ) ; targetDb . saveChanges ( ) ; targetDb . close ( ) ; sourceDb . close ( ) ; } ) ; it . skip ( "Merge test" , async ( ) => { const mergedIModelFileName = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "MergeTest.bim" ) ; const mergedDb = SnapshotDb . createEmpty ( mergedIModelFileName , { rootSubject : { name : "Merge Test" } } ) ; const campusSubjectId = Subject . insert ( mergedDb , IModel . rootSubjectId , "Campus" ) ; assert . isTrue ( Id64 . isValidId64 ( campusSubjectId ) ) ; const garageSubjectId = Subject . insert ( mergedDb , IModel . rootSubjectId , "Garage" ) ; assert . isTrue ( Id64 . isValidId64 ( garageSubjectId ) ) ; const buildingSubjectId = Subject . insert ( mergedDb , IModel . rootSubjectId , "Building" ) ; assert . isTrue ( Id64 . isValidId64 ( buildingSubjectId ) ) ; mergedDb . saveChanges ( "Create Subject hierarchy" ) ; IModelTestUtils . flushTxns ( mergedDb ) ; if ( true ) { const campusIModelFileName = "D:/data/bim/MergeTest/Campus.bim" ; const campusDb = SnapshotDb . openFile ( campusIModelFileName ) ; IModelTestUtils . dumpIModelInfo ( campusDb ) ; const transformer = new IModelTransformer ( campusDb , mergedDb , { targetScopeElementId : campusSubjectId } ) ; await transformer . processSchemas ( ) ; transformer . context . remapElement ( IModel . rootSubjectId , campusSubjectId ) ; await transformer . processAll ( ) ; transformer . dispose ( ) ; mergedDb . saveChanges ( "Imported Campus" ) ; IModelTestUtils . flushTxns ( mergedDb ) ; campusDb . close ( ) ; } if ( true ) { const garageIModelFileName = "D:/data/bim/MergeTest/Garage.bim" ; const garageDb = SnapshotDb . openFile ( garageIModelFileName ) ; IModelTestUtils . dumpIModelInfo ( garageDb ) ; const transformer = new IModelTransformer ( garageDb , mergedDb , { targetScopeElementId : garageSubjectId } ) ; transformer . context . remapElement ( IModel . rootSubjectId , garageSubjectId ) ; await transformer . processAll ( ) ; transformer . dispose ( ) ; mergedDb . saveChanges ( "Imported Garage" ) ; IModelTestUtils . flushTxns ( mergedDb ) ; garageDb . close ( ) ; } if ( true ) { const buildingIModelFileName = "D:/data/bim/MergeTest/Building.bim" ; const buildingDb = SnapshotDb . openFile ( buildingIModelFileName ) ; IModelTestUtils . dumpIModelInfo ( buildingDb ) ; const transformer = new IModelTransformer ( buildingDb , mergedDb , { targetScopeElementId : buildingSubjectId } ) ; await transformer . processSchemas ( ) ; transformer . context . remapElement ( IModel . rootSubjectId , buildingSubjectId ) ; await transformer . processAll ( ) ; transformer . dispose ( ) ; mergedDb . saveChanges ( "Imported Building" ) ; IModelTestUtils . flushTxns ( mergedDb ) ; buildingDb . close ( ) ; } IModelTestUtils . dumpIModelInfo ( mergedDb ) ; mergedDb . close ( ) ; } ) ; it ( "processSchemas should handle out-of-order exported schemas" , async ( ) => { const testSchema1Path = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "TestSchema1.ecschema.xml" ) ; IModelJsFs . writeFileSync ( testSchema1Path , `<?xml version="1.0" encoding="UTF-8"?>
      <ECSchema schemaName="TestSchema1" alias="ts1" version="01.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.1">
          <ECSchemaReference name="BisCore" version="01.00" alias="bis"/>
          <ECEntityClass typeName="TestElement1">
            <BaseClass>bis:PhysicalElement</BaseClass>
            <ECProperty propertyName="MyProp1" typeName="string"/>
          </ECEntityClass>
      </ECSchema>` ) ; const testSchema2Path = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "TestSchema2.ecschema.xml" ) ; IModelJsFs . writeFileSync ( testSchema2Path , `<?xml version="1.0" encoding="UTF-8"?>
      <ECSchema schemaName="TestSchema2" alias="ts2" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
          <ECSchemaReference name="BisCore" version="01.00.00" alias="bis"/>
          <ECSchemaReference name="TestSchema1" version="01.00.00" alias="ts1"/>
          <ECEntityClass typeName="TestElement2">
            <BaseClass>ts1:TestElement1</BaseClass>
            <ECProperty propertyName="MyProp2" typeName="string"/>
          </ECEntityClass>
      </ECSchema>` ) ; const sourceDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "OrderTestSource.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbPath , { rootSubject : { name : "Order Test" } } ) ; await sourceDb . importSchemas ( [ testSchema1Path , testSchema2Path ] ) ; sourceDb . saveChanges ( ) ; class OrderedExporter extends IModelExporter { public override async exportSchemas ( ) { const schemaLoader = new IModelSchemaLoader ( this . sourceDb ) ; const schema1 = schemaLoader . getSchema ( "TestSchema1" ) ; const schema2 = schemaLoader . getSchema ( "TestSchema2" ) ; await this . handler . onExportSchema ( schema2 ) ; await this . handler . onExportSchema ( schema1 ) ; } } const targetDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "OrderTestTarget.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbPath , { rootSubject : { name : "Order Test" } } ) ; const transformer = new IModelTransformer ( new OrderedExporter ( sourceDb ) , targetDb ) ; let error ; try { await transformer . processSchemas ( ) ; } catch ( _error ) { error = _error ; } assert . isUndefined ( error ) ; targetDb . saveChanges ( ) ; const targetImportedSchemasLoader = new IModelSchemaLoader ( targetDb ) ; const schema1InTarget = targetImportedSchemasLoader . getSchema ( "TestSchema1" ) ; assert . isDefined ( schema1InTarget ) ; const schema2InTarget = targetImportedSchemasLoader . getSchema ( "TestSchema2" ) ; assert . isDefined ( schema2InTarget ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it ( "processSchemas should wait for the schema import to finish to delete the export directory" , async ( ) => { const cloneTestSchema100 = path . join ( KnownTestLocations . assetsDir , "CloneTest.01.00.00.ecschema.xml" ) ; const sourceDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "FinallyFirstTest.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbPath , { rootSubject : { name : "FinallyFirstTest" } } ) ; await sourceDb . importSchemas ( [ cloneTestSchema100 ] ) ; sourceDb . saveChanges ( ) ; const targetDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "FinallyFirstTestOut.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbPath , { rootSubject : { name : "FinallyFirstTest" } } ) ; const transformer = new IModelTransformer ( sourceDb , targetDb ) ; const importSchemasResolved = sinon . spy ( ) ; let importSchemasPromise ; sinon . replace ( targetDb , "importSchemas" , sinon . fake ( async ( ) => { importSchemasPromise = new Promise ( ( resolve ) => setImmediate ( ( ) => { importSchemasResolved ( ) ; resolve ( undefined ) ; } ) ) ; return importSchemasPromise ; } ) ) ; const removeSyncSpy = sinon . spy ( IModelJsFs , "removeSync" ) ; await transformer . processSchemas ( ) ; assert ( removeSyncSpy . calledAfter ( importSchemasResolved ) ) ; sinon . restore ( ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it ( "handles definition element scoped by non-definitional element" , async ( ) => { const sourceDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "BadPredecessorsExampleSource.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbPath , { rootSubject : { name : "BadPredecessorExampleSource" } } ) ; const documentListModelId = DocumentListModel . insert ( sourceDb , IModelDb . rootSubjectId , "DocumentList" ) ; const drawingId = sourceDb . elements . insertElement ( { classFullName : Drawing . classFullName , model : documentListModelId , code : Drawing . createCode ( sourceDb , documentListModelId , "Drawing" ) } ) ; expect ( Id64 . isValidId64 ( drawingId ) ) . to . be . true ; const model = sourceDb . models . createModel ( { classFullName : DrawingModel . classFullName , modeledElement : { id : drawingId } } ) ; sourceDb . models . insertModel ( model ) ; const myCodeSpecId = sourceDb . codeSpecs . insert ( CodeSpec . create ( sourceDb , "MyCodeSpec" , CodeScopeSpec . Type . RelatedElement ) ) ; const _physicalMaterialId = sourceDb . elements . insertElement ( { classFullName : GenericPhysicalMaterial . classFullName , model : IModel . dictionaryId , code : new Code ( { spec : myCodeSpecId , scope : drawingId , value : "physical material" } ) } as DefinitionElementProps ) ; sourceDb . saveChanges ( ) ; const targetDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "BadPredecessorExampleTarget.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbPath , { rootSubject : { name : sourceDb . rootSubject . name } } ) ; const transformer = new IModelTransformer ( sourceDb , targetDb ) ; await expect ( transformer . processSchemas ( ) ) . to . eventually . be . fulfilled ; await expect ( transformer . processAll ( ) ) . to . eventually . be . fulfilled ; expect ( targetDb . codeSpecs . hasName ( "MyCodeSpec" ) ) . to . be . true ; const drawingIdTarget = targetDb . elements . queryElementIdByCode ( Drawing . createCode ( targetDb , documentListModelId , "Drawing" ) ) ; expect ( drawingIdTarget ) . to . not . be . undefined ; expect ( Id64 . isValidId64 ( ( drawingIdTarget as string ) ) ) . to . be . true ; const physicalMaterialIdTarget = targetDb . elements . queryElementIdByCode ( new Code ( { spec : myCodeSpecId , scope : drawingId , value : "physical material" } ) ) ; expect ( physicalMaterialIdTarget ) . to . not . be . undefined ; expect ( Id64 . isValidId64 ( ( physicalMaterialIdTarget as string ) ) ) . to . be . true ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it ( "handle backwards related-instance code in model" , async ( ) => { const sourceDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "BadPredecessorsExampleSource.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbPath , { rootSubject : { name : "BadPredecessorExampleSource" } } ) ; const documentListModelId = DocumentListModel . insert ( sourceDb , IModelDb . rootSubjectId , "DocumentList" ) ; const drawing1Id = sourceDb . elements . insertElement ( { classFullName : Drawing . classFullName , model : documentListModelId , code : Drawing . createCode ( sourceDb , documentListModelId , "Drawing1" ) } ) ; const drawing2Id = sourceDb . elements . insertElement ( { classFullName : Drawing . classFullName , model : documentListModelId , code : Drawing . createCode ( sourceDb , documentListModelId , "Drawing2" ) } ) ; const drawingModel1 = sourceDb . models . createModel ( { classFullName : DrawingModel . classFullName , modeledElement : { id : drawing1Id } } ) ; const drawingModel1Id = sourceDb . models . insertModel ( drawingModel1 ) ; const drawingModel2 = sourceDb . models . createModel ( { classFullName : DrawingModel . classFullName , modeledElement : { id : drawing2Id } } ) ; const drawingModel2Id = sourceDb . models . insertModel ( drawingModel2 ) ; const modelCodeSpec = sourceDb . codeSpecs . insert ( CodeSpec . create ( sourceDb , "ModelCodeSpec" , CodeScopeSpec . Type . Model ) ) ; const relatedCodeSpecId = sourceDb . codeSpecs . insert ( CodeSpec . create ( sourceDb , "RelatedCodeSpec" , CodeScopeSpec . Type . RelatedElement ) ) ; const categoryId = DrawingCategory . insert ( sourceDb , IModel . dictionaryId , "DrawingCategory" , { color : ColorDef . green . toJSON ( ) } ) ; const drawingGraphic2Id = new DrawingGraphic ( { classFullName : DrawingGraphic . classFullName , model : drawingModel2Id , code : new Code ( { spec : modelCodeSpec , scope : drawingModel2Id , value : "drawing graphic 2" } ) , category : categoryId } , sourceDb ) . insert ( ) ; const _drawingGraphic1Id = new DrawingGraphic ( { classFullName : DrawingGraphic . classFullName , model : drawingModel1Id , code : new Code ( { spec : relatedCodeSpecId , scope : drawingGraphic2Id , value : "drawing graphic 1" } ) , category : categoryId } , sourceDb ) . insert ( ) ; sourceDb . saveChanges ( ) ; const targetDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "BadPredecessorExampleTarget.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbPath , { rootSubject : { name : sourceDb . rootSubject . name } } ) ; const transformer = new IModelTransformer ( sourceDb , targetDb ) ; await expect ( transformer . processSchemas ( ) ) . to . eventually . be . fulfilled ; await expect ( transformer . processAll ( ) ) . to . eventually . be . fulfilled ; expect ( targetDb . codeSpecs . hasName ( "ModelCodeSpec" ) ) . to . be . true ; expect ( targetDb . codeSpecs . hasName ( "RelatedCodeSpec" ) ) . to . be . true ; const drawingIdTarget1 = targetDb . elements . queryElementIdByCode ( Drawing . createCode ( targetDb , documentListModelId , "Drawing1" ) ) ; expect ( drawingIdTarget1 ) . to . not . be . undefined ; expect ( Id64 . isValidId64 ( ( drawingIdTarget1 as string ) ) ) . to . be . true ; const drawingIdTarget2 = targetDb . elements . queryElementIdByCode ( Drawing . createCode ( targetDb , documentListModelId , "Drawing2" ) ) ; expect ( drawingIdTarget2 ) . to . not . be . undefined ; expect ( Id64 . isValidId64 ( ( drawingIdTarget2 as string ) ) ) . to . be . true ; const drawingGraphicIdTarget2Props = targetDb . elements . getElementProps ( drawingGraphic2Id ) ; expect ( targetDb . elements . queryElementIdByCode ( new Code ( drawingGraphicIdTarget2Props . code ) ) ) . to . not . be . undefined ; expect ( Id64 . isValidId64 ( ( targetDb . elements . queryElementIdByCode ( new Code ( drawingGraphicIdTarget2Props . code ) ) as string ) ) ) . to . be . true ; const drawingGraphicIdTarget1Props = targetDb . elements . getElementProps ( _drawingGraphic1Id ) ; expect ( targetDb . elements . queryElementIdByCode ( new Code ( drawingGraphicIdTarget1Props . code ) ) ) . to . not . be . undefined ; expect ( Id64 . isValidId64 ( ( targetDb . elements . queryElementIdByCode ( new Code ( drawingGraphicIdTarget1Props . code ) ) as string ) ) ) . to . be . true ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; function setToStandalone ( iModelName ) { const nativeDb = new IModelHost . platform . DgnDb ( ) ; nativeDb . openIModel ( iModelName , OpenMode . ReadWrite ) ; nativeDb . setITwinId ( Guid . empty ) ; nativeDb . saveChanges ( ) ; nativeDb . deleteAllTxns ( ) ; nativeDb . resetBriefcaseId ( BriefcaseIdValue . Unassigned ) ; nativeDb . saveLocalValue ( "StandaloneEdit" , JSON . stringify ( { txns : true } ) ) ; nativeDb . saveChanges ( ) ; nativeDb . closeIModel ( ) ; } it ( "biscore update is valid" , async ( ) => { const sourceDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "BisCoreUpdateSource.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbPath , { rootSubject : { name : "BisCoreUpdate" } } ) ; const targetDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "BisCoreUpdateTarget.bim" ) ; const seedDb = SnapshotDb . openFile ( IModelTestUtils . resolveAssetFile ( "CompatibilityTestSeed.bim" ) ) ; const targetDbTestCopy = SnapshotDb . createFrom ( seedDb , targetDbPath ) ; targetDbTestCopy . close ( ) ; seedDb . close ( ) ; setToStandalone ( targetDbPath ) ; const targetDb = StandaloneDb . openFile ( targetDbPath ) ; assert ( Semver . lt ( Schema . toSemverString ( targetDb . querySchemaVersion ( "BisCore" ) ! ) , Schema . toSemverString ( sourceDb . querySchemaVersion ( "BisCore" ) ! ) ) , "The targetDb must have a less up-to-date version of the BisCore schema than the source" ) ; const transformer = new IModelTransformer ( sourceDb , targetDb ) ; await transformer . processSchemas ( ) ; targetDb . saveChanges ( ) ; assert ( Semver . eq ( Schema . toSemverString ( targetDb . querySchemaVersion ( "BisCore" ) ! ) , Schema . toSemverString ( sourceDb . querySchemaVersion ( "BisCore" ) ! ) ) , "The targetDb must now have an equivalent BisCore schema because it was updated" ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; /** gets a mapping of element ids to their invariant content */ async function getAllElementsInvariants ( db , filterPredicate ? ) { const result = { } ; for await ( const row of db . query ( "SELECT * FROM bis.Element" , undefined , { rowFormat : QueryRowFormat . UseJsPropertyNames } ) ) { if ( ! filterPredicate || filterPredicate ( db . elements . getElement ( row . id ) ) ) { const { lastMod : _lastMod , ... invariantPortion } = row ; result [ row . id ] = invariantPortion ; } } return result ; } /** gets the ordered list of the relationships inserted earlier */ async function getInvariantRelationsContent ( db , filterPredicate ? ) { const result = [ ] ; for await ( const row of db . query ( "SELECT * FROM bis.ElementRefersToElements" , undefined , { rowFormat : QueryRowFormat . UseJsPropertyNames } ) ) { if ( ! filterPredicate || filterPredicate ( row ) ) { const { id : _id , ... invariantPortion } = row ; result . push ( invariantPortion ) ; } } return result ; } it ( "preserveId option preserves element ids, not other entity ids" , async ( ) => { const sourceDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "PreserveIdSource.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbPath , { rootSubject : { name : "PreserveId" } } ) ; const spatialCateg1Id = SpatialCategory . insert ( sourceDb , IModelDb . dictionaryId , "spatial-category1" , { color : ColorDef . blue . toJSON ( ) } ) ; const spatialCateg2Id = SpatialCategory . insert ( sourceDb , IModelDb . dictionaryId , "spatial-category2" , { color : ColorDef . red . toJSON ( ) } ) ; const myPhysModelId = PhysicalModel . insert ( sourceDb , IModelDb . rootSubjectId , "myPhysicalModel" ) ; const _physicalObjectIds = [ spatialCateg1Id , spatialCateg2Id , spatialCateg2Id , spatialCateg2Id , spatialCateg2Id ] . map ( ( categoryId , x ) => { const physicalObjectProps = { classFullName : PhysicalObject . classFullName , model : myPhysModelId , category : categoryId , code : Code . createEmpty ( ) , userLabel : `PhysicalObject(${ x })` , geom : IModelTestUtils . createBox ( Point3d . create ( 1 , 1 , 1 ) ) , placement : Placement3d . fromJSON ( { origin : { x } , angles : { } } ) } ; const physicalObjectId = sourceDb . elements . insertElement ( physicalObjectProps ) ; return physicalObjectId ; } ) ; const physicalPartitions = new Array ( 3 ) . fill ( null ) . map ( ( _ , index ) => new PhysicalPartition ( { classFullName : PhysicalPartition . classFullName , model : IModelDb . rootSubjectId , parent : { id : IModelDb . rootSubjectId , relClassName : ElementOwnsChildElements . classFullName } , code : PhysicalPartition . createCode ( sourceDb , IModelDb . rootSubjectId , `physical-partition-${ index }` ) } , sourceDb ) ) . map ( ( partition ) => { const partitionId = partition . insert ( ) ; const model = new PhysicalModel ( { classFullName : PhysicalPartition . classFullName , modeledElement : { id : partitionId } } , sourceDb ) ; const modelId = model . insert ( ) ; return { modelId , partitionId } ; } ) ; const linksIds = new Array ( 2 ) . fill ( null ) . map ( ( _ , index ) => { const link = new RepositoryLink ( { classFullName : RepositoryLink . classFullName , code : RepositoryLink . createCode ( sourceDb , IModelDb . rootSubjectId , `repo-link-${ index }` ) , model : IModelDb . rootSubjectId , repositoryGuid : `2fd0e5ed-a4d7-40cd-be8a-57552f5736b${ index }` , format : "my-format" } , sourceDb ) ; const linkId = link . insert ( ) ; return linkId ; } ) ; const _nonElementEntityIds = [ [ physicalPartitions [ 1 ] . partitionId , linksIds [ 0 ] ] , [ physicalPartitions [ 1 ] . partitionId , linksIds [ 1 ] ] , [ physicalPartitions [ 2 ] . partitionId , linksIds [ 0 ] ] , [ physicalPartitions [ 2 ] . partitionId , linksIds [ 1 ] ] , ] . map ( ( [ sourceId , targetId ] ) => sourceDb . relationships . insertInstance ( { classFullName : "BisCore:PartitionOriginatesFromRepository" , sourceId , targetId } ) ) ; sourceDb . saveChanges ( ) ; const targetDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "PreserveIdTarget.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbPath , { rootSubject : { name : "PreserveId" } } ) ; const spatialCateg2 = sourceDb . elements . getElement < SpatialCategory > ( spatialCateg2Id ) ; /** filter the category and all related elements from the source for transformation */ function filterCategoryTransformationPredicate ( elem ) { if ( elem instanceof GeometricElement && elem . category === spatialCateg2Id ) return false ; if ( elem . id === spatialCateg2Id ) return false ; return true ; } /** filter the category and all related elements from the source for transformation */ function filterRelationshipsToChangeIds ( { sourceId , targetId } ) { if ( sourceId === physicalPartitions [ 1 ] . partitionId && targetId === linksIds [ 0 ] ) return false ; return true ; } /** filter the category and all related and child elements from the source for comparison, not transformation */ function filterCategoryContentsPredicate ( elem ) { if ( elem instanceof GeometricElement && elem . category === spatialCateg2Id ) return false ; if ( elem . id === spatialCateg2Id ) return false ; if ( elem . id === spatialCateg2 . myDefaultSubCategoryId ( ) ) return false ; return true ; } class FilterCategoryTransformer extends IModelTransformer { public override shouldExportElement ( elem ) { if ( ! filterCategoryTransformationPredicate ( elem ) ) return false ; return super . shouldExportElement ( elem ) ; } public override shouldExportRelationship ( rel ) { if ( ! filterRelationshipsToChangeIds ( rel ) ) return false ; return super . shouldExportRelationship ( rel ) ; } } const transformer = new FilterCategoryTransformer ( sourceDb , targetDb , { preserveElementIdsForFiltering : true } ) ; await transformer . processAll ( ) ; targetDb . saveChanges ( ) ; const sourceContent = await getAllElementsInvariants ( sourceDb , filterCategoryContentsPredicate ) ; const targetContent = await getAllElementsInvariants ( targetDb ) ; expect ( targetContent ) . to . deep . equal ( sourceContent ) ; const sourceRelations = await getInvariantRelationsContent ( sourceDb , filterRelationshipsToChangeIds ) ; const targetRelations = await getInvariantRelationsContent ( targetDb ) ; expect ( sourceRelations ) . to . deep . equal ( targetRelations ) ; const spatialCateg3Id = SpatialCategory . insert ( targetDb , IModelDb . dictionaryId , "spatial-category3" , { color : ColorDef . black . toJSON ( ) } ) ; expect ( Id64 . isValid ( spatialCateg3Id ) ) . to . be . true ; const spatialCateg3Subcateg1Id = SubCategory . insert ( targetDb , spatialCateg3Id , "spatial-categ-subcateg-1" , { color : ColorDef . white . toJSON ( ) } ) ; expect ( Id64 . isValid ( spatialCateg3Subcateg1Id ) ) . to . be . true ; const insertedInstance = targetDb . relationships . insertInstance ( { classFullName : "BisCore:PartitionOriginatesFromRepository" , sourceId : physicalPartitions [ 1 ] . partitionId , targetId : linksIds [ 0 ] } ) ; expect ( Id64 . isValid ( insertedInstance ) ) . to . be . true ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it ( "preserveId on test model" , async ( ) => { const seedDb = SnapshotDb . openFile ( IModelTestUtils . resolveAssetFile ( "CompatibilityTestSeed.bim" ) ) ; const sourceDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "PreserveIdOnTestModel-Source.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbPath , { rootSubject : seedDb . rootSubject } ) ; const seedTransformer = new IModelTransformer ( seedDb , sourceDb ) ; await seedTransformer . processAll ( ) ; sourceDb . saveChanges ( ) ; const targetDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "PreserveIdOnTestModel-Target.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbPath , { rootSubject : sourceDb . rootSubject } ) ; const transformer = new IModelTransformer ( sourceDb , targetDb , { preserveElementIdsForFiltering : true } ) ; await transformer . processAll ( ) ; targetDb . saveChanges ( ) ; const sourceContent = await getAllElementsInvariants ( sourceDb ) ; const targetContent = await getAllElementsInvariants ( targetDb ) ; expect ( targetContent ) . to . deep . equal ( sourceContent ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; function createIModelWithDanglingPredecessor ( opts ) { const sourceDb = SnapshotDb . createEmpty ( opts . path , { rootSubject : { name : opts . name } } ) ; const sourceCategoryId = SpatialCategory . insert ( sourceDb , IModel . dictionaryId , "SpatialCategory" , { color : ColorDef . green . toJSON ( ) } ) ; const sourceModelId = PhysicalModel . insert ( sourceDb , IModel . rootSubjectId , "Physical" ) ; const myPhysObjCodeSpec = CodeSpec . create ( sourceDb , "myPhysicalObjects" , CodeScopeSpec . Type . ParentElement ) ; const myPhysObjCodeSpecId = sourceDb . codeSpecs . insert ( myPhysObjCodeSpec ) ; const physicalObjects = [ 1 , 2 ] . map ( ( x ) => { const code = new Code ( { spec : myPhysObjCodeSpecId , scope : sourceModelId , value : `PhysicalObject(${ x })` } ) ; const props = { classFullName : PhysicalObject . classFullName , model : sourceModelId , category : sourceCategoryId , code , userLabel : `PhysicalObject(${ x })` , geom : IModelTestUtils . createBox ( Point3d . create ( 1 , 1 , 1 ) ) , placement : Placement3d . fromJSON ( { origin : { x } , angles : { } } ) } ; const id = sourceDb . elements . insertElement ( props ) ; return { code , id } ; } ) ; const displayStyleId = DisplayStyle3d . insert ( sourceDb , IModel . dictionaryId , "MyDisplayStyle" , { excludedElements : physicalObjects . map ( ( o ) => o . id ) } ) ; const displayStyleCode = sourceDb . elements . getElement ( displayStyleId ) . code ; const physObjId2 = physicalObjects [ 1 ] . id ; sourceDb . elements . deleteElement ( physObjId2 ) ; sourceDb . saveChanges ( ) ; return [ sourceDb , { sourceCategoryId , sourceModelId , physicalObjects , displayStyleId , displayStyleCode , myPhysObjCodeSpec } , ] as const ; } note class ShiftElemIdsTransformer extends IModelTransformer { constructor ( ... args ) { super ( ... args ) ; try { PhysicalModel . insert ( this . targetDb , IModel . rootSubjectId , "MyShiftElemIdsPhysicalModel" ) ; } catch ( _err ) { } } } it ( "predecessor deletion is considered invalid when danglingPredecessorsBehavior='reject' and that is the default" , async ( ) => { const sourceDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "DanglingPredecessorSource.bim" ) ; const [ sourceDb , { displayStyleId , physicalObjects } , ] = createIModelWithDanglingPredecessor ( { name : "DanglingPredecessors" , path : sourceDbPath } ) ; const targetDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "DanglingPredecessorTarget.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbPath , { rootSubject : sourceDb . rootSubject } ) ; const defaultTransformer = new ShiftElemIdsTransformer ( sourceDb , targetDb ) ; await expect ( defaultTransformer . processAll ( ) ) . to . be . rejectedWith ( /Found a reference to an element "[^"]*" that doesn't exist/ ) ; const ignoreDanglingPredecessorsDisabledTransformer = new ShiftElemIdsTransformer ( sourceDb , targetDb , { danglingPredecessorsBehavior : "reject" } ) ; await expect ( ignoreDanglingPredecessorsDisabledTransformer . processAll ( ) ) . to . be . rejectedWith ( /Found a reference to an element "[^"]*" that doesn't exist/ ) ; const ignoreDanglingPredecessorsEnabledTransformer = new ShiftElemIdsTransformer ( sourceDb , targetDb , { danglingPredecessorsBehavior : "ignore" } ) ; await expect ( ignoreDanglingPredecessorsEnabledTransformer . processAll ( ) ) . not . to . be . rejected ; targetDb . saveChanges ( ) ; expect ( sourceDb . elements . tryGetElement ( physicalObjects [ 1 ] . id ) ) . to . be . undefined ; const displayStyleInSource = sourceDb . elements . getElement < DisplayStyle3d > ( displayStyleId ) ; expect ( [ ... displayStyleInSource . settings . excludedElementIds ] ) . to . include ( physicalObjects [ 1 ] . id ) ; const displayStyleInTargetId = ignoreDanglingPredecessorsEnabledTransformer . context . findTargetElementId ( displayStyleId ) ; const displayStyleInTarget = targetDb . elements . getElement < DisplayStyle3d > ( displayStyleInTargetId ) ; const physObjsInTarget = physicalObjects . map ( ( physObjInSource ) => { const physObjInTargetId = ignoreDanglingPredecessorsEnabledTransformer . context . findTargetElementId ( physObjInSource . id ) ; return { ... physObjInSource , id : physObjInTargetId } ; } ) ; expect ( Id64 . isValidId64 ( physObjsInTarget [ 0 ] . id ) ) . to . be . true ; expect ( Id64 . isValidId64 ( physObjsInTarget [ 1 ] . id ) ) . not . to . be . true ; expect ( [ ... displayStyleInTarget . settings . excludedElementIds ] ) . to . deep . equal ( physObjsInTarget . filter ( ( { id } ) => Id64 . isValidId64 ( id ) ) . map ( ( { id } ) => id ) ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; it ( "exports aspects of deferred elements" , async ( ) => { const sourceDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "DeferredElementWithAspects-Source.bim" ) ; const sourceDb = SnapshotDb . createEmpty ( sourceDbPath , { rootSubject : { name : "deferred-element-with-aspects" } } ) ; const testSchema1Path = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "TestSchema1.ecschema.xml" ) ; IModelJsFs . writeFileSync ( testSchema1Path , `<?xml version="1.0" encoding="UTF-8"?>
      <ECSchema schemaName="TestSchema1" alias="ts1" version="01.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.1">
          <ECSchemaReference name="BisCore" version="01.00" alias="bis"/>
          <ECEntityClass typeName="MyUniqueAspect" description="A test unique aspect" displayLabel="a test unique aspect" modifier="Sealed">
            <BaseClass>bis:ElementUniqueAspect</BaseClass>
            <ECProperty propertyName="MyProp1" typeName="string"/>
        </ECEntityClass>
      </ECSchema>` ) ; await sourceDb . importSchemas ( [ testSchema1Path ] ) ; const myPhysicalModelId = PhysicalModel . insert ( sourceDb , IModelDb . rootSubjectId , "MyPhysicalModel" ) ; const mySpatialCategId = SpatialCategory . insert ( sourceDb , IModelDb . dictionaryId , "MySpatialCateg" , { color : ColorDef . black . toJSON ( ) } ) ; const myPhysicalObjId = sourceDb . elements . insertElement ( { classFullName : PhysicalObject . classFullName , model : myPhysicalModelId , category : mySpatialCategId , code : Code . createEmpty ( ) , userLabel : `MyPhysicalObject` , geom : IModelTestUtils . createBox ( Point3d . create ( 1 , 1 , 1 ) ) , placement : Placement3d . fromJSON ( { origin : { x : 1 } , angles : { } } ) } as PhysicalElementProps ) ; const myDisplayStyleId = DisplayStyle3d . insert ( sourceDb , IModelDb . dictionaryId , "MyDisplayStyle3d" , { excludedElements : [ myPhysicalObjId ] } ) ; const sourceRepositoryId = IModelTestUtils . insertRepositoryLink ( sourceDb , "external.repo" , "https://external.example.com/folder/external.repo" , "TEST" ) ; const sourceExternalSourceId = IModelTestUtils . insertExternalSource ( sourceDb , sourceRepositoryId , "HypotheticalDisplayConfigurer" ) ; const multiAspectProps = { classFullName : ExternalSourceAspect . classFullName , element : { id : myDisplayStyleId , relClassName : ElementOwnsExternalSourceAspects . classFullName } , scope : { id : sourceExternalSourceId } , source : { id : sourceExternalSourceId } , identifier : "ID" , kind : ExternalSourceAspect . Kind . Element } ; sourceDb . elements . insertAspect ( multiAspectProps ) ; const uniqueAspectProps = { classFullName : "TestSchema1:MyUniqueAspect" , element : { id : myDisplayStyleId , relClassName : ElementOwnsUniqueAspect . classFullName } , myProp1 : "prop_value" } ; sourceDb . elements . insertAspect ( uniqueAspectProps ) ; sourceDb . saveChanges ( ) ; const targetDbPath = IModelTestUtils . prepareOutputFile ( "IModelTransformer" , "PreserveIdOnTestModel-Target.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetDbPath , { rootSubject : sourceDb . rootSubject } ) ; class PublicSkipElementTransformer extends IModelTransformer { public override skipElement ( ... args ) { super . skipElement ( ... args ) ; } } const transformer = new PublicSkipElementTransformer ( sourceDb , targetDb , { includeSourceProvenance : true , noProvenance : true } ) ; const skipElementSpy = sinon . spy ( transformer , "skipElement" ) ; await transformer . processSchemas ( ) ; await transformer . processAll ( ) ; assert ( skipElementSpy . called ) ; targetDb . saveChanges ( ) ; const targetExternalSourceAspects = new Array < any > ( ) ; const targetMyUniqueAspects = new Array < any > ( ) ; targetDb . withStatement ( "SELECT * FROM bis.ExternalSourceAspect" , ( stmt ) => targetExternalSourceAspects . push ( ... stmt ) ) ; targetDb . withStatement ( "SELECT * FROM TestSchema1.MyUniqueAspect" , ( stmt ) => targetMyUniqueAspects . push ( ... stmt ) ) ; expect ( targetMyUniqueAspects ) . to . have . lengthOf ( 1 ) ; expect ( targetMyUniqueAspects [ 0 ] . myProp1 ) . to . equal ( uniqueAspectProps . myProp1 ) ; expect ( targetExternalSourceAspects ) . to . have . lengthOf ( 1 ) ; expect ( targetExternalSourceAspects [ 0 ] . identifier ) . to . equal ( multiAspectProps . identifier ) ; sinon . restore ( ) ; sourceDb . close ( ) ; targetDb . close ( ) ; } ) ; } ) ;