@itwin/imodel-transformer 2.0.0-dev.2 → 2.0.0-dev.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -1
- package/lib/cjs/BranchProvenanceInitializer.js +18 -15
- package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.js +8 -6
- package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.d.ts +3 -0
- package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.js +103 -40
- package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
- package/lib/cjs/ElementCascadingDeleter.d.ts.map +1 -1
- package/lib/cjs/ElementCascadingDeleter.js +10 -9
- package/lib/cjs/ElementCascadingDeleter.js.map +1 -1
- package/lib/cjs/EntityUnifier.d.ts +1 -1
- package/lib/cjs/EntityUnifier.d.ts.map +1 -1
- package/lib/cjs/EntityUnifier.js +11 -12
- package/lib/cjs/EntityUnifier.js.map +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.d.ts +4 -4
- package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.js +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +1 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +17 -16
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +1 -1
- package/lib/cjs/IModelCloneContext.d.ts +3 -3
- package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
- package/lib/cjs/IModelCloneContext.js +60 -52
- package/lib/cjs/IModelCloneContext.js.map +1 -1
- package/lib/cjs/IModelExporter.d.ts +28 -19
- package/lib/cjs/IModelExporter.d.ts.map +1 -1
- package/lib/cjs/IModelExporter.js +145 -119
- package/lib/cjs/IModelExporter.js.map +1 -1
- package/lib/cjs/IModelImporter.d.ts +21 -21
- package/lib/cjs/IModelImporter.d.ts.map +1 -1
- package/lib/cjs/IModelImporter.js +94 -74
- package/lib/cjs/IModelImporter.js.map +1 -1
- package/lib/cjs/IModelTransformer.d.ts +84 -178
- package/lib/cjs/IModelTransformer.d.ts.map +1 -1
- package/lib/cjs/IModelTransformer.js +371 -997
- package/lib/cjs/IModelTransformer.js.map +1 -1
- package/lib/cjs/ProvenanceManager.d.ts +159 -0
- package/lib/cjs/ProvenanceManager.d.ts.map +1 -0
- package/lib/cjs/ProvenanceManager.js +677 -0
- package/lib/cjs/ProvenanceManager.js.map +1 -0
- package/lib/cjs/SyncTypeResolver.d.ts +34 -0
- package/lib/cjs/SyncTypeResolver.d.ts.map +1 -0
- package/lib/cjs/SyncTypeResolver.js +84 -0
- package/lib/cjs/SyncTypeResolver.js.map +1 -0
- package/lib/cjs/TransformerLoggerCategory.d.ts +6 -5
- package/lib/cjs/TransformerLoggerCategory.d.ts.map +1 -1
- package/lib/cjs/TransformerLoggerCategory.js +6 -5
- package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
- package/lib/cjs/imodel-transformer.js +2 -2
- package/lib/cjs/imodel-transformer.js.map +1 -1
- package/package.json +38 -33
|
@@ -26,7 +26,7 @@ class IModelExportHandler {
|
|
|
26
26
|
/** If `true` is returned, then the CodeSpec will be exported.
|
|
27
27
|
* @note This method can optionally be overridden to exclude an individual CodeSpec from the export. The base implementation always returns `true`.
|
|
28
28
|
*/
|
|
29
|
-
shouldExportCodeSpec(_codeSpec) {
|
|
29
|
+
async shouldExportCodeSpec(_codeSpec) {
|
|
30
30
|
return true;
|
|
31
31
|
}
|
|
32
32
|
/** Called when a CodeSpec should be exported.
|
|
@@ -34,37 +34,37 @@ class IModelExportHandler {
|
|
|
34
34
|
* @param isUpdate If defined, then `true` indicates an UPDATE operation while `false` indicates an INSERT operation. If not defined, then INSERT vs. UPDATE is not known.
|
|
35
35
|
* @note This should be overridden to actually do the export.
|
|
36
36
|
*/
|
|
37
|
-
onExportCodeSpec(_codeSpec, _isUpdate) { }
|
|
37
|
+
async onExportCodeSpec(_codeSpec, _isUpdate) { }
|
|
38
38
|
/** Called when a font should be exported.
|
|
39
39
|
* @param font The font to export
|
|
40
40
|
* @param isUpdate If defined, then `true` indicates an UPDATE operation while `false` indicates an INSERT operation. If not defined, then INSERT vs. UPDATE is not known.
|
|
41
41
|
* @note This should be overridden to actually do the export.
|
|
42
42
|
*/
|
|
43
|
-
onExportFont(_font, _isUpdate) { }
|
|
43
|
+
async onExportFont(_font, _isUpdate) { }
|
|
44
44
|
/** Called when a model should be exported.
|
|
45
45
|
* @param model The model to export
|
|
46
46
|
* @param isUpdate If defined, then `true` indicates an UPDATE operation while `false` indicates an INSERT operation. If not defined, then INSERT vs. UPDATE is not known.
|
|
47
47
|
* @note This should be overridden to actually do the export.
|
|
48
48
|
*/
|
|
49
|
-
onExportModel(_model, _isUpdate) { }
|
|
49
|
+
async onExportModel(_model, _isUpdate) { }
|
|
50
50
|
/** Called when a model should be deleted. */
|
|
51
|
-
onDeleteModel(_modelId) { }
|
|
51
|
+
async onDeleteModel(_modelId) { }
|
|
52
52
|
/** If `true` is returned, then the element will be exported.
|
|
53
53
|
* @note This method can optionally be overridden to exclude an individual Element (and its children and ElementAspects) from the export. The base implementation always returns `true`.
|
|
54
54
|
*/
|
|
55
|
-
shouldExportElement(_element) {
|
|
55
|
+
async shouldExportElement(_element) {
|
|
56
56
|
return true;
|
|
57
57
|
}
|
|
58
58
|
/** Called when element is skipped instead of exported.
|
|
59
59
|
* @note When an element is skipped, exporter will not export any of its child elements. Because of this, [[onSkipElement]] will not be invoked for any children of a "skipped" element.
|
|
60
60
|
*/
|
|
61
|
-
onSkipElement(_elementId) { }
|
|
61
|
+
async onSkipElement(_elementId) { }
|
|
62
62
|
/** Called when an element should be exported.
|
|
63
63
|
* @param element The element to export
|
|
64
64
|
* @param isUpdate If defined, then `true` indicates an UPDATE operation while `false` indicates an INSERT operation. If not defined, then INSERT vs. UPDATE is not known.
|
|
65
65
|
* @note This should be overridden to actually do the export.
|
|
66
66
|
*/
|
|
67
|
-
onExportElement(_element, _isUpdate) { }
|
|
67
|
+
async onExportElement(_element, _isUpdate) { }
|
|
68
68
|
/**
|
|
69
69
|
* Do any asynchronous actions before exporting an element
|
|
70
70
|
* @note Do not implement this handler manually, it is internal, it will be removed.
|
|
@@ -73,11 +73,11 @@ class IModelExportHandler {
|
|
|
73
73
|
*/
|
|
74
74
|
async preExportElement(_element) { }
|
|
75
75
|
/** Called when an element should be deleted. */
|
|
76
|
-
onDeleteElement(_elementId) { }
|
|
76
|
+
async onDeleteElement(_elementId) { }
|
|
77
77
|
/** If `true` is returned, then the ElementAspect will be exported.
|
|
78
78
|
* @note This method can optionally be overridden to exclude an individual ElementAspect from the export. The base implementation always returns `true`.
|
|
79
79
|
*/
|
|
80
|
-
shouldExportElementAspect(_aspect) {
|
|
80
|
+
async shouldExportElementAspect(_aspect) {
|
|
81
81
|
return true;
|
|
82
82
|
}
|
|
83
83
|
/** Called when an ElementUniqueAspect should be exported.
|
|
@@ -85,15 +85,15 @@ class IModelExportHandler {
|
|
|
85
85
|
* @param isUpdate If defined, then `true` indicates an UPDATE operation while `false` indicates an INSERT operation. If not defined, then INSERT vs. UPDATE is not known.
|
|
86
86
|
* @note This should be overridden to actually do the export.
|
|
87
87
|
*/
|
|
88
|
-
onExportElementUniqueAspect(_aspect, _isUpdate) { }
|
|
88
|
+
async onExportElementUniqueAspect(_aspect, _isUpdate) { }
|
|
89
89
|
/** Called when ElementMultiAspects should be exported.
|
|
90
90
|
* @note This should be overridden to actually do the export.
|
|
91
91
|
*/
|
|
92
|
-
onExportElementMultiAspects(_aspects) { }
|
|
92
|
+
async onExportElementMultiAspects(_aspects) { }
|
|
93
93
|
/** If `true` is returned, then the relationship will be exported.
|
|
94
94
|
* @note This method can optionally be overridden to exclude an individual CodeSpec from the export. The base implementation always returns `true`.
|
|
95
95
|
*/
|
|
96
|
-
shouldExportRelationship(_relationship) {
|
|
96
|
+
async shouldExportRelationship(_relationship) {
|
|
97
97
|
return true;
|
|
98
98
|
}
|
|
99
99
|
/** Called when a Relationship should be exported.
|
|
@@ -101,13 +101,13 @@ class IModelExportHandler {
|
|
|
101
101
|
* @param isUpdate If defined, then `true` indicates an UPDATE operation while `false` indicates an INSERT operation. If not defined, then INSERT vs. UPDATE is not known.
|
|
102
102
|
* @note This should be overridden to actually do the export.
|
|
103
103
|
*/
|
|
104
|
-
onExportRelationship(_relationship, _isUpdate) { }
|
|
104
|
+
async onExportRelationship(_relationship, _isUpdate) { }
|
|
105
105
|
/** Called when a relationship should be deleted. */
|
|
106
|
-
onDeleteRelationship(_relInstanceId) { }
|
|
106
|
+
async onDeleteRelationship(_relInstanceId) { }
|
|
107
107
|
/** If `true` is returned, then the schema will be exported.
|
|
108
108
|
* @note This method can optionally be overridden to exclude an individual schema from the export. The base implementation always returns `true`.
|
|
109
109
|
*/
|
|
110
|
-
shouldExportSchema(_schemaKey) {
|
|
110
|
+
async shouldExportSchema(_schemaKey) {
|
|
111
111
|
return true;
|
|
112
112
|
}
|
|
113
113
|
/** Called when a schema should be exported.
|
|
@@ -198,9 +198,9 @@ class IModelExporter {
|
|
|
198
198
|
constructor(sourceDb, elementAspectsStrategy = ExportElementAspectsWithElementsStrategy_1.ExportElementAspectsWithElementsStrategy) {
|
|
199
199
|
this.sourceDb = sourceDb;
|
|
200
200
|
this._exportElementAspectsStrategy = new elementAspectsStrategy(this.sourceDb, {
|
|
201
|
-
onExportElementMultiAspects: (aspects) => this.handler.onExportElementMultiAspects(aspects),
|
|
202
|
-
onExportElementUniqueAspect: (aspect, isUpdate) => this.handler.onExportElementUniqueAspect(aspect, isUpdate),
|
|
203
|
-
shouldExportElementAspect: (aspect) => this.handler.shouldExportElementAspect(aspect),
|
|
201
|
+
onExportElementMultiAspects: async (aspects) => this.handler.onExportElementMultiAspects(aspects),
|
|
202
|
+
onExportElementUniqueAspect: async (aspect, isUpdate) => this.handler.onExportElementUniqueAspect(aspect, isUpdate),
|
|
203
|
+
shouldExportElementAspect: async (aspect) => this.handler.shouldExportElementAspect(aspect),
|
|
204
204
|
trackProgress: async () => this.trackProgress(),
|
|
205
205
|
});
|
|
206
206
|
}
|
|
@@ -297,7 +297,7 @@ class IModelExporter {
|
|
|
297
297
|
if (this.visitElements) {
|
|
298
298
|
// must delete models first since they have a constraint on the submodeling element which may also be deleted
|
|
299
299
|
for (const modelId of this._sourceDbChanges.model.deleteIds) {
|
|
300
|
-
this.handler.onDeleteModel(modelId);
|
|
300
|
+
await this.handler.onDeleteModel(modelId);
|
|
301
301
|
}
|
|
302
302
|
for (const elementId of this._sourceDbChanges.element.deleteIds) {
|
|
303
303
|
// We don't know how the handler wants to handle deletions, and we don't have enough information
|
|
@@ -307,7 +307,7 @@ class IModelExporter {
|
|
|
307
307
|
// In the future, the handler may be responsible for doing the work of finding out which elements were cascade deleted,
|
|
308
308
|
// and returning them for the exporter to use to avoid double-deleting with error ignoring
|
|
309
309
|
try {
|
|
310
|
-
this.handler.onDeleteElement(elementId);
|
|
310
|
+
await this.handler.onDeleteElement(elementId);
|
|
311
311
|
}
|
|
312
312
|
catch (err) {
|
|
313
313
|
const isMissingErr = err instanceof core_common_1.IModelError &&
|
|
@@ -320,7 +320,7 @@ class IModelExporter {
|
|
|
320
320
|
if (this.visitRelationships) {
|
|
321
321
|
for (const relInstanceId of this._sourceDbChanges.relationship
|
|
322
322
|
.deleteIds) {
|
|
323
|
-
this.handler.onDeleteRelationship(relInstanceId);
|
|
323
|
+
await this.handler.onDeleteRelationship(relInstanceId);
|
|
324
324
|
}
|
|
325
325
|
}
|
|
326
326
|
// Enable consecutive exportChanges runs without the need to re-instantiate the exporter.
|
|
@@ -335,7 +335,6 @@ class IModelExporter {
|
|
|
335
335
|
* @note This must be called separately from [[exportAll]] or [[exportChanges]].
|
|
336
336
|
*/
|
|
337
337
|
async exportSchemas() {
|
|
338
|
-
/* eslint-disable @typescript-eslint/indent */
|
|
339
338
|
const sql = `
|
|
340
339
|
SELECT s.Name, s.VersionMajor, s.VersionWrite, s.VersionMinor
|
|
341
340
|
FROM ECDbMeta.ECSchemaDef s
|
|
@@ -346,27 +345,27 @@ class IModelExporter {
|
|
|
346
345
|
`}
|
|
347
346
|
ORDER BY ECInstanceId
|
|
348
347
|
`;
|
|
349
|
-
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
schemaNamesToExport.push(schemaName);
|
|
361
|
-
}
|
|
348
|
+
const schemaKeysToExport = [];
|
|
349
|
+
for await (const row of this.sourceDb.createQueryReader(sql, undefined, {
|
|
350
|
+
usePrimaryConn: true,
|
|
351
|
+
})) {
|
|
352
|
+
const schemaName = row[0];
|
|
353
|
+
const versionMajor = row[1];
|
|
354
|
+
const versionWrite = row[2];
|
|
355
|
+
const versionMinor = row[3];
|
|
356
|
+
const schemaKey = new ecschema_metadata_1.SchemaKey(schemaName, new ecschema_metadata_1.ECVersion(versionMajor, versionWrite, versionMinor));
|
|
357
|
+
if (await this.handler.shouldExportSchema(schemaKey)) {
|
|
358
|
+
schemaKeysToExport.push(schemaKey);
|
|
362
359
|
}
|
|
363
|
-
}
|
|
364
|
-
if (
|
|
360
|
+
}
|
|
361
|
+
if (schemaKeysToExport.length === 0)
|
|
365
362
|
return;
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
363
|
+
await Promise.all(schemaKeysToExport.map(async (schemaKey) => {
|
|
364
|
+
const schema = await this.sourceDb.schemaContext.getSchema(schemaKey);
|
|
365
|
+
if (!schema) {
|
|
366
|
+
throw new Error(`Failed to load schema: ${schemaKey.name}`);
|
|
367
|
+
}
|
|
368
|
+
core_bentley_1.Logger.logTrace(loggerCategory, `exportSchema(${schemaKey.name})`);
|
|
370
369
|
return this.handler.onExportSchema(schema);
|
|
371
370
|
}));
|
|
372
371
|
}
|
|
@@ -380,15 +379,11 @@ class IModelExporter {
|
|
|
380
379
|
async exportCodeSpecs() {
|
|
381
380
|
core_bentley_1.Logger.logTrace(loggerCategory, "exportCodeSpecs()");
|
|
382
381
|
const sql = "SELECT Name FROM BisCore:CodeSpec ORDER BY ECInstanceId";
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const codeSpecName = statement.getValue(0).getString();
|
|
389
|
-
await this.exportCodeSpecByName(codeSpecName);
|
|
390
|
-
}
|
|
391
|
-
});
|
|
382
|
+
for await (const row of this.sourceDb.createQueryReader(sql, undefined, {
|
|
383
|
+
usePrimaryConn: true,
|
|
384
|
+
})) {
|
|
385
|
+
await this.exportCodeSpecByName(row[0]);
|
|
386
|
+
}
|
|
392
387
|
}
|
|
393
388
|
/** Export a single CodeSpec from the source iModel.
|
|
394
389
|
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
@@ -414,9 +409,9 @@ class IModelExporter {
|
|
|
414
409
|
return;
|
|
415
410
|
}
|
|
416
411
|
// CodeSpec has passed standard exclusion rules, now give handler a chance to accept/reject export
|
|
417
|
-
if (this.handler.shouldExportCodeSpec(codeSpec)) {
|
|
412
|
+
if (await this.handler.shouldExportCodeSpec(codeSpec)) {
|
|
418
413
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportCodeSpec(${codeSpecName})${this.getChangeOpSuffix(isUpdate)}`);
|
|
419
|
-
this.handler.onExportCodeSpec(codeSpec, isUpdate);
|
|
414
|
+
await this.handler.onExportCodeSpec(codeSpec, isUpdate);
|
|
420
415
|
return this.trackProgress();
|
|
421
416
|
}
|
|
422
417
|
}
|
|
@@ -432,18 +427,39 @@ class IModelExporter {
|
|
|
432
427
|
*/
|
|
433
428
|
async exportFonts() {
|
|
434
429
|
core_bentley_1.Logger.logTrace(loggerCategory, "exportFonts()");
|
|
435
|
-
for (const font of this.sourceDb.
|
|
436
|
-
await this.
|
|
430
|
+
for (const font of this.sourceDb.fonts.queryMappedFamilies()) {
|
|
431
|
+
await this.exportFontByFontProps(font);
|
|
437
432
|
}
|
|
438
433
|
}
|
|
439
434
|
/** Export a single font from the source iModel.
|
|
440
|
-
* @note
|
|
435
|
+
* @note multiple fonts can have the same font name, if a font with a specific type is needed use exportFontByFontFamilyDescriptor.
|
|
436
|
+
* If not this will only export the first font with this type in the db.
|
|
441
437
|
*/
|
|
442
438
|
async exportFontByName(fontName) {
|
|
443
439
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportFontByName(${fontName})`);
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
440
|
+
const fontId = this.sourceDb.fonts.findId({
|
|
441
|
+
name: fontName,
|
|
442
|
+
});
|
|
443
|
+
if (undefined !== fontId) {
|
|
444
|
+
await this.exportFontByNumber(fontId);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
/** Export a single font from the source iModel.
|
|
448
|
+
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
449
|
+
*/
|
|
450
|
+
async exportFontByFontProps(fontProps) {
|
|
451
|
+
core_bentley_1.Logger.logTrace(loggerCategory, `exportFontByFamily(${fontProps.name}, ${fontProps.type})`);
|
|
452
|
+
await this.handler.onExportFont(fontProps, true);
|
|
453
|
+
return this.trackProgress();
|
|
454
|
+
}
|
|
455
|
+
/** Export a single font from the source iModel.
|
|
456
|
+
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
457
|
+
*/
|
|
458
|
+
async exportFontByFontFamilyDescriptor(fontFamily) {
|
|
459
|
+
core_bentley_1.Logger.logTrace(loggerCategory, `exportFontByFamilyDescriptor(${fontFamily.name}, ${fontFamily.type})`);
|
|
460
|
+
const fontId = await this.sourceDb.fonts.acquireId(fontFamily);
|
|
461
|
+
if (undefined !== fontId) {
|
|
462
|
+
await this.exportFontByFontProps({ ...fontFamily, id: fontId });
|
|
447
463
|
}
|
|
448
464
|
}
|
|
449
465
|
/** Export a single font from the source iModel.
|
|
@@ -455,12 +471,10 @@ class IModelExporter {
|
|
|
455
471
|
* It is very rare and even problematic for the font table to reach a large size, so it is not a bottleneck in transforming changes.
|
|
456
472
|
* See https://github.com/iTwin/imodel-transformer/pull/135 for removed code.
|
|
457
473
|
*/
|
|
458
|
-
const isUpdate = true;
|
|
459
474
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportFontById(${fontNumber})`);
|
|
460
|
-
const font = this.sourceDb.
|
|
475
|
+
const font = this.sourceDb.fonts.findDescriptor(fontNumber);
|
|
461
476
|
if (undefined !== font) {
|
|
462
|
-
this.
|
|
463
|
-
return this.trackProgress();
|
|
477
|
+
await this.exportFontByFontFamilyDescriptor(font);
|
|
464
478
|
}
|
|
465
479
|
}
|
|
466
480
|
/** Export the model container, contents, and sub-models from the source iModel.
|
|
@@ -477,7 +491,7 @@ class IModelExporter {
|
|
|
477
491
|
wantBRepData: this.wantGeometry,
|
|
478
492
|
});
|
|
479
493
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportModel(${modeledElementId})`);
|
|
480
|
-
if (this.shouldExportElement(modeledElement)) {
|
|
494
|
+
if (await this.shouldExportElement(modeledElement)) {
|
|
481
495
|
await this.exportModelContainer(model);
|
|
482
496
|
if (this.visitElements) {
|
|
483
497
|
await this.exportModelContents(modeledElementId);
|
|
@@ -500,7 +514,7 @@ class IModelExporter {
|
|
|
500
514
|
return; // not in changeset, don't export
|
|
501
515
|
}
|
|
502
516
|
}
|
|
503
|
-
this.handler.onExportModel(model, isUpdate);
|
|
517
|
+
await this.handler.onExportModel(model, isUpdate);
|
|
504
518
|
return this.trackProgress();
|
|
505
519
|
}
|
|
506
520
|
_yieldManager = new core_bentley_1.YieldManager();
|
|
@@ -535,19 +549,16 @@ class IModelExporter {
|
|
|
535
549
|
else {
|
|
536
550
|
sql = `SELECT ECInstanceId FROM ${elementClassFullName} WHERE Parent.Id IS NULL AND Model.Id=:modelId ORDER BY ECInstanceId`;
|
|
537
551
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
await this._yieldManager.allowYield();
|
|
549
|
-
}
|
|
550
|
-
});
|
|
552
|
+
const params = new core_common_1.QueryBinder().bindId("modelId", modelId);
|
|
553
|
+
if (skipRootSubject) {
|
|
554
|
+
params.bindId("rootSubjectId", core_common_1.IModel.rootSubjectId);
|
|
555
|
+
}
|
|
556
|
+
for await (const row of this.sourceDb.createQueryReader(sql, params, {
|
|
557
|
+
usePrimaryConn: true,
|
|
558
|
+
})) {
|
|
559
|
+
await this.exportElement(row.id);
|
|
560
|
+
await this._yieldManager.allowYield();
|
|
561
|
+
}
|
|
551
562
|
}
|
|
552
563
|
/** Export the sub-models directly below the specified model.
|
|
553
564
|
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
@@ -557,22 +568,19 @@ class IModelExporter {
|
|
|
557
568
|
const definitionModelIds = [];
|
|
558
569
|
const otherModelIds = [];
|
|
559
570
|
const sql = `SELECT ECInstanceId FROM ${core_backend_1.Model.classFullName} WHERE ParentModel.Id=:parentModelId ORDER BY ECInstanceId`;
|
|
560
|
-
|
|
561
|
-
this.sourceDb.
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
if (model instanceof core_backend_1.DefinitionModel) {
|
|
569
|
-
definitionModelIds.push(modelId);
|
|
570
|
-
}
|
|
571
|
-
else {
|
|
572
|
-
otherModelIds.push(modelId);
|
|
573
|
-
}
|
|
571
|
+
const params = new core_common_1.QueryBinder().bindId("parentModelId", parentModelId);
|
|
572
|
+
for await (const row of this.sourceDb.createQueryReader(sql, params, {
|
|
573
|
+
usePrimaryConn: true,
|
|
574
|
+
})) {
|
|
575
|
+
const modelId = row.id;
|
|
576
|
+
const model = this.sourceDb.models.getModel(modelId);
|
|
577
|
+
if (model instanceof core_backend_1.DefinitionModel) {
|
|
578
|
+
definitionModelIds.push(modelId);
|
|
574
579
|
}
|
|
575
|
-
|
|
580
|
+
else {
|
|
581
|
+
otherModelIds.push(modelId);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
576
584
|
// export DefinitionModels before other types of Models
|
|
577
585
|
for (const definitionModelId of definitionModelIds) {
|
|
578
586
|
await this.exportModel(definitionModelId);
|
|
@@ -585,7 +593,7 @@ class IModelExporter {
|
|
|
585
593
|
* This considers the standard IModelExporter exclusion rules plus calls [IModelExportHandler.shouldExportElement]($transformer) for any custom exclusion rules.
|
|
586
594
|
* @note This method is called from within [[exportChanges]] and [[exportAll]], so usually does not need to be called directly.
|
|
587
595
|
*/
|
|
588
|
-
shouldExportElement(element) {
|
|
596
|
+
async shouldExportElement(element) {
|
|
589
597
|
if (this._excludedElementIds.has(element.id)) {
|
|
590
598
|
core_bentley_1.Logger.logInfo(loggerCategory, `Excluded element ${element.id} by Id`);
|
|
591
599
|
return false;
|
|
@@ -621,7 +629,7 @@ class IModelExporter {
|
|
|
621
629
|
// Return early if the elementId is already in the excludedElementIds, that way we don't need to load the element from the db.
|
|
622
630
|
if (this._excludedElementIds.has(elementId)) {
|
|
623
631
|
core_bentley_1.Logger.logInfo(loggerCategory, `Excluded element ${elementId} by Id`);
|
|
624
|
-
this.handler.onSkipElement(elementId);
|
|
632
|
+
await this.handler.onSkipElement(elementId);
|
|
625
633
|
return;
|
|
626
634
|
}
|
|
627
635
|
// are we processing changes?
|
|
@@ -637,15 +645,15 @@ class IModelExporter {
|
|
|
637
645
|
});
|
|
638
646
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportElement(${element.id}, "${element.getDisplayLabel()}")${this.getChangeOpSuffix(isUpdate)}`);
|
|
639
647
|
// the order and `await`ing of calls beyond here is depended upon by the IModelTransformer for a current bug workaround
|
|
640
|
-
if (this.shouldExportElement(element)) {
|
|
648
|
+
if (await this.shouldExportElement(element)) {
|
|
641
649
|
await this.handler.preExportElement(element);
|
|
642
|
-
this.handler.onExportElement(element, isUpdate);
|
|
650
|
+
await this.handler.onExportElement(element, isUpdate);
|
|
643
651
|
await this.trackProgress();
|
|
644
652
|
await this._exportElementAspectsStrategy.exportElementAspectsForElement(elementId);
|
|
645
653
|
return this.exportChildElements(elementId);
|
|
646
654
|
}
|
|
647
655
|
else {
|
|
648
|
-
this.handler.onSkipElement(element.id);
|
|
656
|
+
await this.handler.onSkipElement(element.id);
|
|
649
657
|
}
|
|
650
658
|
}
|
|
651
659
|
/** Export the child elements of the specified element from the source iModel.
|
|
@@ -678,23 +686,18 @@ class IModelExporter {
|
|
|
678
686
|
return;
|
|
679
687
|
}
|
|
680
688
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportRelationships(${baseRelClassFullName})`);
|
|
681
|
-
const sql = `SELECT r.ECInstanceId, r.ECClassId FROM ${baseRelClassFullName} r
|
|
689
|
+
const sql = `SELECT r.ECInstanceId, ec_className(r.ECClassId, 's.c') as className FROM ${baseRelClassFullName} r
|
|
682
690
|
JOIN bis.Element s ON s.ECInstanceId = r.SourceECInstanceId
|
|
683
691
|
JOIN bis.Element t ON t.ECInstanceId = r.TargetECInstanceId
|
|
684
692
|
WHERE s.ECInstanceId IS NOT NULL AND t.ECInstanceId IS NOT NULL`;
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
.getClassNameForClassId();
|
|
694
|
-
await this.exportRelationship(relationshipClass, relationshipId); // must call exportRelationship using the actual classFullName, not baseRelClassFullName
|
|
695
|
-
await this._yieldManager.allowYield();
|
|
696
|
-
}
|
|
697
|
-
});
|
|
693
|
+
for await (const row of this.sourceDb.createQueryReader(sql, undefined, {
|
|
694
|
+
usePrimaryConn: true,
|
|
695
|
+
})) {
|
|
696
|
+
const relationshipId = row.id;
|
|
697
|
+
const relationshipClass = row.className;
|
|
698
|
+
await this.exportRelationship(relationshipClass, relationshipId); // must call exportRelationship using the actual classFullName, not baseRelClassFullName
|
|
699
|
+
await this._yieldManager.allowYield();
|
|
700
|
+
}
|
|
698
701
|
}
|
|
699
702
|
/** Export a relationship from the source iModel. */
|
|
700
703
|
async exportRelationship(relClassFullName, relInstanceId) {
|
|
@@ -725,8 +728,8 @@ class IModelExporter {
|
|
|
725
728
|
}
|
|
726
729
|
}
|
|
727
730
|
// relationship has passed standard exclusion rules, now give handler a chance to accept/reject export
|
|
728
|
-
if (this.handler.shouldExportRelationship(relationship)) {
|
|
729
|
-
this.handler.onExportRelationship(relationship, isUpdate);
|
|
731
|
+
if (await this.handler.shouldExportRelationship(relationship)) {
|
|
732
|
+
await this.handler.onExportRelationship(relationship, isUpdate);
|
|
730
733
|
await this.trackProgress();
|
|
731
734
|
}
|
|
732
735
|
}
|
|
@@ -797,7 +800,7 @@ class ChangedInstanceIds {
|
|
|
797
800
|
this._relationshipSubclassIds = new Set();
|
|
798
801
|
this._relationshipSubclassIdsToSkip = new Set();
|
|
799
802
|
const addECClassIdsToSet = async (setToModify, baseClass) => {
|
|
800
|
-
for await (const row of this._db.createQueryReader(`SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (${baseClass})
|
|
803
|
+
for await (const row of this._db.createQueryReader(`SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (${baseClass})`, undefined, { usePrimaryConn: true })) {
|
|
801
804
|
setToModify.add(row.ECInstanceId);
|
|
802
805
|
}
|
|
803
806
|
};
|
|
@@ -852,6 +855,7 @@ class ChangedInstanceIds {
|
|
|
852
855
|
* from the set of updatedIds and add it to the set of deletedIds for the appropriate class type.
|
|
853
856
|
* @param change ChangedECInstance which has the ECInstanceId, changeType (insert, update, delete) and ECClassId of the changed entity
|
|
854
857
|
*/
|
|
858
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
855
859
|
async addChange(change) {
|
|
856
860
|
if (!this._ecClassIdsInitialized)
|
|
857
861
|
await this.setupECClassIds();
|
|
@@ -960,7 +964,9 @@ class ChangedInstanceIds {
|
|
|
960
964
|
SELECT parentId FROM hierarchy where parentId is not null
|
|
961
965
|
`;
|
|
962
966
|
const parentModelIds = new Set();
|
|
963
|
-
for await (const row of this._db.createQueryReader(ecQuery, params
|
|
967
|
+
for await (const row of this._db.createQueryReader(ecQuery, params, {
|
|
968
|
+
usePrimaryConn: true,
|
|
969
|
+
})) {
|
|
964
970
|
// Transformer handles update as insert when element does not exist in target.
|
|
965
971
|
// Which means that in scenario where child and parent model are filtered out from target,
|
|
966
972
|
// and child element is inserted trough custom change, its parent model will be marked as updated.
|
|
@@ -978,7 +984,9 @@ class ChangedInstanceIds {
|
|
|
978
984
|
WHERE InVirtualSet(:elementIds, TargetECInstanceId)
|
|
979
985
|
OR InVirtualSet(:elementIds, SourceECInstanceId)`;
|
|
980
986
|
const queryBinder = new core_common_1.QueryBinder().bindIdSet("elementIds", elementIds);
|
|
981
|
-
const queryReader = this._db.createQueryReader(ecQuery, queryBinder
|
|
987
|
+
const queryReader = this._db.createQueryReader(ecQuery, queryBinder, {
|
|
988
|
+
usePrimaryConn: true,
|
|
989
|
+
});
|
|
982
990
|
for await (const row of queryReader) {
|
|
983
991
|
this.handleChange(this.relationship, "Inserted", row.ECInstanceId);
|
|
984
992
|
}
|
|
@@ -990,7 +998,9 @@ class ChangedInstanceIds {
|
|
|
990
998
|
]) {
|
|
991
999
|
const ecQuery = `Select ECInstanceId from ${aspectClassName} where InVirtualSet(:elementIds, Element.Id)`;
|
|
992
1000
|
const queryBinder = new core_common_1.QueryBinder().bindIdSet("elementIds", elementIds);
|
|
993
|
-
const queryReader = this._db.createQueryReader(ecQuery, queryBinder
|
|
1001
|
+
const queryReader = this._db.createQueryReader(ecQuery, queryBinder, {
|
|
1002
|
+
usePrimaryConn: true,
|
|
1003
|
+
});
|
|
994
1004
|
for await (const row of queryReader) {
|
|
995
1005
|
this.addCustomAspectChange("Inserted", row.toArray()[0]);
|
|
996
1006
|
}
|
|
@@ -1065,13 +1075,29 @@ class ChangedInstanceIds {
|
|
|
1065
1075
|
db: opts.iModel,
|
|
1066
1076
|
disableSchemaCheck: true,
|
|
1067
1077
|
});
|
|
1078
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1068
1079
|
const csAdaptor = new core_backend_1.ChangesetECAdaptor(csReader);
|
|
1069
|
-
|
|
1080
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1081
|
+
const ecChangeUnifier = new core_backend_1.PartialECChangeUnifier(opts.iModel);
|
|
1070
1082
|
while (csAdaptor.step()) {
|
|
1071
1083
|
ecChangeUnifier.appendFrom(csAdaptor);
|
|
1072
1084
|
}
|
|
1085
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1073
1086
|
const changes = [...ecChangeUnifier.instances];
|
|
1074
1087
|
for (const change of changes) {
|
|
1088
|
+
// Change is recorded at table level, not EC entity level.
|
|
1089
|
+
// This `change.$meta.op` operation overwrite is needed to properly handle scenario when:
|
|
1090
|
+
// 1. Source has an EC class with less than 32 properties. There are existing elements for that class.
|
|
1091
|
+
// 2. Class is then updated to have more than 32 properties. Which means overflow table is now needed to store its elements.
|
|
1092
|
+
// During schema update all elements that belong to updated class, will be expanded into overflow table.
|
|
1093
|
+
// 3. Changeset will have a record about `insert` operation into overflow table for already existing elements.
|
|
1094
|
+
// This fix will overwrite such 'insert' and 'delete' operations to 'update' as no changes are done to main table.
|
|
1095
|
+
// It ensures that changes will be processed and squashed correctly.
|
|
1096
|
+
if (change.$meta &&
|
|
1097
|
+
(change.$meta.op === "Inserted" || change.$meta.op === "Deleted") &&
|
|
1098
|
+
change.$meta.tables.every((e) => e.endsWith("Overflow"))) {
|
|
1099
|
+
change.$meta.op = "Updated";
|
|
1100
|
+
}
|
|
1075
1101
|
await changedInstanceIds.addChange(change);
|
|
1076
1102
|
}
|
|
1077
1103
|
csReader.close();
|