@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.
Files changed (62) hide show
  1. package/lib/cjs/Algo.d.ts.map +1 -1
  2. package/lib/cjs/Algo.js +3 -4
  3. package/lib/cjs/Algo.js.map +1 -1
  4. package/lib/cjs/BigMap.d.ts.map +1 -1
  5. package/lib/cjs/BigMap.js +1 -1
  6. package/lib/cjs/BigMap.js.map +1 -1
  7. package/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
  8. package/lib/cjs/BranchProvenanceInitializer.js +15 -4
  9. package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
  10. package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +1 -1
  11. package/lib/cjs/DetachedExportElementAspectsStrategy.js +12 -5
  12. package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +1 -1
  13. package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
  14. package/lib/cjs/ECReferenceTypesCache.js +32 -18
  15. package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
  16. package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.d.ts +1 -1
  17. package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.d.ts.map +1 -1
  18. package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.js +7 -5
  19. package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.js.map +1 -1
  20. package/lib/cjs/ElementCascadingDeleter.d.ts +3 -3
  21. package/lib/cjs/ElementCascadingDeleter.d.ts.map +1 -1
  22. package/lib/cjs/ElementCascadingDeleter.js +9 -7
  23. package/lib/cjs/ElementCascadingDeleter.js.map +1 -1
  24. package/lib/cjs/EntityMap.d.ts.map +1 -1
  25. package/lib/cjs/EntityMap.js.map +1 -1
  26. package/lib/cjs/EntityUnifier.d.ts +5 -0
  27. package/lib/cjs/EntityUnifier.d.ts.map +1 -1
  28. package/lib/cjs/EntityUnifier.js +22 -35
  29. package/lib/cjs/EntityUnifier.js.map +1 -1
  30. package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +1 -1
  31. package/lib/cjs/ExportElementAspectsStrategy.js +5 -4
  32. package/lib/cjs/ExportElementAspectsStrategy.js.map +1 -1
  33. package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +1 -1
  34. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +9 -5
  35. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +1 -1
  36. package/lib/cjs/IModelCloneContext.d.ts +1 -4
  37. package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
  38. package/lib/cjs/IModelCloneContext.js +21 -40
  39. package/lib/cjs/IModelCloneContext.js.map +1 -1
  40. package/lib/cjs/IModelExporter.d.ts +69 -73
  41. package/lib/cjs/IModelExporter.d.ts.map +1 -1
  42. package/lib/cjs/IModelExporter.js +240 -176
  43. package/lib/cjs/IModelExporter.js.map +1 -1
  44. package/lib/cjs/IModelImporter.d.ts +17 -65
  45. package/lib/cjs/IModelImporter.d.ts.map +1 -1
  46. package/lib/cjs/IModelImporter.js +79 -109
  47. package/lib/cjs/IModelImporter.js.map +1 -1
  48. package/lib/cjs/IModelTransformer.d.ts +143 -99
  49. package/lib/cjs/IModelTransformer.d.ts.map +1 -1
  50. package/lib/cjs/IModelTransformer.js +717 -731
  51. package/lib/cjs/IModelTransformer.js.map +1 -1
  52. package/lib/cjs/PendingReferenceMap.d.ts.map +1 -1
  53. package/lib/cjs/PendingReferenceMap.js +12 -6
  54. package/lib/cjs/PendingReferenceMap.js.map +1 -1
  55. package/lib/cjs/TransformerLoggerCategory.d.ts +2 -2
  56. package/lib/cjs/TransformerLoggerCategory.d.ts.map +1 -1
  57. package/lib/cjs/TransformerLoggerCategory.js +5 -5
  58. package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
  59. package/lib/cjs/transformer.d.ts.map +1 -1
  60. package/lib/cjs/transformer.js +14 -10
  61. package/lib/cjs/transformer.js.map +1 -1
  62. 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) { return true; }
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) { return true; }
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) { return true; }
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) { return true; }
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) { return true; }
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
- const hasChangeData = options.startChangeset || options.changesetRanges || options.changedInstanceIds;
198
- if (this._sourceDbChanges || !this.sourceDb.isBriefcaseDb() || !hasChangeData)
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
- await this.exportModelContents(core_common_1.IModel.repositoryModelId);
261
- await this.exportSubModels(core_common_1.IModel.repositoryModelId);
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 && err.errorNumber === core_bentley_1.IModelStatus.NotFound;
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.deleteIds) {
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, `exportCodeSpecs()`);
344
- const sql = `SELECT Name FROM BisCore:CodeSpec ORDER BY ECInstanceId`;
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) { // is changeset information available?
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, `exportFonts()`);
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
- let isUpdate;
412
- if (undefined !== this._sourceDbChanges) { // is changeset information available?
413
- const fontId = core_bentley_1.Id64.fromUint32Pair(fontNumber, 0); // changeset information uses Id64String, not number
414
- if (this._sourceDbChanges.font.insertIds.has(fontId)) {
415
- isUpdate = false;
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({ id: modeledElementId, wantGeometry: this.wantGeometry, wantBRepData: this.wantGeometry });
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) { // is changeset information available?
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) { // is changeset information available?
483
- if (!this._sourceDbChanges.model.insertIds.has(modelId) && !this._sourceDbChanges.model.updateIds.has(modelId)) {
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 && (element instanceof core_backend_1.RecipeDefinitionElement)) {
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) ? false
573
- : this._sourceDbChanges?.element.updateIds.has(elementId) ? true
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({ id: elementId, wantGeometry: this.wantGeometry, wantBRepData: this.wantGeometry });
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, `visitRelationships=false, skipping exportRelationships()`);
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.getValue(1).getClassNameForClassId();
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) { // is changeset information available?
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 === (this._progressCounter % this.progressInterval)) {
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 ((undefined !== val.insert) && (Array.isArray(val.insert)))
725
+ if (undefined !== val.insert && Array.isArray(val.insert))
767
726
  val.insert.forEach((id) => this.insertIds.add(id));
768
- if ((undefined !== val.update) && (Array.isArray(val.update)))
727
+ if (undefined !== val.update && Array.isArray(val.update))
769
728
  val.update.forEach((id) => this.updateIds.add(id));
770
- if ((undefined !== val.delete) && (Array.isArray(val.delete)))
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
- * @deprecated in 0.1.x. Pass a [[ChangedInstanceIdsInitOptions]] object instead of a changeset id
792
- * @param accessToken Access token.
793
- * @param iModel IModel briefcase whose changesets will be queried.
794
- * @param firstChangesetId Changeset id.
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 changesetRanges = opts.changesetRanges
809
- ?? [[
810
- // we know startChangeset is defined because of the assert above
811
- opts.startChangeset.index
812
- ?? (await core_backend_1.IModelHost.hubAccess.queryChangeset({
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: { id: opts.startChangeset.id ?? opts.iModel.changeset.id },
855
+ changeset: {
856
+ id: startChangeset.id ?? opts.iModel.changeset.id,
857
+ },
815
858
  accessToken,
816
859
  })).index,
817
- opts.iModel.changeset.index
818
- ?? (await core_backend_1.IModelHost.hubAccess.queryChangeset({
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
- core_bentley_1.Logger.logTrace(loggerCategory, `ChangedInstanceIds.initialize ranges: ${changesetRanges.join("|")}`);
825
- const changesets = (await Promise.all(changesetRanges.map(async ([first, end]) => core_backend_1.IModelHost.hubAccess.downloadChangesets({
826
- accessToken,
827
- iModelId, range: { first, end },
828
- targetDir: core_backend_1.BriefcaseManager.getChangeSetsPath(iModelId),
829
- })))).flat();
830
- const changedInstanceIds = new ChangedInstanceIds();
831
- const changesetFiles = changesets.map((c) => c.pathname);
832
- const statusOrResult = opts.iModel.nativeDb.extractChangedInstanceIdsFromChangeSets(changesetFiles);
833
- if (statusOrResult.error) {
834
- throw new core_common_1.IModelError(statusOrResult.error.status, "Error processing changeset");
835
- }
836
- const result = statusOrResult.result;
837
- (0, core_bentley_1.assert)(result !== undefined);
838
- changedInstanceIds.codeSpec.addFromJson(result.codeSpec);
839
- changedInstanceIds.model.addFromJson(result.model);
840
- changedInstanceIds.element.addFromJson(result.element);
841
- changedInstanceIds.aspect.addFromJson(result.aspect);
842
- changedInstanceIds.relationship.addFromJson(result.relationship);
843
- changedInstanceIds.font.addFromJson(result.font);
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
  }