import { assert } from "chai" ; import * as path from "path" ; import { DbResult , Id64 , Id64Set , Id64String , Logger , LogLevel } from "@itwin/core-bentley" ; import { Angle , Point2d , Point3d , Range2d , Range3d , YawPitchRollAngles } from "@itwin/core-geometry" ; import { DefinitionContainer , DefinitionGroup , DefinitionGroupGroupsDefinitions , DefinitionModel , DocumentListModel , Drawing , DrawingCategory , DrawingGraphic , DrawingModel , ECSqlStatement , Element , ElementOwnsChildElements , EntityClassType , IModelDb , IModelJsFs , LinkElement , PhysicalElement , PhysicalElementIsOfType , PhysicalModel , PhysicalObject , PhysicalType , RecipeDefinitionElement , RepositoryLink , SnapshotDb , SpatialCategory , TemplateRecipe2d , TemplateRecipe3d , TypeDefinitionElement , } from "@itwin/core-backend" ; import { IModelTestUtils , KnownTestLocations } from "@itwin/core-backend/lib/cjs/test" ; import { Code , CodeScopeSpec , DefinitionElementProps , GeometricElement2dProps , GeometryStreamProps , IModel , PhysicalElementProps , Placement2d , Placement3d , RepositoryLinkProps , SubCategoryAppearance , } from "@itwin/core-common" ; import { IModelImporter , IModelTransformer , IModelTransformOptions , TemplateModelCloner , TransformerLoggerCategory } from "../../core-transformer" ; const createClassViews = false ; note note note note /** This function mocks a Catalog Connector that reads ACME Equipment product data and outputs a catalog of components.
 * Notes about this catalog:
 * - Utilizes the TestDomain schema
 * - Has templates for the physical perspective and for a drawing perspective.
 * - Groups the physical template with a corresponding drawing template.
 */ async function createAcmeCatalog ( dbFile ) { const db = SnapshotDb . createEmpty ( dbFile , { rootSubject : { name : "ACME Equipment" } , createClassViews } ) ; const domainSchemaFilePath = path . join ( KnownTestLocations . assetsDir , "TestDomain.ecschema.xml" ) ; await db . importSchemas ( [ domainSchemaFilePath ] ) ; const manufacturerName = "ACME" ; const productLineName = `${ manufacturerName } Product Line A` ; const containerCodeSpecId = db . codeSpecs . insert ( "ACME:Equipment" , CodeScopeSpec . Type . Repository ) ; const templateGroupCodeSpecId = db . codeSpecs . insert ( "ACME:TemplateGroup" , CodeScopeSpec . Type . Model ) ; const containerCode = createContainerCode ( containerCodeSpecId , productLineName ) ; const containerId = DefinitionContainer . insert ( db , IModel . dictionaryId , containerCode ) ; const spatialCategoryId = SpatialCategory . insert ( db , containerId , "Equipment" , new SubCategoryAppearance ( ) ) ; const drawingCategoryId = DrawingCategory . insert ( db , containerId , "Symbols" , new SubCategoryAppearance ( ) ) ; const codeValue1 = "A-1 Series" ; const physicalGeomProps1 = IModelTestUtils . createBox ( new Point3d ( 1 , 1 , 1 ) ) ; const physicalRecipeId1 = insertEquipmentRecipe ( db , containerId , spatialCategoryId , codeValue1 , physicalGeomProps1 ) ; insertEquipmentType ( db , containerId , "A-101" , physicalRecipeId1 , manufacturerName , productLineName ) ; insertEquipmentType ( db , containerId , "A-102" , physicalRecipeId1 , manufacturerName , productLineName ) ; const symbolGeomProps1 = IModelTestUtils . createRectangle ( Point2d . create ( 1 , 1 ) ) ; const symbolRecipeId1 = insertSymbolRecipe ( db , containerId , drawingCategoryId , codeValue1 , symbolGeomProps1 ) ; const groupProps1 = { classFullName : DefinitionGroup . classFullName , model : containerId , code : new Code ( { spec : templateGroupCodeSpecId , scope : containerId , value : codeValue1 } ) } ; const groupId1 = db . elements . insertElement ( groupProps1 ) ; DefinitionGroupGroupsDefinitions . insert ( db , groupId1 , physicalRecipeId1 ) ; DefinitionGroupGroupsDefinitions . insert ( db , groupId1 , symbolRecipeId1 ) ; const codeValue2 = "A-2 Series" ; const physicalGeomProps2 = IModelTestUtils . createBox ( new Point3d ( 2 , 2 , 2 ) ) ; const physicalRecipeId2 = insertEquipmentRecipe ( db , containerId , spatialCategoryId , codeValue2 , physicalGeomProps2 ) ; insertEquipmentType ( db , containerId , "A-201" , physicalRecipeId2 , manufacturerName , productLineName ) ; insertEquipmentType ( db , containerId , "A-202" , physicalRecipeId2 , manufacturerName , productLineName ) ; insertEquipmentType ( db , containerId , "A-203" , physicalRecipeId2 , manufacturerName , productLineName ) ; const symbolGeomProps2 = IModelTestUtils . createRectangle ( Point2d . create ( 2 , 2 ) ) ; const symbolRecipeId2 = insertSymbolRecipe ( db , containerId , drawingCategoryId , codeValue2 , symbolGeomProps2 ) ; const groupProps2 = { classFullName : DefinitionGroup . classFullName , model : containerId , code : new Code ( { spec : templateGroupCodeSpecId , scope : containerId , value : codeValue2 } ) } ; const groupId2 = db . elements . insertElement ( groupProps2 ) ; DefinitionGroupGroupsDefinitions . insert ( db , groupId2 , physicalRecipeId2 ) ; DefinitionGroupGroupsDefinitions . insert ( db , groupId2 , symbolRecipeId2 ) ; const codeValue3 = "A-3 Series" ; const physicalGeomProps3 = IModelTestUtils . createBox ( new Point3d ( 3 , 3 , 3 ) ) ; const physicalRecipeId3 = insertEquipmentRecipe ( db , containerId , spatialCategoryId , codeValue3 , physicalGeomProps3 ) ; insertEquipmentType ( db , containerId , "A-301" , physicalRecipeId3 , manufacturerName , productLineName ) ; const symbolGeomProps3 = IModelTestUtils . createRectangle ( Point2d . create ( 3 , 3 ) ) ; const symbolRecipeId3 = insertSymbolRecipe ( db , containerId , drawingCategoryId , codeValue3 , symbolGeomProps3 ) ; const groupProps3 = { classFullName : DefinitionGroup . classFullName , model : containerId , code : new Code ( { spec : templateGroupCodeSpecId , scope : containerId , value : codeValue3 } ) } ; const groupId3 = db . elements . insertElement ( groupProps3 ) ; DefinitionGroupGroupsDefinitions . insert ( db , groupId3 , physicalRecipeId3 ) ; DefinitionGroupGroupsDefinitions . insert ( db , groupId3 , symbolRecipeId3 ) ; db . saveChanges ( ) ; db . close ( ) ; } /** This function mocks a Catalog Connector that reads Best Equipment product data and outputs a catalog of components.
 * Notes about this catalog:
 * - Utilizes the TestDomain schema
 * - Demonstrates multiple containers (mapped to product line in this sample) for a single catalog.
 * - Happens to only have templates for the physical perspective (no symbols, no groups)
*/ async function createBestCatalog ( dbFile ) { const db = SnapshotDb . createEmpty ( dbFile , { rootSubject : { name : "Best Equipment" } } ) ; const domainSchemaFilePath = path . join ( KnownTestLocations . assetsDir , "TestDomain.ecschema.xml" ) ; await db . importSchemas ( [ domainSchemaFilePath ] ) ; const manufacturerName = "Best" ; const containerCodeSpecId = db . codeSpecs . insert ( `${ manufacturerName }:Equipment` , CodeScopeSpec . Type . Repository ) ; const productLineNameB = `${ manufacturerName } Product Line B` ; const containerCodeB = createContainerCode ( containerCodeSpecId , productLineNameB ) ; const containerIdB = DefinitionContainer . insert ( db , IModel . dictionaryId , containerCodeB ) ; const categoryIdB = SpatialCategory . insert ( db , containerIdB , "Equipment" , new SubCategoryAppearance ( ) ) ; const codeValueB2 = "B-2 Series" ; const physicalGeomPropsB2 = IModelTestUtils . createCylinder ( 2 ) ; const physicalRecipeIdB2 = insertEquipmentRecipe ( db , containerIdB , categoryIdB , codeValueB2 , physicalGeomPropsB2 ) ; insertEquipmentType ( db , containerIdB , "B-201" , physicalRecipeIdB2 , manufacturerName , productLineNameB ) ; insertEquipmentType ( db , containerIdB , "B-202" , physicalRecipeIdB2 , manufacturerName , productLineNameB ) ; const codeValueB3 = "B-3 Series" ; const physicalGeomPropsB3 = IModelTestUtils . createCylinder ( 3 ) ; const physicalRecipeIdB3 = insertEquipmentRecipe ( db , containerIdB , categoryIdB , codeValueB3 , physicalGeomPropsB3 ) ; insertEquipmentType ( db , containerIdB , "B-301" , physicalRecipeIdB3 , manufacturerName , productLineNameB ) ; insertEquipmentType ( db , containerIdB , "B-302" , physicalRecipeIdB3 , manufacturerName , productLineNameB ) ; insertEquipmentType ( db , containerIdB , "B-303" , physicalRecipeIdB3 , manufacturerName , productLineNameB ) ; insertEquipmentType ( db , containerIdB , "B-304" , physicalRecipeIdB3 , manufacturerName , productLineNameB ) ; const productLineNameD = `${ manufacturerName } Product Line D` ; const containerCodeD = createContainerCode ( containerCodeSpecId , productLineNameD ) ; const containerIdD = DefinitionContainer . insert ( db , IModel . dictionaryId , containerCodeD ) ; const categoryIdD = SpatialCategory . insert ( db , containerIdD , "Equipment" , new SubCategoryAppearance ( ) ) ; const codeValueD1 = "D-1 Series" ; const physicalGeomPropsD1 = IModelTestUtils . createCylinder ( 1 ) ; const physicalRecipeIdD1 = insertEquipmentRecipe ( db , containerIdD , categoryIdD , codeValueD1 , physicalGeomPropsD1 ) ; insertEquipmentType ( db , containerIdD , "D-101" , physicalRecipeIdD1 , manufacturerName , productLineNameD ) ; insertEquipmentType ( db , containerIdD , "D-102" , physicalRecipeIdD1 , manufacturerName , productLineNameD ) ; const codeValueD2 = "D-2 Series" ; const physicalGeomPropsD2 = IModelTestUtils . createCylinder ( 2 ) ; const physicalRecipeIdD2 = insertEquipmentRecipe ( db , containerIdD , categoryIdD , codeValueD2 , physicalGeomPropsD2 ) ; insertEquipmentType ( db , containerIdD , "D-201" , physicalRecipeIdD2 , manufacturerName , productLineNameD ) ; insertEquipmentType ( db , containerIdD , "D-202" , physicalRecipeIdD2 , manufacturerName , productLineNameD ) ; db . saveChanges ( ) ; db . close ( ) ; } /** This function creates test catalog components.
 * Notes about this catalog:
 * - Utilizes only the builtin BisCore and Generic schemas
 * - Has an example template (Assembly) that contains multiple elements
 * - Has no associated PhysicalTypes
*/ async function createTestCatalog ( dbFile ) { const db = SnapshotDb . createEmpty ( dbFile , { rootSubject : { name : "Test Catalog" } , createClassViews } ) ; const containerCodeSpecId = db . codeSpecs . insert ( "Test:Components" , CodeScopeSpec . Type . Repository ) ; const containerCode = createContainerCode ( containerCodeSpecId , "Test Components" ) ; const containerId = DefinitionContainer . insert ( db , IModel . dictionaryId , containerCode ) ; const spatialCategoryId = SpatialCategory . insert ( db , containerId , "Test Components" , new SubCategoryAppearance ( ) ) ; const drawingCategoryId = DrawingCategory . insert ( db , containerId , "Test Components" , new SubCategoryAppearance ( ) ) ; const cylinderTemplateId = TemplateRecipe3d . insert ( db , containerId , "Cylinder Template" ) ; const cylinderTemplateModel = db . models . getModel < PhysicalModel > ( cylinderTemplateId , PhysicalModel ) ; assert . isTrue ( cylinderTemplateModel . isTemplate ) ; const cylinderProps = { classFullName : PhysicalObject . classFullName , model : cylinderTemplateId , category : spatialCategoryId , code : Code . createEmpty ( ) , userLabel : "Cylinder" , placement : { origin : Point3d . createZero ( ) , angles : { yaw : 0 , pitch : 0 , roll : 0 } } , geom : IModelTestUtils . createCylinder ( 1 ) } ; db . elements . insertElement ( cylinderProps ) ; const assemblyTemplateId = TemplateRecipe3d . insert ( db , containerId , "Assembly Template" ) ; assert . exists ( db . models . getModel < PhysicalModel > ( assemblyTemplateId ) ) ; const assemblyHeadProps = { classFullName : PhysicalObject . classFullName , model : assemblyTemplateId , category : spatialCategoryId , code : Code . createEmpty ( ) , userLabel : "Assembly Head" , placement : { origin : Point3d . createZero ( ) , angles : { yaw : 0 , pitch : 0 , roll : 0 } } , geom : IModelTestUtils . createCylinder ( 1 ) } ; const assemblyHeadId = db . elements . insertElement ( assemblyHeadProps ) ; const childBoxProps = { classFullName : PhysicalObject . classFullName , model : assemblyTemplateId , category : spatialCategoryId , parent : new ElementOwnsChildElements ( assemblyHeadId ) , code : Code . createEmpty ( ) , userLabel : "Child" , placement : { origin : Point3d . create ( 2 , 0 , 0 ) , angles : { yaw : 0 , pitch : 0 , roll : 0 } } , geom : IModelTestUtils . createBox ( Point3d . create ( 1 , 1 , 1 ) ) } ; db . elements . insertElement ( childBoxProps ) ; const drawingGraphicTemplateId = TemplateRecipe2d . insert ( db , containerId , "DrawingGraphic Template" ) ; const drawingGraphicTemplateModel = db . models . getModel < DrawingModel > ( drawingGraphicTemplateId , DrawingModel ) ; assert . isTrue ( drawingGraphicTemplateModel . isTemplate ) ; const drawingGraphicProps = { classFullName : DrawingGraphic . classFullName , model : drawingGraphicTemplateId , category : drawingCategoryId , code : Code . createEmpty ( ) , userLabel : "DrawingGraphic" , placement : { origin : Point2d . createZero ( ) , angle : 0 } , geom : IModelTestUtils . createRectangle ( Point2d . create ( 1 , 1 ) ) } ; db . elements . insertElement ( drawingGraphicProps ) ; db . saveChanges ( ) ; db . close ( ) ; } note function indexCatalog ( db , outputFile ) { IModelJsFs . writeFileSync ( outputFile , `# ${ db . rootSubject . name }\n` ) ; if ( db . rootSubject . description ) { IModelJsFs . appendFileSync ( outputFile , `${ db . rootSubject . description }\n` ) ; } const containerIds = queryContainerIds ( db ) ; for ( const containerId of containerIds ) { const container = db . elements . getElement < DefinitionContainer > ( containerId , DefinitionContainer ) ; IModelJsFs . appendFileSync ( outputFile , `## ${ container . code . value }\n` ) ; const templateRecipeIds = queryTemplateRecipeIds ( db , containerId ) ; if ( templateRecipeIds . size > 0 ) { IModelJsFs . appendFileSync ( outputFile , `### TemplateRecipes\n` ) ; for ( const templateRecipeId of templateRecipeIds ) { const templateRecipe = db . elements . getElement < RecipeDefinitionElement > ( templateRecipeId , RecipeDefinitionElement ) ; IModelJsFs . appendFileSync ( outputFile , `#### ${ templateRecipe . code . value }\n` ) ; const typeDefinitionIds = queryTypeDefinitionIds ( db , templateRecipeId ) ; for ( const typeDefinitionId of typeDefinitionIds ) { const typeDefinition = db . elements . getElement < TypeDefinitionElement > ( typeDefinitionId , TypeDefinitionElement ) ; IModelJsFs . appendFileSync ( outputFile , `- ${ typeDefinition . code . value }\n` ) ; } } } const groupIds = queryDefinitionGroupIds ( db , containerId ) ; if ( groupIds . size > 0 ) { IModelJsFs . appendFileSync ( outputFile , `### DefinitionGroups\n` ) ; for ( const groupId of groupIds ) { const group = db . elements . getElement < DefinitionGroup > ( groupId , DefinitionGroup ) ; IModelJsFs . appendFileSync ( outputFile , `#### ${ group . code . value }\n` ) ; const memberIds = queryDefinitionGroupMemberIds ( db , groupId ) ; for ( const memberId of memberIds ) { const templateRecipe = db . elements . getElement < RecipeDefinitionElement > ( memberId , RecipeDefinitionElement ) ; IModelJsFs . appendFileSync ( outputFile , `- ${ templateRecipe . code . value }\n` ) ; } } } } } note function insertEquipmentRecipe ( db , modelId , categoryId , codeValue , geom ) { const templateId = TemplateRecipe3d . insert ( db , modelId , codeValue ) ; const equipmentProps = { classFullName : "TestDomain:Equipment" , model : templateId , category : categoryId , code : Code . createEmpty ( ) , userLabel : codeValue , placement : { origin : Point3d . createZero ( ) , angles : { yaw : 0 , pitch : 0 , roll : 0 } } , geom } ; db . elements . insertElement ( equipmentProps ) ; return templateId ; } function insertSymbolRecipe ( db , modelId , categoryId , codeValue , geom ) { const templateId = TemplateRecipe2d . insert ( db , modelId , codeValue ) ; const drawingGraphicProps = { classFullName : DrawingGraphic . classFullName , model : templateId , category : categoryId , code : Code . createEmpty ( ) , userLabel : codeValue , placement : { origin : Point2d . createZero ( ) , angle : 0 } , geom } ; db . elements . insertElement ( drawingGraphicProps ) ; return templateId ; } function createContainerCode ( codeSpecId , codeValue ) { return new Code ( { spec : codeSpecId , scope : IModel . rootSubjectId , value : codeValue } ) ; } note function queryContainerIds ( db ) { const sql = `SELECT ECInstanceId FROM ${ DefinitionContainer . classFullName } WHERE Model.Id=:modelId` ; const containerIds = new Set < Id64String > ( ) ; db . withPreparedStatement ( sql , ( statement ) : void => { statement . bindId ( "modelId" , IModel . dictionaryId ) ; while ( DbResult . BE_SQLITE_ROW === statement . step ( ) ) { containerIds . add ( statement . getValue ( 0 ) . getId ( ) ) ; } } ) ; return containerIds ; } note function queryDefinitionGroupIds ( db , containerId ) { const sql = `SELECT ECInstanceId FROM ${ DefinitionGroup . classFullName } WHERE Model.Id=:modelId` ; const groupIds = new Set < Id64String > ( ) ; db . withPreparedStatement ( sql , ( statement ) : void => { statement . bindId ( "modelId" , containerId ) ; while ( DbResult . BE_SQLITE_ROW === statement . step ( ) ) { groupIds . add ( statement . getValue ( 0 ) . getId ( ) ) ; } } ) ; return groupIds ; } /** Query for the members of a DefinitionGroup. */ function queryDefinitionGroupMemberIds ( db , groupId ) { const sql = `SELECT TargetECInstanceId FROM ${ DefinitionGroupGroupsDefinitions . classFullName } WHERE SourceECInstanceId=:groupId` ; const memberIds = new Set < Id64String > ( ) ; db . withPreparedStatement ( sql , ( statement ) : void => { statement . bindId ( "groupId" , groupId ) ; while ( DbResult . BE_SQLITE_ROW === statement . step ( ) ) { memberIds . add ( statement . getValue ( 0 ) . getId ( ) ) ; } } ) ; return memberIds ; } /** This mocks the concept of a standard domain category. */ function queryEquipmentCategory ( db , modelId ) { const code = SpatialCategory . createCode ( db , modelId , "Equipment" ) ; return db . elements . queryElementIdByCode ( code ) ; } /** This mocks a domain-specific subclass of PhysicalType that would be defined by an aligned domain schema. */ function insertEquipmentType ( db , modelId , codeValue , recipeId , manufacturerName , productLineName ) { const equipmentTypeProps = { classFullName : "TestDomain:EquipmentType" , model : modelId , code : createEquipmentTypeCode ( db , modelId , codeValue ) , recipe : { id : recipeId , relClassName : "BisCore:PhysicalTypeHasTemplateRecipe" } , manufacturerName , productLineName } ; return db . elements . insertElement ( equipmentTypeProps ) ; } function createEquipmentTypeCode ( db , modelId , codeValue ) { return PhysicalType . createCode ( db , modelId , codeValue ) ; } function queryEquipmentTypeId ( db , modelId , codeValue ) { const code = createEquipmentTypeCode ( db , modelId , codeValue ) ; return db . elements . queryElementIdByCode ( code ) ; } /** Query for all TypeDefinitions that reference a particular template recipe. */ function queryTypeDefinitionIds ( db , templateRecipeId ) { const sql = `SELECT ECInstanceId FROM ${ TypeDefinitionElement . classFullName } WHERE Recipe.Id=:templateRecipeId` ; const typeDefinitionIds = new Set < Id64String > ( ) ; db . withPreparedStatement ( sql , ( statement ) : void => { statement . bindId ( "templateRecipeId" , templateRecipeId ) ; while ( DbResult . BE_SQLITE_ROW === statement . step ( ) ) { typeDefinitionIds . add ( statement . getValue ( 0 ) . getId ( ) ) ; } } ) ; return typeDefinitionIds ; } /** Query for all template recipes in a particular model/container. */ function queryTemplateRecipeIds ( db , containerId ) { const sql = `SELECT ECInstanceId FROM ${ RecipeDefinitionElement . classFullName } WHERE Model.Id=:modelId` ; const templateRecipeIds = new Set < Id64String > ( ) ; db . withPreparedStatement ( sql , ( statement ) : void => { statement . bindId ( "modelId" , containerId ) ; while ( DbResult . BE_SQLITE_ROW === statement . step ( ) ) { templateRecipeIds . add ( statement . getValue ( 0 ) . getId ( ) ) ; } } ) ; return templateRecipeIds ; } note function queryEquipmentId ( db , templateModelId ) { const sql = `SELECT ECInstanceId FROM TestDomain:Equipment WHERE Model.Id=:modelId LIMIT 1` ; return db . withPreparedStatement ( sql , ( statement ) : Id64String | undefined => { statement . bindId ( "modelId" , templateModelId ) ; return DbResult . BE_SQLITE_ROW === statement . step ( ) ? statement . getValue ( 0 ) . getId ( ) : undefined ; } ) ; } function countElementsInModel ( db , classFullName , modelId ) { return db . withPreparedStatement ( `SELECT COUNT(*) FROM ${ classFullName } WHERE Model.Id=:modelId` , ( statement ) : number => { statement . bindId ( "modelId" , modelId ) ; return DbResult . BE_SQLITE_ROW === statement . step ( ) ? statement . getValue ( 0 ) . getInteger ( ) : 0 ; } ) ; } /** Create a RepositoryLink for the catalog that will scope the provenance for elements imported from the catalog. */ function insertCatalogRepositoryLink ( iModelDb , codeValue , url ) { const code = LinkElement . createCode ( iModelDb , IModel . repositoryModelId , codeValue ) ; const repositoryLinkId = iModelDb . elements . queryElementIdByCode ( code ) ; if ( undefined === repositoryLinkId ) { const repositoryLinkProps = { classFullName : RepositoryLink . classFullName , model : IModel . repositoryModelId , code , url , format : "Catalog" } ; return iModelDb . elements . insertElement ( repositoryLinkProps ) ; } return repositoryLinkId ; } /** Specialization of IModelTransformer designed to import definitions from a catalog. */ class CatalogImporter extends IModelTransformer { private _targetSpatialCategories : Map < string , Id64String > | undefined ; private _targetDrawingCategories : Map < string , Id64String > | undefined ; param sourceDb param targetDb param targetScopeElementId param targetSpatialCategories param targetDrawingCategories public constructor ( sourceDb , targetDb , targetScopeElementId ? , targetSpatialCategories ? , targetDrawingCategories ? ) { const options = { targetScopeElementId , noProvenance : targetScopeElementId ? undefined : true } ; const target = new IModelImporter ( targetDb , { autoExtendProjectExtents : false } ) ; super ( sourceDb , target , options ) ; this . _targetSpatialCategories = targetSpatialCategories ; this . _targetDrawingCategories = targetDrawingCategories ; } public async importDefinitionContainers ( ) { const containerIds = queryContainerIds ( this . sourceDb ) ; for ( const containerId of containerIds ) { await this . importDefinitionContainer ( containerId ) ; } } public async importDefinitionContainer ( sourceContainerId ) { const sourceContainer = this . sourceDb . elements . getElement < DefinitionContainer > ( sourceContainerId , DefinitionContainer ) ; const sourceContainerCodeSpec = this . sourceDb . codeSpecs . getById ( sourceContainer . code . spec ) ; let targetContainerId ; try { const targetContainerCodeSpec = this . targetDb . codeSpecs . getByName ( sourceContainerCodeSpec . name ) ; const targetContainerCode = new Code ( { spec : targetContainerCodeSpec . id , scope : IModel . rootSubjectId , value : sourceContainer . code . value } ) ; targetContainerId = this . targetDb . elements . queryElementIdByCode ( targetContainerCode ) ; } catch ( error ) { } if ( undefined === targetContainerId ) { this . _remapSpatialCategories ( ) ; this . _remapDrawingCategories ( ) ; await this . exporter . exportElement ( sourceContainerId ) ; return this . exporter . exportModel ( sourceContainerId ) ; } } private _remapSpatialCategories ( ) { if ( undefined === this . _targetSpatialCategories || this . _targetSpatialCategories . size === 0 ) { return ; } const sql = `SELECT ECInstanceId,CodeValue FROM ${ SpatialCategory . classFullName }` ; this . sourceDb . withPreparedStatement ( sql , ( statement ) : void => { while ( DbResult . BE_SQLITE_ROW === statement . step ( ) ) { const sourceCategoryId = statement . getValue ( 0 ) . getId ( ) ; const sourceCategoryName = statement . getValue ( 1 ) . getString ( ) ; if ( this . _targetSpatialCategories ! . has ( sourceCategoryName ) ) { const targetCategoryId = this . _targetSpatialCategories ! . get ( sourceCategoryName ) ! ; this . context . remapElement ( sourceCategoryId , targetCategoryId ) ; this . importer . doNotUpdateElementIds . add ( targetCategoryId ) ; } } } ) ; } private _remapDrawingCategories ( ) { if ( undefined === this . _targetDrawingCategories || this . _targetDrawingCategories . size === 0 ) { return ; } const sql = `SELECT ECInstanceId,CodeValue FROM ${ DrawingCategory . classFullName }` ; this . sourceDb . withPreparedStatement ( sql , ( statement ) : void => { while ( DbResult . BE_SQLITE_ROW === statement . step ( ) ) { const sourceCategoryId = statement . getValue ( 0 ) . getId ( ) ; const sourceCategoryName = statement . getValue ( 1 ) . getString ( ) ; if ( this . _targetDrawingCategories ! . has ( sourceCategoryName ) ) { const targetCategoryId = this . _targetDrawingCategories ! . get ( sourceCategoryName ) ! ; this . context . remapElement ( sourceCategoryId , targetCategoryId ) ; this . importer . doNotUpdateElementIds . add ( targetCategoryId ) ; } } } ) ; } } /** Catalog test fixture */ describe ( "Catalog" , ( ) => { const outputDir = path . join ( KnownTestLocations . outputDir , "Catalog" ) ; const acmeCatalogDbFile = IModelTestUtils . prepareOutputFile ( "Catalog" , "AcmeEquipment.catalog" ) ; const bestCatalogDbFile = IModelTestUtils . prepareOutputFile ( "Catalog" , "BestEquipment.catalog" ) ; const testCatalogDbFile = IModelTestUtils . prepareOutputFile ( "Catalog" , "Test.catalog" ) ; 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 ) ; } await createAcmeCatalog ( acmeCatalogDbFile ) ; await createBestCatalog ( bestCatalogDbFile ) ; await createTestCatalog ( testCatalogDbFile ) ; } ) ; it ( "should index catalog" , async ( ) => { const acmeCatalogDb = SnapshotDb . openFile ( acmeCatalogDbFile ) ; indexCatalog ( acmeCatalogDb , `${ acmeCatalogDb . pathName }.md` ) ; acmeCatalogDb . close ( ) ; const bestCatalogDb = SnapshotDb . openFile ( bestCatalogDbFile ) ; indexCatalog ( bestCatalogDb , `${ bestCatalogDb . pathName }.md` ) ; bestCatalogDb . close ( ) ; const testCatalogDb = SnapshotDb . openFile ( testCatalogDbFile ) ; indexCatalog ( testCatalogDb , `${ testCatalogDb . pathName }.md` ) ; testCatalogDb . close ( ) ; } ) ; it ( "should import from catalog" , async ( ) => { const iModelFile = IModelTestUtils . prepareOutputFile ( "Catalog" , "Facility.bim" ) ; const iModelDb = SnapshotDb . createEmpty ( iModelFile , { rootSubject : { name : "Facility" } , createClassViews } ) ; const domainSchemaFilePath = path . join ( KnownTestLocations . assetsDir , "TestDomain.ecschema.xml" ) ; await iModelDb . importSchemas ( [ domainSchemaFilePath ] ) ; const physicalModelId = PhysicalModel . insert ( iModelDb , IModel . rootSubjectId , "Physical" ) ; const spatialCategoryId = SpatialCategory . insert ( iModelDb , IModel . dictionaryId , "Equipment" , new SubCategoryAppearance ( ) ) ; const standardSpatialCategories = new Map < string , Id64String > ( ) ; standardSpatialCategories . set ( "Equipment" , spatialCategoryId ) ; const drawingListModelId = DocumentListModel . insert ( iModelDb , IModel . rootSubjectId , "Drawings" ) ; const drawingId = Drawing . insert ( iModelDb , drawingListModelId , "Drawing1" ) ; const drawingCategoryId = DrawingCategory . insert ( iModelDb , IModel . dictionaryId , "Symbols" , new SubCategoryAppearance ( ) ) ; const standardDrawingCategories = new Map < string , Id64String > ( ) ; standardDrawingCategories . set ( "Symbols" , drawingCategoryId ) ; { const catalogDb = SnapshotDb . openFile ( acmeCatalogDbFile ) ; const catalogContainerIds = queryContainerIds ( catalogDb ) ; assert . equal ( catalogContainerIds . size , 1 ) ; const catalogContainer = catalogDb . elements . getElement < DefinitionContainer > ( catalogContainerIds . values ( ) . next ( ) . value , DefinitionContainer ) ; const catalogContainerCodeSpec = catalogDb . codeSpecs . getById ( catalogContainer . code . spec ) ; const catalogContainerCodeValue = catalogContainer . code . value ; const catalogRepositoryLinkId = insertCatalogRepositoryLink ( iModelDb , path . basename ( acmeCatalogDbFile ) , acmeCatalogDbFile ) ; const catalogImporter = new CatalogImporter ( catalogDb , iModelDb , catalogRepositoryLinkId , standardSpatialCategories , standardDrawingCategories ) ; await catalogImporter . importDefinitionContainers ( ) ; catalogImporter . dispose ( ) ; catalogDb . close ( ) ; assert . isTrue ( iModelDb . codeSpecs . hasName ( catalogContainerCodeSpec . name ) ) ; const importedContainerCodeSpec = iModelDb . codeSpecs . getByName ( catalogContainerCodeSpec . name ) ; const importedContainerId = iModelDb . elements . queryElementIdByCode ( createContainerCode ( importedContainerCodeSpec . id , catalogContainerCodeValue ) ) ! ; iModelDb . elements . getElement < DefinitionContainer > ( importedContainerId , DefinitionContainer ) ; iModelDb . models . getModel < DefinitionModel > ( importedContainerId , DefinitionModel ) ; assert . isUndefined ( queryEquipmentCategory ( iModelDb , importedContainerId ) , "Expected category to be remapped" ) ; assert . isTrue ( Id64 . isValidId64 ( queryEquipmentCategory ( iModelDb , IModel . dictionaryId ) ! ) ) ; assert . isTrue ( Id64 . isValidId64 ( queryEquipmentTypeId ( iModelDb , importedContainerId , "A-101" ) ! ) ) ; assert . isTrue ( Id64 . isValidId64 ( queryEquipmentTypeId ( iModelDb , importedContainerId , "A-201" ) ! ) ) ; assert . isTrue ( Id64 . isValidId64 ( queryEquipmentTypeId ( iModelDb , importedContainerId , "A-301" ) ! ) ) ; const templateRecipeIds = queryTemplateRecipeIds ( iModelDb , importedContainerId ) ; assert . equal ( templateRecipeIds . size , 6 ) ; } { const catalogDb = SnapshotDb . openFile ( bestCatalogDbFile ) ; assert . equal ( countElementsInModel ( catalogDb , DefinitionContainer . classFullName , IModel . dictionaryId ) , 2 ) ; const catalogContainerSql = `SELECT ECInstanceId FROM ${ DefinitionContainer . classFullName } WHERE CodeValue=:containerName LIMIT 1` ; const catalogContainerId = catalogDb . withPreparedStatement ( catalogContainerSql , ( statement ) : Id64String => { statement . bindString ( "containerName" , "Best Product Line B" ) ; return DbResult . BE_SQLITE_ROW === statement . step ( ) ? statement . getValue ( 0 ) . getId ( ) : Id64 . invalid ; } ) ; const catalogContainer = catalogDb . elements . getElement < DefinitionContainer > ( catalogContainerId , DefinitionContainer ) ; const catalogContainerCodeSpec = catalogDb . codeSpecs . getById ( catalogContainer . code . spec ) ; const catalogContainerCodeValue = catalogContainer . code . value ; const catalogRepositoryLinkId = insertCatalogRepositoryLink ( iModelDb , path . basename ( bestCatalogDbFile ) , bestCatalogDbFile ) ; const catalogImporter = new CatalogImporter ( catalogDb , iModelDb , catalogRepositoryLinkId , standardSpatialCategories , standardDrawingCategories ) ; await catalogImporter . importDefinitionContainer ( catalogContainerId ) ; catalogImporter . dispose ( ) ; catalogDb . close ( ) ; assert . isTrue ( iModelDb . codeSpecs . hasName ( catalogContainerCodeSpec . name ) ) ; const importedContainerCodeSpec = iModelDb . codeSpecs . getByName ( catalogContainerCodeSpec . name ) ; const importedContainerId = iModelDb . elements . queryElementIdByCode ( createContainerCode ( importedContainerCodeSpec . id , catalogContainerCodeValue ) ) ! ; iModelDb . elements . getElement < DefinitionContainer > ( importedContainerId , DefinitionContainer ) ; iModelDb . models . getModel < DefinitionModel > ( importedContainerId , DefinitionModel ) ; assert . isUndefined ( queryEquipmentCategory ( iModelDb , importedContainerId ) , "Expected category to be remapped" ) ; assert . isTrue ( Id64 . isValidId64 ( queryEquipmentCategory ( iModelDb , IModel . dictionaryId ) ! ) ) ; assert . isTrue ( Id64 . isValidId64 ( queryEquipmentTypeId ( iModelDb , importedContainerId , "B-201" ) ! ) ) ; assert . isTrue ( Id64 . isValidId64 ( queryEquipmentTypeId ( iModelDb , importedContainerId , "B-304" ) ! ) ) ; const templateRecipeIds = queryTemplateRecipeIds ( iModelDb , importedContainerId ) ; assert . equal ( templateRecipeIds . size , 2 ) ; } let testContainerId ; { const catalogDb = SnapshotDb . openFile ( testCatalogDbFile ) ; const catalogContainerIds = queryContainerIds ( catalogDb ) ; assert . equal ( catalogContainerIds . size , 1 ) ; const catalogContainer = catalogDb . elements . getElement < DefinitionContainer > ( catalogContainerIds . values ( ) . next ( ) . value , DefinitionContainer ) ; const catalogContainerCodeSpec = catalogDb . codeSpecs . getById ( catalogContainer . code . spec ) ; const catalogContainerCodeValue = catalogContainer . code . value ; const catalogRepositoryLinkId = insertCatalogRepositoryLink ( iModelDb , path . basename ( testCatalogDbFile ) , testCatalogDbFile ) ; const catalogTemplateRecipeIds = queryTemplateRecipeIds ( catalogDb , catalogContainer . id ) ; assert . equal ( catalogTemplateRecipeIds . size , 3 ) ; const catalogImporter = new CatalogImporter ( catalogDb , iModelDb , catalogRepositoryLinkId ) ; const cylinderTemplateCode = TemplateRecipe3d . createCode ( catalogDb , catalogContainer . id , "Cylinder Template" ) ; const cylinderTemplateId = catalogDb . elements . queryElementIdByCode ( cylinderTemplateCode ) ! ; catalogImporter . exporter . excludeElement ( cylinderTemplateId ) ; await catalogImporter . importDefinitionContainer ( catalogContainer . id ) ; catalogImporter . dispose ( ) ; catalogDb . close ( ) ; assert . isTrue ( iModelDb . codeSpecs . hasName ( catalogContainerCodeSpec . name ) ) ; const importedContainerCodeSpec = iModelDb . codeSpecs . getByName ( catalogContainerCodeSpec . name ) ; testContainerId = iModelDb . elements . queryElementIdByCode ( createContainerCode ( importedContainerCodeSpec . id , catalogContainerCodeValue ) ) ! ; iModelDb . elements . getElement < DefinitionContainer > ( testContainerId , DefinitionContainer ) ; iModelDb . models . getModel < DefinitionModel > ( testContainerId , DefinitionModel ) ; const importedTemplateRecipeIds = queryTemplateRecipeIds ( iModelDb , testContainerId ) ; assert . equal ( importedTemplateRecipeIds . size , 2 ) ; } const importedContainerIds = queryContainerIds ( iModelDb ) ; assert . equal ( importedContainerIds . size , 3 , "Expect 1 container from each of ACME, Best, and Test" ) ; const componentPlacer = new TemplateModelCloner ( iModelDb ) ; const physicalTypeSql = `SELECT ECInstanceId FROM ${ PhysicalType . classFullName }` ; const physicalTypeIds = new Set < Id64String > ( ) ; iModelDb . withPreparedStatement ( physicalTypeSql , ( statement ) : void => { while ( DbResult . BE_SQLITE_ROW === statement . step ( ) ) { physicalTypeIds . add ( statement . getValue ( 0 ) . getId ( ) ) ; } } ) ; let x = 0 ; for ( const physicalTypeId of physicalTypeIds ) { x += 5 ; const physicalType = iModelDb . elements . getElement < PhysicalType > ( physicalTypeId , PhysicalType ) ; if ( physicalType . recipe ?. id ) { iModelDb . elements . getElement < TemplateRecipe3d > ( physicalType . recipe . id , TemplateRecipe3d ) ; const placement = new Placement3d ( new Point3d ( x , 0 ) , new YawPitchRollAngles ( ) , new Range3d ( ) ) ; const templateToInstanceMap = await componentPlacer . placeTemplate3d ( physicalType . recipe . id , physicalModelId , placement ) ; const templateEquipmentId = queryEquipmentId ( iModelDb , physicalType . recipe . id ) ; if ( templateEquipmentId ) { const instanceEquipmentId = templateToInstanceMap . get ( templateEquipmentId ) ; const equipmentClass = iModelDb . getJsClass ( "TestDomain:Equipment" ) as unknown as EntityClassType < Element > ; const equipment = iModelDb . elements . getElement < PhysicalElement > ( instanceEquipmentId ! , equipmentClass ) ; equipment . typeDefinition = new PhysicalElementIsOfType ( physicalTypeId ) ; equipment . update ( ) ; assert . isDefined ( equipment . typeDefinition ?. id ) ; } } } const assemblyTemplateCode = TemplateRecipe3d . createCode ( iModelDb , testContainerId , "Assembly Template" ) ; const assemblyTemplateId = iModelDb . elements . queryElementIdByCode ( assemblyTemplateCode ) ! ; const assemblyLocations = [ Point3d . create ( - 10 , 0 ) , Point3d . create ( - 20 , 0 ) , Point3d . create ( - 30 , 0 ) ] ; for ( const location of assemblyLocations ) { const placement = new Placement3d ( location , new YawPitchRollAngles ( ) , new Range3d ( ) ) ; const templateToInstanceMap = await componentPlacer . placeTemplate3d ( assemblyTemplateId , physicalModelId , placement ) ; assert . isAtLeast ( templateToInstanceMap . size , 2 ) ; for ( const templateElementId of templateToInstanceMap . keys ( ) ) { const templateElement = iModelDb . elements . getElement ( templateElementId ) ; const instanceElement = iModelDb . elements . getElement ( templateToInstanceMap . get ( templateElementId ) ! ) ; assert . isDefined ( templateElement . federationGuid ) ; assert . isDefined ( instanceElement . federationGuid ) ; assert . notStrictEqual ( templateElement . federationGuid , instanceElement . federationGuid ) ; assert . equal ( templateElement . classFullName , instanceElement . classFullName ) ; assert . equal ( templateElement . parent ?. id ? true : false , instanceElement . parent ?. id ? true : false ) ; } } const drawingGraphicTemplateCode = TemplateRecipe2d . createCode ( iModelDb , testContainerId , "DrawingGraphic Template" ) ; const drawingGraphicTemplateId = iModelDb . elements . queryElementIdByCode ( drawingGraphicTemplateCode ) ! ; const drawingGraphicLocations = [ Point2d . create ( 10 , 10 ) , Point2d . create ( 20 , 10 ) , Point2d . create ( 30 , 10 ) , Point2d . create ( 10 , 20 ) , Point2d . create ( 20 , 20 ) , Point2d . create ( 30 , 20 ) , Point2d . create ( 10 , 30 ) , Point2d . create ( 20 , 30 ) , Point2d . create ( 30 , 30 ) , ] ; assert . equal ( countElementsInModel ( iModelDb , DrawingGraphic . classFullName , drawingId ) , 0 ) ; for ( const location of drawingGraphicLocations ) { const placement = new Placement2d ( location , Angle . zero ( ) , new Range2d ( ) ) ; await componentPlacer . placeTemplate2d ( drawingGraphicTemplateId , drawingId , placement ) ; } assert . equal ( countElementsInModel ( iModelDb , DrawingGraphic . classFullName , drawingId ) , drawingGraphicLocations . length ) ; componentPlacer . dispose ( ) ; iModelDb . saveChanges ( ) ; iModelDb . close ( ) ; } ) ; note it ( "should clone catalog" , async ( ) => { const sourceDb = SnapshotDb . openFile ( acmeCatalogDbFile ) ; const targetFile = IModelTestUtils . prepareOutputFile ( "Catalog" , "CloneOfAcmeEquipment.bim" ) ; const targetDb = SnapshotDb . createEmpty ( targetFile , { rootSubject : { name : "Facility" } , createClassViews } ) ; const target = new IModelImporter ( targetDb , { autoExtendProjectExtents : false } ) ; const cloner = new IModelTransformer ( sourceDb , target ) ; await cloner . processSchemas ( ) ; await cloner . processAll ( ) ; cloner . dispose ( ) ; const containerIds = queryContainerIds ( targetDb ) ; assert . equal ( containerIds . size , 1 ) ; containerIds . forEach ( ( containerId ) => { targetDb . elements . getElement < DefinitionContainer > ( containerId , DefinitionContainer ) ; targetDb . models . getModel < DefinitionModel > ( containerId , DefinitionModel ) ; assert . isTrue ( Id64 . isValidId64 ( queryEquipmentCategory ( targetDb , containerId ) ! ) ) ; assert . isTrue ( Id64 . isValidId64 ( queryEquipmentTypeId ( targetDb , containerId , "A-101" ) ! ) ) ; assert . isTrue ( Id64 . isValidId64 ( queryEquipmentTypeId ( targetDb , containerId , "A-201" ) ! ) ) ; assert . isTrue ( Id64 . isValidId64 ( queryEquipmentTypeId ( targetDb , containerId , "A-301" ) ! ) ) ; } ) ; sourceDb . close ( ) ; targetDb . saveChanges ( ) ; targetDb . close ( ) ; } ) ; } ) ;