@itwin/imodel-transformer 0.4.18-fedguidopt.6 → 1.0.0-dev.2
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/lib/cjs/Algo.d.ts.map +1 -1
- package/lib/cjs/Algo.js +3 -4
- package/lib/cjs/Algo.js.map +1 -1
- package/lib/cjs/BigMap.d.ts.map +1 -1
- package/lib/cjs/BigMap.js +1 -1
- package/lib/cjs/BigMap.js.map +1 -1
- package/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
- package/lib/cjs/BranchProvenanceInitializer.js +15 -4
- package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.js +12 -5
- package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.js +32 -18
- package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
- package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.d.ts +1 -1
- package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.d.ts.map +1 -1
- package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.js +7 -5
- package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.js.map +1 -1
- package/lib/cjs/ElementCascadingDeleter.d.ts +3 -3
- package/lib/cjs/ElementCascadingDeleter.d.ts.map +1 -1
- package/lib/cjs/ElementCascadingDeleter.js +9 -7
- package/lib/cjs/ElementCascadingDeleter.js.map +1 -1
- package/lib/cjs/EntityMap.d.ts.map +1 -1
- package/lib/cjs/EntityMap.js.map +1 -1
- package/lib/cjs/EntityUnifier.d.ts +5 -0
- package/lib/cjs/EntityUnifier.d.ts.map +1 -1
- package/lib/cjs/EntityUnifier.js +22 -35
- package/lib/cjs/EntityUnifier.js.map +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.js +5 -4
- package/lib/cjs/ExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +1 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +9 -5
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +1 -1
- package/lib/cjs/IModelCloneContext.d.ts +1 -4
- package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
- package/lib/cjs/IModelCloneContext.js +21 -40
- package/lib/cjs/IModelCloneContext.js.map +1 -1
- package/lib/cjs/IModelExporter.d.ts +69 -73
- package/lib/cjs/IModelExporter.d.ts.map +1 -1
- package/lib/cjs/IModelExporter.js +240 -176
- package/lib/cjs/IModelExporter.js.map +1 -1
- package/lib/cjs/IModelImporter.d.ts +17 -65
- package/lib/cjs/IModelImporter.d.ts.map +1 -1
- package/lib/cjs/IModelImporter.js +79 -109
- package/lib/cjs/IModelImporter.js.map +1 -1
- package/lib/cjs/IModelTransformer.d.ts +143 -99
- package/lib/cjs/IModelTransformer.d.ts.map +1 -1
- package/lib/cjs/IModelTransformer.js +717 -731
- package/lib/cjs/IModelTransformer.js.map +1 -1
- package/lib/cjs/PendingReferenceMap.d.ts.map +1 -1
- package/lib/cjs/PendingReferenceMap.js +12 -6
- package/lib/cjs/PendingReferenceMap.js.map +1 -1
- package/lib/cjs/TransformerLoggerCategory.d.ts +2 -2
- package/lib/cjs/TransformerLoggerCategory.d.ts.map +1 -1
- package/lib/cjs/TransformerLoggerCategory.js +5 -5
- package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
- package/lib/cjs/transformer.d.ts.map +1 -1
- package/lib/cjs/transformer.js +14 -10
- package/lib/cjs/transformer.js.map +1 -1
- package/package.json +20 -17
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*---------------------------------------------------------------------------------------------
|
|
3
|
-
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
-
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
-
*--------------------------------------------------------------------------------------------*/
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
6
|
/** @packageDocumentation
|
|
7
7
|
* @module iModels
|
|
8
8
|
*/
|
|
@@ -26,7 +26,9 @@ 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
|
+
shouldExportCodeSpec(_codeSpec) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
30
32
|
/** Called when a CodeSpec should be exported.
|
|
31
33
|
* @param codeSpec The CodeSpec to export
|
|
32
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.
|
|
@@ -50,7 +52,9 @@ class IModelExportHandler {
|
|
|
50
52
|
/** If `true` is returned, then the element will be exported.
|
|
51
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`.
|
|
52
54
|
*/
|
|
53
|
-
shouldExportElement(_element) {
|
|
55
|
+
shouldExportElement(_element) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
54
58
|
/** Called when element is skipped instead of exported. */
|
|
55
59
|
onSkipElement(_elementId) { }
|
|
56
60
|
/** Called when an element should be exported.
|
|
@@ -71,7 +75,9 @@ class IModelExportHandler {
|
|
|
71
75
|
/** If `true` is returned, then the ElementAspect will be exported.
|
|
72
76
|
* @note This method can optionally be overridden to exclude an individual ElementAspect from the export. The base implementation always returns `true`.
|
|
73
77
|
*/
|
|
74
|
-
shouldExportElementAspect(_aspect) {
|
|
78
|
+
shouldExportElementAspect(_aspect) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
75
81
|
/** Called when an ElementUniqueAspect should be exported.
|
|
76
82
|
* @param aspect The ElementUniqueAspect to export
|
|
77
83
|
* @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.
|
|
@@ -85,7 +91,9 @@ class IModelExportHandler {
|
|
|
85
91
|
/** If `true` is returned, then the relationship will be exported.
|
|
86
92
|
* @note This method can optionally be overridden to exclude an individual CodeSpec from the export. The base implementation always returns `true`.
|
|
87
93
|
*/
|
|
88
|
-
shouldExportRelationship(_relationship) {
|
|
94
|
+
shouldExportRelationship(_relationship) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
89
97
|
/** Called when a Relationship should be exported.
|
|
90
98
|
* @param relationship The Relationship to export
|
|
91
99
|
* @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.
|
|
@@ -97,7 +105,9 @@ class IModelExportHandler {
|
|
|
97
105
|
/** If `true` is returned, then the schema will be exported.
|
|
98
106
|
* @note This method can optionally be overridden to exclude an individual schema from the export. The base implementation always returns `true`.
|
|
99
107
|
*/
|
|
100
|
-
shouldExportSchema(_schemaKey) {
|
|
108
|
+
shouldExportSchema(_schemaKey) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
101
111
|
/** Called when a schema should be exported.
|
|
102
112
|
* @param schema The schema to export
|
|
103
113
|
* @note This should be overridden to actually do the export.
|
|
@@ -121,7 +131,7 @@ class IModelExporter {
|
|
|
121
131
|
/**
|
|
122
132
|
* Retrieve the cached entity change information.
|
|
123
133
|
* @note This will only be initialized after [IModelExporter.exportChanges] is invoked.
|
|
124
|
-
|
|
134
|
+
*/
|
|
125
135
|
get sourceDbChanges() {
|
|
126
136
|
return this._sourceDbChanges;
|
|
127
137
|
}
|
|
@@ -194,11 +204,14 @@ class IModelExporter {
|
|
|
194
204
|
* you pass to [[IModelExporter.exportChanges]]
|
|
195
205
|
*/
|
|
196
206
|
async initialize(options) {
|
|
197
|
-
|
|
198
|
-
|
|
207
|
+
if (!this.sourceDb.isBriefcaseDb() || this._sourceDbChanges)
|
|
208
|
+
return;
|
|
209
|
+
this._sourceDbChanges = await ChangedInstanceIds.initialize({
|
|
210
|
+
iModel: this.sourceDb,
|
|
211
|
+
...options,
|
|
212
|
+
});
|
|
213
|
+
if (this._sourceDbChanges === undefined)
|
|
199
214
|
return;
|
|
200
|
-
this._sourceDbChanges = options.changedInstanceIds
|
|
201
|
-
?? await ChangedInstanceIds.initialize({ iModel: this.sourceDb, ...options });
|
|
202
215
|
this._exportElementAspectsStrategy.setAspectChanges(this._sourceDbChanges.aspect);
|
|
203
216
|
}
|
|
204
217
|
/** Register the handler that will be called by IModelExporter. */
|
|
@@ -257,8 +270,13 @@ class IModelExporter {
|
|
|
257
270
|
nodeAssert(this._sourceDbChanges !== undefined, "sourceDbChanges must be initialized.");
|
|
258
271
|
await this.exportCodeSpecs();
|
|
259
272
|
await this.exportFonts();
|
|
260
|
-
|
|
261
|
-
|
|
273
|
+
if (initOpts.skipPropagateChangesToRootElements) {
|
|
274
|
+
await this.exportModelContents(core_common_1.IModel.repositoryModelId);
|
|
275
|
+
await this.exportSubModels(core_common_1.IModel.repositoryModelId);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
await this.exportModel(core_common_1.IModel.repositoryModelId);
|
|
279
|
+
}
|
|
262
280
|
await this.exportAllAspects();
|
|
263
281
|
await this.exportRelationships(core_backend_1.ElementRefersToElements.classFullName);
|
|
264
282
|
// handle deletes
|
|
@@ -278,14 +296,16 @@ class IModelExporter {
|
|
|
278
296
|
this.handler.onDeleteElement(elementId);
|
|
279
297
|
}
|
|
280
298
|
catch (err) {
|
|
281
|
-
const isMissingErr = err instanceof core_common_1.IModelError &&
|
|
299
|
+
const isMissingErr = err instanceof core_common_1.IModelError &&
|
|
300
|
+
err.errorNumber === core_bentley_1.IModelStatus.NotFound;
|
|
282
301
|
if (!isMissingErr)
|
|
283
302
|
throw err;
|
|
284
303
|
}
|
|
285
304
|
}
|
|
286
305
|
}
|
|
287
306
|
if (this.visitRelationships) {
|
|
288
|
-
for (const relInstanceId of this._sourceDbChanges.relationship
|
|
307
|
+
for (const relInstanceId of this._sourceDbChanges.relationship
|
|
308
|
+
.deleteIds) {
|
|
289
309
|
this.handler.onDeleteRelationship(relInstanceId);
|
|
290
310
|
}
|
|
291
311
|
}
|
|
@@ -304,7 +324,9 @@ class IModelExporter {
|
|
|
304
324
|
const sql = `
|
|
305
325
|
SELECT s.Name, s.VersionMajor, s.VersionWrite, s.VersionMinor
|
|
306
326
|
FROM ECDbMeta.ECSchemaDef s
|
|
307
|
-
${this.wantSystemSchemas
|
|
327
|
+
${this.wantSystemSchemas
|
|
328
|
+
? ""
|
|
329
|
+
: `
|
|
308
330
|
WHERE ECInstanceId >= (SELECT ECInstanceId FROM ECDbMeta.ECSchemaDef WHERE Name='BisCore')
|
|
309
331
|
`}
|
|
310
332
|
ORDER BY ECInstanceId
|
|
@@ -340,8 +362,8 @@ class IModelExporter {
|
|
|
340
362
|
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
341
363
|
*/
|
|
342
364
|
async exportCodeSpecs() {
|
|
343
|
-
core_bentley_1.Logger.logTrace(loggerCategory,
|
|
344
|
-
const sql =
|
|
365
|
+
core_bentley_1.Logger.logTrace(loggerCategory, "exportCodeSpecs()");
|
|
366
|
+
const sql = "SELECT Name FROM BisCore:CodeSpec ORDER BY ECInstanceId";
|
|
345
367
|
await this.sourceDb.withPreparedStatement(sql, async (statement) => {
|
|
346
368
|
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
347
369
|
const codeSpecName = statement.getValue(0).getString();
|
|
@@ -355,7 +377,8 @@ class IModelExporter {
|
|
|
355
377
|
async exportCodeSpecByName(codeSpecName) {
|
|
356
378
|
const codeSpec = this.sourceDb.codeSpecs.getByName(codeSpecName);
|
|
357
379
|
let isUpdate;
|
|
358
|
-
if (undefined !== this._sourceDbChanges) {
|
|
380
|
+
if (undefined !== this._sourceDbChanges) {
|
|
381
|
+
// is changeset information available?
|
|
359
382
|
if (this._sourceDbChanges.codeSpec.insertIds.has(codeSpec.id)) {
|
|
360
383
|
isUpdate = false;
|
|
361
384
|
}
|
|
@@ -389,7 +412,7 @@ class IModelExporter {
|
|
|
389
412
|
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
390
413
|
*/
|
|
391
414
|
async exportFonts() {
|
|
392
|
-
core_bentley_1.Logger.logTrace(loggerCategory,
|
|
415
|
+
core_bentley_1.Logger.logTrace(loggerCategory, "exportFonts()");
|
|
393
416
|
for (const font of this.sourceDb.fontMap.fonts.values()) {
|
|
394
417
|
await this.exportFontByNumber(font.id);
|
|
395
418
|
}
|
|
@@ -408,19 +431,12 @@ class IModelExporter {
|
|
|
408
431
|
* @note This method is called from [[exportChanges]] and [[exportAll]], so it only needs to be called directly when exporting a subset of an iModel.
|
|
409
432
|
*/
|
|
410
433
|
async exportFontByNumber(fontNumber) {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
else if (this._sourceDbChanges.font.updateIds.has(fontId)) {
|
|
418
|
-
isUpdate = true;
|
|
419
|
-
}
|
|
420
|
-
else {
|
|
421
|
-
return; // not in changeset, don't export
|
|
422
|
-
}
|
|
423
|
-
}
|
|
434
|
+
/** sourceDbChanges now works by using TS ChangesetECAdaptor which doesn't pick up changes to fonts since fonts is not an ec table.
|
|
435
|
+
* So lets always export fonts for the time being by always setting isUpdate = true.
|
|
436
|
+
* 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.
|
|
437
|
+
* See https://github.com/iTwin/imodel-transformer/pull/135 for removed code.
|
|
438
|
+
*/
|
|
439
|
+
const isUpdate = true;
|
|
424
440
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportFontById(${fontNumber})`);
|
|
425
441
|
const font = this.sourceDb.fontMap.getFont(fontNumber);
|
|
426
442
|
if (undefined !== font) {
|
|
@@ -436,7 +452,11 @@ class IModelExporter {
|
|
|
436
452
|
if (model.isTemplate && !this.wantTemplateModels) {
|
|
437
453
|
return;
|
|
438
454
|
}
|
|
439
|
-
const modeledElement = this.sourceDb.elements.getElement({
|
|
455
|
+
const modeledElement = this.sourceDb.elements.getElement({
|
|
456
|
+
id: modeledElementId,
|
|
457
|
+
wantGeometry: this.wantGeometry,
|
|
458
|
+
wantBRepData: this.wantGeometry,
|
|
459
|
+
});
|
|
440
460
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportModel(${modeledElementId})`);
|
|
441
461
|
if (this.shouldExportElement(modeledElement)) {
|
|
442
462
|
await this.exportModelContainer(model);
|
|
@@ -449,7 +469,8 @@ class IModelExporter {
|
|
|
449
469
|
/** Export the model (the container only) from the source iModel. */
|
|
450
470
|
async exportModelContainer(model) {
|
|
451
471
|
let isUpdate;
|
|
452
|
-
if (undefined !== this._sourceDbChanges) {
|
|
472
|
+
if (undefined !== this._sourceDbChanges) {
|
|
473
|
+
// is changeset information available?
|
|
453
474
|
if (this._sourceDbChanges.model.insertIds.has(model.id)) {
|
|
454
475
|
isUpdate = false;
|
|
455
476
|
}
|
|
@@ -479,8 +500,10 @@ class IModelExporter {
|
|
|
479
500
|
core_bentley_1.Logger.logTrace(loggerCategory, `visitElements=false, skipping exportModelContents(${modelId})`);
|
|
480
501
|
return;
|
|
481
502
|
}
|
|
482
|
-
if (undefined !== this._sourceDbChanges) {
|
|
483
|
-
|
|
503
|
+
if (undefined !== this._sourceDbChanges) {
|
|
504
|
+
// is changeset information available?
|
|
505
|
+
if (!this._sourceDbChanges.model.insertIds.has(modelId) &&
|
|
506
|
+
!this._sourceDbChanges.model.updateIds.has(modelId)) {
|
|
484
507
|
return; // this optimization assumes that the Model changes (LastMod) any time an Element in the Model changes
|
|
485
508
|
}
|
|
486
509
|
}
|
|
@@ -547,7 +570,8 @@ class IModelExporter {
|
|
|
547
570
|
return false;
|
|
548
571
|
}
|
|
549
572
|
}
|
|
550
|
-
if (!this.wantTemplateModels &&
|
|
573
|
+
if (!this.wantTemplateModels &&
|
|
574
|
+
element instanceof core_backend_1.RecipeDefinitionElement) {
|
|
551
575
|
core_bentley_1.Logger.logInfo(loggerCategory, `Excluded RecipeDefinitionElement ${element.id} because wantTemplate=false`);
|
|
552
576
|
return false;
|
|
553
577
|
}
|
|
@@ -568,11 +592,23 @@ class IModelExporter {
|
|
|
568
592
|
core_bentley_1.Logger.logTrace(loggerCategory, `visitElements=false, skipping exportElement(${elementId})`);
|
|
569
593
|
return;
|
|
570
594
|
}
|
|
595
|
+
// Return early if the elementId is already in the excludedElementIds, that way we don't need to load the element from the db.
|
|
596
|
+
if (this._excludedElementIds.has(elementId)) {
|
|
597
|
+
core_bentley_1.Logger.logInfo(loggerCategory, `Excluded element ${elementId} by Id`);
|
|
598
|
+
this.handler.onSkipElement(elementId);
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
571
601
|
// are we processing changes?
|
|
572
|
-
const isUpdate = this._sourceDbChanges?.element.insertIds.has(elementId)
|
|
573
|
-
|
|
602
|
+
const isUpdate = this._sourceDbChanges?.element.insertIds.has(elementId)
|
|
603
|
+
? false
|
|
604
|
+
: this._sourceDbChanges?.element.updateIds.has(elementId)
|
|
605
|
+
? true
|
|
574
606
|
: undefined;
|
|
575
|
-
const element = this.sourceDb.elements.getElement({
|
|
607
|
+
const element = this.sourceDb.elements.getElement({
|
|
608
|
+
id: elementId,
|
|
609
|
+
wantGeometry: this.wantGeometry,
|
|
610
|
+
wantBRepData: this.wantGeometry,
|
|
611
|
+
});
|
|
576
612
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportElement(${element.id}, "${element.getDisplayLabel()}")${this.getChangeOpSuffix(isUpdate)}`);
|
|
577
613
|
// the order and `await`ing of calls beyond here is depended upon by the IModelTransformer for a current bug workaround
|
|
578
614
|
if (this.shouldExportElement(element)) {
|
|
@@ -612,7 +648,7 @@ class IModelExporter {
|
|
|
612
648
|
*/
|
|
613
649
|
async exportRelationships(baseRelClassFullName) {
|
|
614
650
|
if (!this.visitRelationships) {
|
|
615
|
-
core_bentley_1.Logger.logTrace(loggerCategory,
|
|
651
|
+
core_bentley_1.Logger.logTrace(loggerCategory, "visitRelationships=false, skipping exportRelationships()");
|
|
616
652
|
return;
|
|
617
653
|
}
|
|
618
654
|
core_bentley_1.Logger.logTrace(loggerCategory, `exportRelationships(${baseRelClassFullName})`);
|
|
@@ -623,7 +659,9 @@ class IModelExporter {
|
|
|
623
659
|
await this.sourceDb.withPreparedStatement(sql, async (statement) => {
|
|
624
660
|
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
625
661
|
const relationshipId = statement.getValue(0).getId();
|
|
626
|
-
const relationshipClass = statement
|
|
662
|
+
const relationshipClass = statement
|
|
663
|
+
.getValue(1)
|
|
664
|
+
.getClassNameForClassId();
|
|
627
665
|
await this.exportRelationship(relationshipClass, relationshipId); // must call exportRelationship using the actual classFullName, not baseRelClassFullName
|
|
628
666
|
await this._yieldManager.allowYield();
|
|
629
667
|
}
|
|
@@ -636,7 +674,8 @@ class IModelExporter {
|
|
|
636
674
|
return;
|
|
637
675
|
}
|
|
638
676
|
let isUpdate;
|
|
639
|
-
if (undefined !== this._sourceDbChanges) {
|
|
677
|
+
if (undefined !== this._sourceDbChanges) {
|
|
678
|
+
// is changeset information available?
|
|
640
679
|
if (this._sourceDbChanges.relationship.insertIds.has(relInstanceId)) {
|
|
641
680
|
isUpdate = false;
|
|
642
681
|
}
|
|
@@ -665,95 +704,15 @@ class IModelExporter {
|
|
|
665
704
|
/** Tracks incremental progress */
|
|
666
705
|
async trackProgress() {
|
|
667
706
|
this._progressCounter++;
|
|
668
|
-
if (0 ===
|
|
707
|
+
if (0 === this._progressCounter % this.progressInterval) {
|
|
669
708
|
return this.handler.onProgress();
|
|
670
709
|
}
|
|
671
710
|
}
|
|
672
|
-
/**
|
|
673
|
-
* You may override this to store arbitrary json state in a exporter state dump, useful for some resumptions
|
|
674
|
-
* @see [[IModelTransformer.saveStateToFile]]
|
|
675
|
-
*/
|
|
676
|
-
getAdditionalStateJson() {
|
|
677
|
-
return {};
|
|
678
|
-
}
|
|
679
|
-
/**
|
|
680
|
-
* You may override this to load arbitrary json state in a transformer state dump, useful for some resumptions
|
|
681
|
-
* @see [[IModelTransformer.loadStateFromFile]]
|
|
682
|
-
*/
|
|
683
|
-
loadAdditionalStateJson(_additionalState) { }
|
|
684
|
-
/**
|
|
685
|
-
* Reload our state from a JSON object
|
|
686
|
-
* Intended for [[IModelTransformer.resumeTransformation]]
|
|
687
|
-
* @internal
|
|
688
|
-
* You can load custom json from the exporter save state for custom exporters by overriding [[IModelExporter.loadAdditionalStateJson]]
|
|
689
|
-
*/
|
|
690
|
-
loadStateFromJson(state) {
|
|
691
|
-
if (state.exporterClass !== this.constructor.name)
|
|
692
|
-
throw Error("resuming from a differently named exporter class, it is not necessarily valid to resume with a different exporter class");
|
|
693
|
-
this.wantGeometry = state.wantGeometry;
|
|
694
|
-
this.wantTemplateModels = state.wantTemplateModels;
|
|
695
|
-
this.wantSystemSchemas = state.wantSystemSchemas;
|
|
696
|
-
this.visitElements = state.visitElements;
|
|
697
|
-
this.visitRelationships = state.visitRelationships;
|
|
698
|
-
this._excludedCodeSpecNames = new Set(state.excludedCodeSpecNames);
|
|
699
|
-
this._excludedElementIds = core_bentley_1.CompressedId64Set.decompressSet(state.excludedElementIds),
|
|
700
|
-
this._excludedElementCategoryIds = core_bentley_1.CompressedId64Set.decompressSet(state.excludedElementCategoryIds),
|
|
701
|
-
this._excludedElementClasses = new Set(state.excludedElementClassNames.map((c) => this.sourceDb.getJsClass(c)));
|
|
702
|
-
this._exportElementAspectsStrategy.loadExcludedElementAspectClasses(state.excludedElementAspectClassFullNames);
|
|
703
|
-
this._excludedRelationshipClasses = new Set(state.excludedRelationshipClassNames.map((c) => this.sourceDb.getJsClass(c)));
|
|
704
|
-
this.loadAdditionalStateJson(state.additionalState);
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* Serialize state to a JSON object
|
|
708
|
-
* Intended for [[IModelTransformer.resumeTransformation]]
|
|
709
|
-
* @internal
|
|
710
|
-
* You can add custom json to the exporter save state for custom exporters by overriding [[IModelExporter.getAdditionalStateJson]]
|
|
711
|
-
*/
|
|
712
|
-
saveStateToJson() {
|
|
713
|
-
return {
|
|
714
|
-
exporterClass: this.constructor.name,
|
|
715
|
-
wantGeometry: this.wantGeometry,
|
|
716
|
-
wantTemplateModels: this.wantTemplateModels,
|
|
717
|
-
wantSystemSchemas: this.wantSystemSchemas,
|
|
718
|
-
visitElements: this.visitElements,
|
|
719
|
-
visitRelationships: this.visitRelationships,
|
|
720
|
-
excludedCodeSpecNames: [...this._excludedCodeSpecNames],
|
|
721
|
-
excludedElementIds: core_bentley_1.CompressedId64Set.compressSet(this._excludedElementIds),
|
|
722
|
-
excludedElementCategoryIds: core_bentley_1.CompressedId64Set.compressSet(this._excludedElementCategoryIds),
|
|
723
|
-
excludedElementClassNames: Array.from(this._excludedElementClasses, (cls) => cls.classFullName),
|
|
724
|
-
excludedElementAspectClassFullNames: [...this._exportElementAspectsStrategy.excludedElementAspectClassFullNames],
|
|
725
|
-
excludedRelationshipClassNames: Array.from(this._excludedRelationshipClasses, (cls) => cls.classFullName),
|
|
726
|
-
additionalState: this.getAdditionalStateJson(),
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
711
|
}
|
|
730
712
|
exports.IModelExporter = IModelExporter;
|
|
731
|
-
/**
|
|
732
|
-
* Asserts that the passed in options have exactly one of:
|
|
733
|
-
* startChangeset xor changesetRanges xor changedInstanceIds
|
|
734
|
-
* defined
|
|
735
|
-
*/
|
|
736
|
-
function assertHasChangeDataOptions(opts) {
|
|
737
|
-
const xor = (...args) => {
|
|
738
|
-
let result = false;
|
|
739
|
-
for (const a of args) {
|
|
740
|
-
if (!result && a)
|
|
741
|
-
result = true;
|
|
742
|
-
else if (result && a)
|
|
743
|
-
return false;
|
|
744
|
-
}
|
|
745
|
-
return result;
|
|
746
|
-
};
|
|
747
|
-
nodeAssert(xor(opts.startChangeset, opts.changesetRanges, opts.changedInstanceIds), "exactly one of startChangeset, XOR changesetRanges XOR opts.changedInstanceIds may be defined but "
|
|
748
|
-
+ `received ${JSON.stringify({
|
|
749
|
-
startChangeset: !!opts.startChangeset,
|
|
750
|
-
changesetRanges: !!opts.changesetRanges,
|
|
751
|
-
ChangedInstanceIds: !!opts.changedInstanceIds,
|
|
752
|
-
})}`);
|
|
753
|
-
}
|
|
754
713
|
/** Class for holding change information.
|
|
755
714
|
* @beta
|
|
756
|
-
*/
|
|
715
|
+
*/
|
|
757
716
|
class ChangedInstanceOps {
|
|
758
717
|
constructor() {
|
|
759
718
|
this.insertIds = new Set();
|
|
@@ -763,11 +722,11 @@ class ChangedInstanceOps {
|
|
|
763
722
|
/** Initializes the object from IModelJsNative.ChangedInstanceOpsProps. */
|
|
764
723
|
addFromJson(val) {
|
|
765
724
|
if (undefined !== val) {
|
|
766
|
-
if (
|
|
725
|
+
if (undefined !== val.insert && Array.isArray(val.insert))
|
|
767
726
|
val.insert.forEach((id) => this.insertIds.add(id));
|
|
768
|
-
if (
|
|
727
|
+
if (undefined !== val.update && Array.isArray(val.update))
|
|
769
728
|
val.update.forEach((id) => this.updateIds.add(id));
|
|
770
|
-
if (
|
|
729
|
+
if (undefined !== val.delete && Array.isArray(val.delete))
|
|
771
730
|
val.delete.forEach((id) => this.deleteIds.add(id));
|
|
772
731
|
}
|
|
773
732
|
}
|
|
@@ -778,69 +737,174 @@ exports.ChangedInstanceOps = ChangedInstanceOps;
|
|
|
778
737
|
* @beta
|
|
779
738
|
*/
|
|
780
739
|
class ChangedInstanceIds {
|
|
781
|
-
constructor() {
|
|
740
|
+
constructor(db) {
|
|
782
741
|
this.codeSpec = new ChangedInstanceOps();
|
|
783
742
|
this.model = new ChangedInstanceOps();
|
|
784
743
|
this.element = new ChangedInstanceOps();
|
|
785
744
|
this.aspect = new ChangedInstanceOps();
|
|
786
745
|
this.relationship = new ChangedInstanceOps();
|
|
787
746
|
this.font = new ChangedInstanceOps();
|
|
747
|
+
this._db = db;
|
|
748
|
+
}
|
|
749
|
+
async setupECClassIds() {
|
|
750
|
+
this._codeSpecSubclassIds = new Set();
|
|
751
|
+
this._modelSubclassIds = new Set();
|
|
752
|
+
this._elementSubclassIds = new Set();
|
|
753
|
+
this._aspectSubclassIds = new Set();
|
|
754
|
+
this._relationshipSubclassIds = new Set();
|
|
755
|
+
const addECClassIdsToSet = async (setToModify, baseClass) => {
|
|
756
|
+
for await (const row of this._db.createQueryReader(`SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (${baseClass})`)) {
|
|
757
|
+
setToModify.add(row.ECInstanceId);
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
const promises = [
|
|
761
|
+
addECClassIdsToSet(this._codeSpecSubclassIds, "BisCore.CodeSpec"),
|
|
762
|
+
addECClassIdsToSet(this._modelSubclassIds, "BisCore.Model"),
|
|
763
|
+
addECClassIdsToSet(this._elementSubclassIds, "BisCore.Element"),
|
|
764
|
+
addECClassIdsToSet(this._aspectSubclassIds, "BisCore.ElementUniqueAspect"),
|
|
765
|
+
addECClassIdsToSet(this._aspectSubclassIds, "BisCore.ElementMultiAspect"),
|
|
766
|
+
addECClassIdsToSet(this._relationshipSubclassIds, "BisCore.ElementRefersToElements"),
|
|
767
|
+
];
|
|
768
|
+
await Promise.all(promises);
|
|
769
|
+
}
|
|
770
|
+
get _ecClassIdsInitialized() {
|
|
771
|
+
return (this._codeSpecSubclassIds &&
|
|
772
|
+
this._modelSubclassIds &&
|
|
773
|
+
this._elementSubclassIds &&
|
|
774
|
+
this._aspectSubclassIds &&
|
|
775
|
+
this._relationshipSubclassIds);
|
|
776
|
+
}
|
|
777
|
+
isRelationship(ecClassId) {
|
|
778
|
+
return this._relationshipSubclassIds?.has(ecClassId);
|
|
779
|
+
}
|
|
780
|
+
isCodeSpec(ecClassId) {
|
|
781
|
+
return this._codeSpecSubclassIds?.has(ecClassId);
|
|
782
|
+
}
|
|
783
|
+
isAspect(ecClassId) {
|
|
784
|
+
return this._aspectSubclassIds?.has(ecClassId);
|
|
785
|
+
}
|
|
786
|
+
isModel(ecClassId) {
|
|
787
|
+
return this._modelSubclassIds?.has(ecClassId);
|
|
788
|
+
}
|
|
789
|
+
isElement(ecClassId) {
|
|
790
|
+
return this._elementSubclassIds?.has(ecClassId);
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Adds the provided [[ChangedECInstance]] to the appropriate set of changes by class type (codeSpec, model, element, aspect, or relationship) maintained by this instance of ChangedInstanceIds.
|
|
794
|
+
* If the same ECInstanceId is seen multiple times, the changedInstanceIds will be modified accordingly, i.e. if an id 'x' was updated but now we see 'x' was deleted, we will remove 'x'
|
|
795
|
+
* from the set of updatedIds and add it to the set of deletedIds for the appropriate class type.
|
|
796
|
+
* @param change ChangedECInstance which has the ECInstanceId, changeType (insert, update, delete) and ECClassId of the changed entity
|
|
797
|
+
*/
|
|
798
|
+
async addChange(change) {
|
|
799
|
+
if (!this._ecClassIdsInitialized)
|
|
800
|
+
await this.setupECClassIds();
|
|
801
|
+
const ecClassId = change.ECClassId ?? change.$meta?.fallbackClassId;
|
|
802
|
+
if (ecClassId === undefined)
|
|
803
|
+
throw new Error(`ECClassId was not found for id: ${change.ECInstanceId}! Table is : ${change?.$meta?.tables}`);
|
|
804
|
+
const changeType = change.$meta?.op;
|
|
805
|
+
if (changeType === undefined)
|
|
806
|
+
throw new Error(`ChangeType was undefined for id: ${change.ECInstanceId}.`);
|
|
807
|
+
if (this.isRelationship(ecClassId))
|
|
808
|
+
this.handleChange(this.relationship, changeType, change.ECInstanceId);
|
|
809
|
+
else if (this.isCodeSpec(ecClassId))
|
|
810
|
+
this.handleChange(this.codeSpec, changeType, change.ECInstanceId);
|
|
811
|
+
else if (this.isAspect(ecClassId))
|
|
812
|
+
this.handleChange(this.aspect, changeType, change.ECInstanceId);
|
|
813
|
+
else if (this.isModel(ecClassId))
|
|
814
|
+
this.handleChange(this.model, changeType, change.ECInstanceId);
|
|
815
|
+
else if (this.isElement(ecClassId))
|
|
816
|
+
this.handleChange(this.element, changeType, change.ECInstanceId);
|
|
817
|
+
}
|
|
818
|
+
handleChange(changedInstanceOps, changeType, id) {
|
|
819
|
+
// if changeType is a delete and we already have the id in the inserts then we can remove the id from the inserts.
|
|
820
|
+
// if changeType is a delete and we already have the id in the updates then we can remove the id from the updates AND add it to the deletes.
|
|
821
|
+
// if changeType is an insert and we already have the id in the deletes then we can remove the id from the deletes AND add it to the inserts.
|
|
822
|
+
if (changeType === "Inserted") {
|
|
823
|
+
changedInstanceOps.insertIds.add(id);
|
|
824
|
+
changedInstanceOps.deleteIds.delete(id);
|
|
825
|
+
}
|
|
826
|
+
else if (changeType === "Updated") {
|
|
827
|
+
if (!changedInstanceOps.insertIds.has(id))
|
|
828
|
+
changedInstanceOps.updateIds.add(id);
|
|
829
|
+
}
|
|
830
|
+
else if (changeType === "Deleted") {
|
|
831
|
+
// If we've inserted the entity at some point already and now we're seeing a delete. We can simply remove the entity from our inserted ids without adding it to deletedIds.
|
|
832
|
+
if (changedInstanceOps.insertIds.has(id))
|
|
833
|
+
changedInstanceOps.insertIds.delete(id);
|
|
834
|
+
else {
|
|
835
|
+
changedInstanceOps.updateIds.delete(id);
|
|
836
|
+
changedInstanceOps.deleteIds.add(id);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
788
839
|
}
|
|
789
840
|
/**
|
|
790
841
|
* Initializes a new ChangedInstanceIds object with information taken from a range of changesets.
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
* @note Modified element information will be taken from a range of changesets. First changeset in a range will be the 'firstChangesetId', the last will be whichever changeset the 'iModel' briefcase is currently opened on.
|
|
796
|
-
*/
|
|
797
|
-
static async initialize(accessTokenOrOpts, iModel, startChangesetId) {
|
|
798
|
-
const opts = typeof accessTokenOrOpts === "object"
|
|
799
|
-
? accessTokenOrOpts
|
|
800
|
-
: {
|
|
801
|
-
accessToken: accessTokenOrOpts,
|
|
802
|
-
iModel: iModel,
|
|
803
|
-
startChangeset: { id: startChangesetId },
|
|
804
|
-
};
|
|
805
|
-
assertHasChangeDataOptions(opts);
|
|
842
|
+
*/
|
|
843
|
+
static async initialize(opts) {
|
|
844
|
+
if ("changedInstanceIds" in opts)
|
|
845
|
+
return opts.changedInstanceIds;
|
|
806
846
|
const iModelId = opts.iModel.iModelId;
|
|
807
847
|
const accessToken = opts.accessToken;
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
848
|
+
const startChangeset = "startChangeset" in opts ? opts.startChangeset : undefined;
|
|
849
|
+
const changesetRanges = startChangeset !== undefined
|
|
850
|
+
? [
|
|
851
|
+
[
|
|
852
|
+
startChangeset.index ??
|
|
853
|
+
(await core_backend_1.IModelHost.hubAccess.queryChangeset({
|
|
813
854
|
iModelId,
|
|
814
|
-
changeset: {
|
|
855
|
+
changeset: {
|
|
856
|
+
id: startChangeset.id ?? opts.iModel.changeset.id,
|
|
857
|
+
},
|
|
815
858
|
accessToken,
|
|
816
859
|
})).index,
|
|
817
|
-
opts.iModel.changeset.index
|
|
818
|
-
|
|
860
|
+
opts.iModel.changeset.index ??
|
|
861
|
+
(await core_backend_1.IModelHost.hubAccess.queryChangeset({
|
|
819
862
|
iModelId,
|
|
820
863
|
changeset: { id: opts.iModel.changeset.id },
|
|
821
864
|
accessToken,
|
|
822
865
|
})).index,
|
|
823
|
-
]
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
changedInstanceIds
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
866
|
+
],
|
|
867
|
+
]
|
|
868
|
+
: "changesetRanges" in opts
|
|
869
|
+
? opts.changesetRanges
|
|
870
|
+
: undefined;
|
|
871
|
+
const csFileProps = changesetRanges !== undefined
|
|
872
|
+
? (await Promise.all(changesetRanges.map(async ([first, end]) => core_backend_1.IModelHost.hubAccess.downloadChangesets({
|
|
873
|
+
accessToken,
|
|
874
|
+
iModelId,
|
|
875
|
+
range: { first, end },
|
|
876
|
+
targetDir: core_backend_1.BriefcaseManager.getChangeSetsPath(iModelId),
|
|
877
|
+
})))).flat()
|
|
878
|
+
: "csFileProps" in opts
|
|
879
|
+
? opts.csFileProps
|
|
880
|
+
: undefined;
|
|
881
|
+
if (csFileProps === undefined)
|
|
882
|
+
return undefined;
|
|
883
|
+
const changedInstanceIds = new ChangedInstanceIds(opts.iModel);
|
|
884
|
+
const relationshipECClassIdsToSkip = new Set();
|
|
885
|
+
for await (const row of opts.iModel.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementDrivesElement)")) {
|
|
886
|
+
relationshipECClassIdsToSkip.add(row.ECInstanceId);
|
|
887
|
+
}
|
|
888
|
+
for (const csFile of csFileProps) {
|
|
889
|
+
const csReader = core_backend_1.SqliteChangesetReader.openFile({
|
|
890
|
+
fileName: csFile.pathname,
|
|
891
|
+
db: opts.iModel,
|
|
892
|
+
disableSchemaCheck: true,
|
|
893
|
+
});
|
|
894
|
+
const csAdaptor = new core_backend_1.ChangesetECAdaptor(csReader);
|
|
895
|
+
const ecChangeUnifier = new core_backend_1.PartialECChangeUnifier();
|
|
896
|
+
while (csAdaptor.step()) {
|
|
897
|
+
ecChangeUnifier.appendFrom(csAdaptor);
|
|
898
|
+
}
|
|
899
|
+
const changes = [...ecChangeUnifier.instances];
|
|
900
|
+
for (const change of changes) {
|
|
901
|
+
if (change.ECClassId !== undefined &&
|
|
902
|
+
relationshipECClassIdsToSkip.has(change.ECClassId))
|
|
903
|
+
continue;
|
|
904
|
+
await changedInstanceIds.addChange(change);
|
|
905
|
+
}
|
|
906
|
+
csReader.close();
|
|
907
|
+
}
|
|
844
908
|
return changedInstanceIds;
|
|
845
909
|
}
|
|
846
910
|
}
|