@itwin/imodel-transformer 2.0.0-dev.3 → 2.0.0-dev.31

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 (65) hide show
  1. package/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
  2. package/lib/cjs/BranchProvenanceInitializer.js +24 -18
  3. package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
  4. package/lib/cjs/ChangesetScanner.d.ts +60 -0
  5. package/lib/cjs/ChangesetScanner.d.ts.map +1 -0
  6. package/lib/cjs/ChangesetScanner.js +101 -0
  7. package/lib/cjs/ChangesetScanner.js.map +1 -0
  8. package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +1 -1
  9. package/lib/cjs/DetachedExportElementAspectsStrategy.js +8 -6
  10. package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +1 -1
  11. package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
  12. package/lib/cjs/ECReferenceTypesCache.js +60 -38
  13. package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
  14. package/lib/cjs/ElementCascadingDeleter.d.ts +3 -3
  15. package/lib/cjs/ElementCascadingDeleter.d.ts.map +1 -1
  16. package/lib/cjs/ElementCascadingDeleter.js +12 -12
  17. package/lib/cjs/ElementCascadingDeleter.js.map +1 -1
  18. package/lib/cjs/EntityUnifier.d.ts +4 -8
  19. package/lib/cjs/EntityUnifier.d.ts.map +1 -1
  20. package/lib/cjs/EntityUnifier.js +6 -24
  21. package/lib/cjs/EntityUnifier.js.map +1 -1
  22. package/lib/cjs/ExportElementAspectsStrategy.d.ts +4 -4
  23. package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +1 -1
  24. package/lib/cjs/ExportElementAspectsStrategy.js +1 -1
  25. package/lib/cjs/ExportElementAspectsStrategy.js.map +1 -1
  26. package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +1 -1
  27. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +17 -16
  28. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +1 -1
  29. package/lib/cjs/IModelCloneContext.d.ts +3 -3
  30. package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
  31. package/lib/cjs/IModelCloneContext.js +70 -54
  32. package/lib/cjs/IModelCloneContext.js.map +1 -1
  33. package/lib/cjs/IModelExporter.d.ts +33 -23
  34. package/lib/cjs/IModelExporter.d.ts.map +1 -1
  35. package/lib/cjs/IModelExporter.js +200 -152
  36. package/lib/cjs/IModelExporter.js.map +1 -1
  37. package/lib/cjs/IModelImporter.d.ts +32 -24
  38. package/lib/cjs/IModelImporter.d.ts.map +1 -1
  39. package/lib/cjs/IModelImporter.js +199 -127
  40. package/lib/cjs/IModelImporter.js.map +1 -1
  41. package/lib/cjs/IModelTransformer.d.ts +94 -186
  42. package/lib/cjs/IModelTransformer.d.ts.map +1 -1
  43. package/lib/cjs/IModelTransformer.js +502 -1075
  44. package/lib/cjs/IModelTransformer.js.map +1 -1
  45. package/lib/cjs/IModelTransformerError.d.ts +74 -0
  46. package/lib/cjs/IModelTransformerError.d.ts.map +1 -0
  47. package/lib/cjs/IModelTransformerError.js +83 -0
  48. package/lib/cjs/IModelTransformerError.js.map +1 -0
  49. package/lib/cjs/ProvenanceManager.d.ts +165 -0
  50. package/lib/cjs/ProvenanceManager.d.ts.map +1 -0
  51. package/lib/cjs/ProvenanceManager.js +735 -0
  52. package/lib/cjs/ProvenanceManager.js.map +1 -0
  53. package/lib/cjs/SyncTypeResolver.d.ts +34 -0
  54. package/lib/cjs/SyncTypeResolver.d.ts.map +1 -0
  55. package/lib/cjs/SyncTypeResolver.js +96 -0
  56. package/lib/cjs/SyncTypeResolver.js.map +1 -0
  57. package/lib/cjs/TransformerLoggerCategory.d.ts +2 -1
  58. package/lib/cjs/TransformerLoggerCategory.d.ts.map +1 -1
  59. package/lib/cjs/TransformerLoggerCategory.js +1 -0
  60. package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
  61. package/lib/cjs/imodel-transformer.d.ts +5 -0
  62. package/lib/cjs/imodel-transformer.d.ts.map +1 -1
  63. package/lib/cjs/imodel-transformer.js +26 -7
  64. package/lib/cjs/imodel-transformer.js.map +1 -1
  65. package/package.json +22 -17
@@ -15,6 +15,8 @@ const TransformerLoggerCategory_1 = require("./TransformerLoggerCategory");
15
15
  const core_backend_1 = require("@itwin/core-backend");
16
16
  const assert = require("assert");
17
17
  const ElementCascadingDeleter_1 = require("./ElementCascadingDeleter");
18
+ const ecschema_metadata_1 = require("@itwin/ecschema-metadata");
19
+ const IModelTransformerError_1 = require("./IModelTransformerError");
18
20
  const loggerCategory = TransformerLoggerCategory_1.TransformerLoggerCategory.IModelImporter;
19
21
  /** Base class for importing data into an iModel.
20
22
  * @see [iModel Transformation and Data Exchange]($docs/learning/transformer/index.md)
@@ -25,6 +27,16 @@ const loggerCategory = TransformerLoggerCategory_1.TransformerLoggerCategory.IMo
25
27
  class IModelImporter {
26
28
  /** The read/write target iModel. */
27
29
  targetDb;
30
+ /** The [[EditTxn]] used for write operations on the target iModel.
31
+ * @beta
32
+ */
33
+ _editTxn;
34
+ /** The [[EditTxn]] used for write operations on the target iModel.
35
+ * @beta
36
+ */
37
+ get editTxn() {
38
+ return this._editTxn;
39
+ }
28
40
  /** resolved initialization options for the importer
29
41
  * @beta
30
42
  */
@@ -71,11 +83,12 @@ class IModelImporter {
71
83
  "geometryGuid", // cannot compare GeometricModel.GeometryGuid values across iModels
72
84
  ]);
73
85
  /** Construct a new IModelImporter
74
- * @param targetDb The target IModelDb
86
+ * @param editTxn The [[EditTxn]] to use for write operations on the target iModel. The target db is derived from `editTxn.iModel`.
75
87
  * @param options The options that specify how the import should be done.
76
88
  */
77
- constructor(targetDb, options) {
78
- this.targetDb = targetDb;
89
+ constructor(editTxn, options) {
90
+ this.targetDb = editTxn.iModel;
91
+ this._editTxn = editTxn;
79
92
  this.options = {
80
93
  autoExtendProjectExtents: options?.autoExtendProjectExtents ?? true,
81
94
  preserveElementIdsForFiltering: options?.preserveElementIdsForFiltering ?? false,
@@ -105,9 +118,15 @@ class IModelImporter {
105
118
  this._elementsToUpdateDuringPreserveIds.add(elementId);
106
119
  }
107
120
  /** Import the specified ModelProps (either as an insert or an update) into the target iModel. */
108
- importModel(modelProps) {
121
+ async importModel(modelProps) {
109
122
  if (undefined === modelProps.id || !core_bentley_1.Id64.isValidId64(modelProps.id))
110
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, "Model Id not provided, should be the same as the ModeledElementId");
123
+ core_bentley_1.ITwinError.throwError({
124
+ iTwinErrorId: {
125
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
126
+ key: IModelTransformerError_1.IModelTransformerError.InvalidModelId,
127
+ },
128
+ message: "Model Id not provided, should be the same as the ModeledElementId",
129
+ });
111
130
  if (this.doNotUpdateElement(modelProps.id)) {
112
131
  core_bentley_1.Logger.logInfo(loggerCategory, `Do not update target model ${modelProps.id}`);
113
132
  return;
@@ -115,14 +134,14 @@ class IModelImporter {
115
134
  try {
116
135
  const model = this.targetDb.models.getModel(modelProps.id); // throws IModelError.NotFound if model does not exist
117
136
  if (hasEntityChanged(model, modelProps, this._modelPropertiesToIgnore)) {
118
- this.onUpdateModel(modelProps);
137
+ await this.onUpdateModel(modelProps);
119
138
  }
120
139
  }
121
140
  catch (error) {
122
141
  // catch NotFound error and insertModel
123
142
  if (error instanceof core_common_1.IModelError &&
124
143
  error.errorNumber === core_bentley_1.IModelStatus.NotFound) {
125
- this.onInsertModel(modelProps);
144
+ await this.onInsertModel(modelProps);
126
145
  return;
127
146
  }
128
147
  throw error;
@@ -131,29 +150,35 @@ class IModelImporter {
131
150
  /** Create a new Model from the specified ModelProps and insert it into the target iModel.
132
151
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertModel`.
133
152
  */
134
- onInsertModel(modelProps) {
153
+ async onInsertModel(modelProps) {
135
154
  try {
136
- const modelId = this.targetDb.models.insertModel(modelProps);
155
+ const modelId = this._editTxn.insertModel(modelProps);
137
156
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatModelForLogger(modelProps)}`);
138
- this.trackProgress();
157
+ await this.trackProgress();
139
158
  return modelId;
140
159
  }
141
160
  catch (error) {
142
161
  if (!this.targetDb.containsClass(modelProps.classFullName)) {
143
- // replace standard insert error with something more helpful
144
- const errorMessage = `Model class "${modelProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`;
145
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidName, errorMessage);
162
+ // Translate only confirmed missing-class failures; otherwise retain the backend error's identity.
163
+ core_bentley_1.ITwinError.throwError({
164
+ iTwinErrorId: {
165
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
166
+ key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
167
+ },
168
+ message: `Model class "${modelProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
169
+ cause: error,
170
+ });
146
171
  }
147
- throw error; // throw original error
172
+ throw error;
148
173
  }
149
174
  }
150
175
  /** Update an existing Model in the target iModel from the specified ModelProps.
151
176
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateModel`.
152
177
  */
153
- onUpdateModel(modelProps) {
154
- this.targetDb.models.updateModel(modelProps);
178
+ async onUpdateModel(modelProps) {
179
+ this._editTxn.updateModel(modelProps);
155
180
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatModelForLogger(modelProps)}`);
156
- this.trackProgress();
181
+ await this.trackProgress();
157
182
  }
158
183
  /** Format a Model for the Logger. */
159
184
  formatModelForLogger(modelProps) {
@@ -164,9 +189,9 @@ class IModelImporter {
164
189
  * Tries to update an element with the specified element properties.
165
190
  * If a duplicate code error occurs, it assigns a new unique code value and retries the update
166
191
  */
167
- tryUpdateElement(elemProps) {
192
+ async tryUpdateElement(elemProps) {
168
193
  try {
169
- this.onUpdateElement(elemProps);
194
+ await this.onUpdateElement(elemProps);
170
195
  }
171
196
  catch (err) {
172
197
  if (err.errorNumber === core_bentley_1.IModelStatus.DuplicateCode) {
@@ -175,7 +200,7 @@ class IModelImporter {
175
200
  this._duplicateCodeValueMap.set(elemProps.id, elemProps.code.value);
176
201
  // Using NULL code values as an alternative is not valid because definition elements cannot have NULL code values.
177
202
  elemProps.code.value = core_bentley_1.Guid.createValue();
178
- this.onUpdateElement(elemProps);
203
+ await this.onUpdateElement(elemProps);
179
204
  }
180
205
  else {
181
206
  throw err;
@@ -183,7 +208,7 @@ class IModelImporter {
183
208
  }
184
209
  }
185
210
  /** Import the specified ElementProps (either as an insert or an update) into the target iModel. */
186
- importElement(elementProps) {
211
+ async importElement(elementProps) {
187
212
  if (undefined !== elementProps.id &&
188
213
  this.doNotUpdateElement(elementProps.id)) {
189
214
  core_bentley_1.Logger.logInfo(loggerCategory, `Do not update target element ${elementProps.id}`);
@@ -191,7 +216,13 @@ class IModelImporter {
191
216
  }
192
217
  if (this.options.preserveElementIdsForFiltering) {
193
218
  if (elementProps.id === undefined) {
194
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "elementProps.id must be defined during a preserveIds operation");
219
+ core_bentley_1.ITwinError.throwError({
220
+ iTwinErrorId: {
221
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
222
+ key: IModelTransformerError_1.IModelTransformerError.ElementIdRequired,
223
+ },
224
+ message: "elementProps.id must be defined during a preserveIds operation",
225
+ });
195
226
  }
196
227
  // Categories are the only element that onInserted will immediately insert a new element (their default subcategory)
197
228
  // since default subcategories always exist and always will be inserted after their categories, we treat them as an update
@@ -199,24 +230,24 @@ class IModelImporter {
199
230
  // Always present elements (0xe, 0x1, 0x10) also will be updated to prevent duplicate inserts.
200
231
  if ((isSubCategory(elementProps) && isDefaultSubCategory(elementProps)) ||
201
232
  this._rootElementIds.has(elementProps.id)) {
202
- this.onUpdateElement(elementProps);
233
+ await this.onUpdateElement(elementProps);
203
234
  }
204
235
  else {
205
236
  if (this._elementsToUpdateDuringPreserveIds.has(elementProps.id)) {
206
- this.tryUpdateElement(elementProps);
237
+ await this.tryUpdateElement(elementProps);
207
238
  this._elementsToUpdateDuringPreserveIds.delete(elementProps.id);
208
239
  }
209
240
  else {
210
- this.onInsertElement(elementProps);
241
+ await this.onInsertElement(elementProps);
211
242
  }
212
243
  }
213
244
  }
214
245
  else {
215
246
  if (undefined !== elementProps.id) {
216
- this.tryUpdateElement(elementProps);
247
+ await this.tryUpdateElement(elementProps);
217
248
  }
218
249
  else {
219
- elementProps.id = this.onInsertElement(elementProps); // targetElementProps.id assigned by insertElement
250
+ elementProps.id = await this.onInsertElement(elementProps); // targetElementProps.id assigned by insertElement
220
251
  }
221
252
  }
222
253
  return elementProps.id;
@@ -225,16 +256,15 @@ class IModelImporter {
225
256
  * @returns The Id of the newly inserted Element.
226
257
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertElement`.
227
258
  */
228
- onInsertElement(elementProps) {
229
- /* eslint-disable @typescript-eslint/no-deprecated */
259
+ async onInsertElement(elementProps) {
230
260
  try {
231
- const elementId = this.targetDb.elements.insertElement(elementProps, {
261
+ const elementId = this._editTxn.insertElement(elementProps, {
232
262
  forceUseId: this.options.preserveElementIdsForFiltering,
233
263
  });
234
264
  // set the id like [IModelDb.insertElement]($backend), does, the raw nativeDb method does not
235
265
  elementProps.id = elementId;
236
266
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatElementForLogger(elementProps)}`);
237
- this.trackProgress();
267
+ await this.trackProgress();
238
268
  if (this.options.simplifyElementGeometry) {
239
269
  this.targetDb.simplifyElementGeometry({
240
270
  id: elementId,
@@ -246,26 +276,35 @@ class IModelImporter {
246
276
  }
247
277
  catch (error) {
248
278
  if (!this.targetDb.containsClass(elementProps.classFullName)) {
249
- // replace standard insert error with something more helpful
250
- const errorMessage = `Element class "${elementProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`;
251
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidName, errorMessage);
279
+ core_bentley_1.ITwinError.throwError({
280
+ iTwinErrorId: {
281
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
282
+ key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
283
+ },
284
+ message: `Element class "${elementProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
285
+ cause: error,
286
+ });
252
287
  }
253
- throw error; // throw original error
288
+ throw error;
254
289
  }
255
- /* eslint-enable @typescript-eslint/no-deprecated */
256
290
  }
257
291
  /** Update an existing Element in the target iModel from the specified ElementProps.
258
292
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateElement`.
259
293
  */
260
- onUpdateElement(elementProps) {
294
+ async onUpdateElement(elementProps) {
261
295
  if (!elementProps.id) {
262
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, "ElementId not provided");
296
+ core_bentley_1.ITwinError.throwError({
297
+ iTwinErrorId: {
298
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
299
+ key: IModelTransformerError_1.IModelTransformerError.ElementIdRequired,
300
+ },
301
+ message: "ElementId not provided",
302
+ });
263
303
  }
264
- this.targetDb.elements.updateElement(elementProps);
304
+ this._editTxn.updateElement(elementProps);
265
305
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatElementForLogger(elementProps)}`);
266
- this.trackProgress();
306
+ await this.trackProgress();
267
307
  if (this.options.simplifyElementGeometry) {
268
- /* eslint-disable-next-line @typescript-eslint/no-deprecated */
269
308
  this.targetDb.simplifyElementGeometry({
270
309
  id: elementProps.id,
271
310
  convertBReps: true,
@@ -277,30 +316,30 @@ class IModelImporter {
277
316
  * Will delete special elements like definition elements and subjects.
278
317
  * @note A subclass may override this method to customize delete behavior but should call `super.onDeleteElement`.
279
318
  */
280
- onDeleteElement(elementId) {
281
- (0, ElementCascadingDeleter_1.deleteElementTreeCascade)(this.targetDb, elementId);
319
+ async onDeleteElement(elementId) {
320
+ (0, ElementCascadingDeleter_1.deleteElementTreeCascade)(this._editTxn, elementId);
282
321
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted element ${elementId} and its descendants`);
283
- this.trackProgress();
322
+ await this.trackProgress();
284
323
  }
285
324
  /** Delete the specified Element from the target iModel. */
286
- deleteElement(elementId) {
325
+ async deleteElement(elementId) {
287
326
  if (this.doNotUpdateElement(elementId)) {
288
327
  core_bentley_1.Logger.logInfo(loggerCategory, `Do not delete target element ${elementId}`);
289
328
  return;
290
329
  }
291
- this.onDeleteElement(elementId);
330
+ await this.onDeleteElement(elementId);
292
331
  }
293
332
  /** Delete the specified Model from the target iModel.
294
333
  * @note A subclass may override this method to customize delete behavior but should call `super.onDeleteModel`.
295
334
  */
296
- onDeleteModel(modelId) {
297
- this.targetDb.models.deleteModel(modelId);
335
+ async onDeleteModel(modelId) {
336
+ this._editTxn.deleteModel(modelId);
298
337
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted model ${modelId}`);
299
- this.trackProgress();
338
+ await this.trackProgress();
300
339
  }
301
340
  /** Delete the specified Model from the target iModel. */
302
- deleteModel(modelId) {
303
- this.onDeleteModel(modelId);
341
+ async deleteModel(modelId) {
342
+ await this.onDeleteModel(modelId);
304
343
  }
305
344
  /** Format an Element for the Logger. */
306
345
  formatElementForLogger(elementProps) {
@@ -312,14 +351,14 @@ class IModelImporter {
312
351
  return `${elementProps.classFullName} ${namePiece}[${elementProps.id}]`;
313
352
  }
314
353
  /** Import an ElementUniqueAspect into the target iModel. */
315
- importElementUniqueAspect(aspectProps) {
354
+ async importElementUniqueAspect(aspectProps) {
316
355
  const aspects = this.targetDb.elements.getAspects(aspectProps.element.id, aspectProps.classFullName);
317
356
  if (aspects.length === 0) {
318
357
  return this.onInsertElementAspect(aspectProps);
319
358
  }
320
359
  else if (hasEntityChanged(aspects[0], aspectProps)) {
321
360
  aspectProps.id = aspects[0].id;
322
- this.onUpdateElementAspect(aspectProps);
361
+ await this.onUpdateElementAspect(aspectProps);
323
362
  }
324
363
  return aspects[0].id;
325
364
  }
@@ -329,7 +368,7 @@ class IModelImporter {
329
368
  * @note For insert vs. update reasons, it is important to process all ElementMultiAspects owned by an Element at once since we don't have aspect-specific provenance.
330
369
  * @returns the array of ids of the resulting ElementMultiAspects, in the same order of the aspectPropsArray parameter
331
370
  */
332
- importElementMultiAspects(aspectPropsArray,
371
+ async importElementMultiAspects(aspectPropsArray,
333
372
  /** caller must use this to enforce any aspects added by IModelTransformer are not considered for update */
334
373
  filterFunc = () => true) {
335
374
  const result = new Array(aspectPropsArray.length).fill(undefined);
@@ -337,91 +376,96 @@ class IModelImporter {
337
376
  return result;
338
377
  }
339
378
  const elementId = aspectPropsArray[0].element.id;
340
- // Determine the set of ElementMultiAspect classes to consider
341
- const aspectClassFullNames = new Set();
342
- aspectPropsArray.forEach((aspectsProps) => {
343
- aspectClassFullNames.add(aspectsProps.classFullName);
379
+ const proposedAspectsByClass = new Map();
380
+ aspectPropsArray.forEach((props, index) => {
381
+ const proposedAspects = proposedAspectsByClass.get(props.classFullName) ?? [];
382
+ proposedAspects.push({ props, index });
383
+ proposedAspectsByClass.set(props.classFullName, proposedAspects);
344
384
  });
345
385
  // Handle ElementMultiAspects in groups by class
346
- aspectClassFullNames.forEach((aspectClassFullName) => {
347
- const proposedAspects = aspectPropsArray
348
- .map((props, index) => ({ props, index }))
349
- .filter(({ props }) => aspectClassFullName === props.classFullName);
386
+ for (const [aspectClassFullName, proposedAspects,] of proposedAspectsByClass) {
350
387
  const currentAspects = this.targetDb.elements
351
388
  .getAspects(elementId, aspectClassFullName)
352
389
  .map((props, index) => ({ props, index }))
353
390
  .filter(({ props }) => filterFunc(props));
354
391
  if (proposedAspects.length >= currentAspects.length) {
355
- proposedAspects.forEach(({ props, index: resultIndex }, index) => {
392
+ for (let index = 0; index < proposedAspects.length; index++) {
393
+ const { props, index: resultIndex } = proposedAspects[index];
356
394
  let id;
357
395
  if (index < currentAspects.length) {
358
396
  id = currentAspects[index].props.id;
359
397
  props.id = id;
360
398
  if (hasEntityChanged(currentAspects[index].props, props)) {
361
- this.onUpdateElementAspect(props);
399
+ await this.onUpdateElementAspect(props);
362
400
  }
363
401
  id = props.id;
364
402
  }
365
403
  else {
366
- id = this.onInsertElementAspect(props);
404
+ id = await this.onInsertElementAspect(props);
367
405
  }
368
406
  result[resultIndex] = id;
369
- });
407
+ }
370
408
  }
371
409
  else {
372
- currentAspects.forEach(({ props, index: resultIndex }, index) => {
373
- let id;
410
+ for (let index = 0; index < currentAspects.length; index++) {
411
+ const { props } = currentAspects[index];
374
412
  if (index < proposedAspects.length) {
375
- id = props.id;
376
- proposedAspects[index].props.id = id;
377
- if (hasEntityChanged(props, proposedAspects[index].props)) {
378
- this.onUpdateElementAspect(proposedAspects[index].props);
413
+ const { props: proposedProps, index: resultIndex } = proposedAspects[index];
414
+ const id = props.id;
415
+ proposedProps.id = id;
416
+ if (hasEntityChanged(props, proposedProps)) {
417
+ await this.onUpdateElementAspect(proposedProps);
379
418
  }
380
419
  result[resultIndex] = id;
381
420
  }
382
421
  else {
383
- this.onDeleteElementAspect(props);
422
+ await this.onDeleteElementAspect(props);
384
423
  }
385
- });
424
+ }
386
425
  }
387
- });
388
- assert(result.every((r) => typeof r !== undefined));
426
+ }
427
+ assert(result.every((r) => r !== undefined));
389
428
  return result;
390
429
  }
391
430
  /** Insert the ElementAspect into the target iModel.
392
431
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertElementAspect`.
393
432
  */
394
- onInsertElementAspect(aspectProps) {
433
+ async onInsertElementAspect(aspectProps) {
395
434
  try {
396
- const id = this.targetDb.elements.insertAspect(aspectProps);
435
+ const id = this._editTxn.insertAspect(aspectProps);
397
436
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatElementAspectForLogger(aspectProps)}`);
398
- this.trackProgress();
437
+ await this.trackProgress();
399
438
  return id;
400
439
  }
401
440
  catch (error) {
402
441
  if (!this.targetDb.containsClass(aspectProps.classFullName)) {
403
- // replace standard insert error with something more helpful
404
- const errorMessage = `ElementAspect class "${aspectProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`;
405
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidName, errorMessage);
442
+ core_bentley_1.ITwinError.throwError({
443
+ iTwinErrorId: {
444
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
445
+ key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
446
+ },
447
+ message: `ElementAspect class "${aspectProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
448
+ cause: error,
449
+ });
406
450
  }
407
- throw error; // throw original error
451
+ throw error;
408
452
  }
409
453
  }
410
454
  /** Update the ElementAspect within the target iModel.
411
455
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateElementAspect`.
412
456
  */
413
- onUpdateElementAspect(aspectProps) {
414
- this.targetDb.elements.updateAspect(aspectProps);
457
+ async onUpdateElementAspect(aspectProps) {
458
+ this._editTxn.updateAspect(aspectProps);
415
459
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatElementAspectForLogger(aspectProps)}`);
416
- this.trackProgress();
460
+ await this.trackProgress();
417
461
  }
418
462
  /** Delete the specified ElementAspect from the target iModel.
419
463
  * @note A subclass may override this method to customize delete behavior but should call `super.onDeleteElementAspect`.
420
464
  */
421
- onDeleteElementAspect(targetElementAspect) {
422
- this.targetDb.elements.deleteAspect(targetElementAspect.id);
465
+ async onDeleteElementAspect(targetElementAspect) {
466
+ this._editTxn.deleteAspect(targetElementAspect.id);
423
467
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted ${this.formatElementAspectForLogger(targetElementAspect)}`);
424
- this.trackProgress();
468
+ await this.trackProgress();
425
469
  }
426
470
  /** Format an ElementAspect for the Logger. */
427
471
  formatElementAspectForLogger(elementAspectProps) {
@@ -430,7 +474,7 @@ class IModelImporter {
430
474
  /** Import the specified RelationshipProps (either as an insert or an update) into the target iModel.
431
475
  * @returns The instance Id of the inserted or updated Relationship.
432
476
  */
433
- importRelationship(relationshipProps) {
477
+ async importRelationship(relationshipProps) {
434
478
  if (undefined === relationshipProps.sourceId ||
435
479
  !core_bentley_1.Id64.isValidId64(relationshipProps.sourceId)) {
436
480
  core_bentley_1.Logger.logInfo(loggerCategory, `Ignoring ${relationshipProps.classFullName} instance because of invalid RelationshipProps.sourceId`);
@@ -451,7 +495,7 @@ class IModelImporter {
451
495
  // if relationship found, update it
452
496
  relationshipProps.id = relationship.id;
453
497
  if (hasEntityChanged(relationship, relationshipProps)) {
454
- this.onUpdateRelationship(relationshipProps);
498
+ await this.onUpdateRelationship(relationshipProps);
455
499
  }
456
500
  return relationshipProps.id;
457
501
  }
@@ -463,62 +507,73 @@ class IModelImporter {
463
507
  * @returns The instance Id of the newly inserted relationship.
464
508
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertRelationship`.
465
509
  */
466
- onInsertRelationship(relationshipProps) {
510
+ async onInsertRelationship(relationshipProps) {
467
511
  try {
468
- const targetRelInstanceId = this.targetDb.relationships.insertInstance(relationshipProps);
512
+ const targetRelInstanceId = this._editTxn.insertRelationship(relationshipProps);
469
513
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatRelationshipForLogger(relationshipProps)}`);
470
- this.trackProgress();
514
+ await this.trackProgress();
471
515
  return targetRelInstanceId;
472
516
  }
473
517
  catch (error) {
474
518
  if (!this.targetDb.containsClass(relationshipProps.classFullName)) {
475
- // replace standard insert error with something more helpful
476
- const errorMessage = `Relationship class "${relationshipProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`;
477
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidName, errorMessage);
519
+ core_bentley_1.ITwinError.throwError({
520
+ iTwinErrorId: {
521
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
522
+ key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
523
+ },
524
+ message: `Relationship class "${relationshipProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
525
+ cause: error,
526
+ });
478
527
  }
479
- throw error; // throw original error
528
+ throw error;
480
529
  }
481
530
  }
482
531
  /** Update an existing Relationship in the target iModel from the specified RelationshipProps.
483
532
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateRelationship`.
484
533
  */
485
- onUpdateRelationship(relationshipProps) {
534
+ async onUpdateRelationship(relationshipProps) {
486
535
  if (!relationshipProps.id) {
487
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, "Relationship instance Id not provided");
536
+ core_bentley_1.ITwinError.throwError({
537
+ iTwinErrorId: {
538
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
539
+ key: IModelTransformerError_1.IModelTransformerError.RelationshipIdRequired,
540
+ },
541
+ message: "Relationship instance Id not provided",
542
+ });
488
543
  }
489
- this.targetDb.relationships.updateInstance(relationshipProps);
544
+ this._editTxn.updateRelationship(relationshipProps);
490
545
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatRelationshipForLogger(relationshipProps)}`);
491
- this.trackProgress();
546
+ await this.trackProgress();
492
547
  }
493
548
  /** Delete the specified Relationship from the target iModel. */
494
- onDeleteRelationship(relationshipProps) {
549
+ async onDeleteRelationship(relationshipProps) {
495
550
  // Only passing in what deleteInstance actually uses, full relationshipProps is not necessary.
496
- this.targetDb.relationships.deleteInstance({
551
+ this._editTxn.deleteRelationship({
497
552
  id: relationshipProps.id,
498
553
  classFullName: relationshipProps.classFullName,
499
554
  });
500
555
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted relationship ${relationshipProps.classFullName} id=${relationshipProps.id}`);
501
- this.trackProgress();
556
+ await this.trackProgress();
502
557
  }
503
558
  /** Delete the specified Relationship from the target iModel. */
504
- deleteRelationship(relationshipProps) {
505
- this.onDeleteRelationship(relationshipProps);
559
+ async deleteRelationship(relationshipProps) {
560
+ await this.onDeleteRelationship(relationshipProps);
506
561
  }
507
562
  /** Format a Relationship for the Logger. */
508
563
  formatRelationshipForLogger(relProps) {
509
564
  return `${relProps.classFullName} sourceId=[${relProps.sourceId}] targetId=[${relProps.targetId}]`;
510
565
  }
511
566
  /** Tracks incremental progress */
512
- trackProgress() {
567
+ async trackProgress() {
513
568
  this._progressCounter++;
514
569
  if (0 === this._progressCounter % this.progressInterval) {
515
- this.onProgress();
570
+ await this.onProgress();
516
571
  }
517
572
  }
518
573
  /** This method is called when IModelImporter has made incremental progress based on the [[progressInterval]] setting.
519
574
  * @note A subclass may override this method to report custom progress but should call `super.onProgress`.
520
575
  */
521
- onProgress() { }
576
+ async onProgress() { }
522
577
  /** Optionally compute the projectExtents for the target iModel depending on the options for this IModelImporter.
523
578
  * @note This method is automatically called from [IModelTransformer.process]($transformer).
524
579
  * @see [IModelDb.computeProjectExtents]($backend), [[autoExtendProjectExtents]]
@@ -535,13 +590,15 @@ class IModelImporter {
535
590
  const excludeOutliers = typeof this.options.autoExtendProjectExtents === "object"
536
591
  ? this.options.autoExtendProjectExtents.excludeOutliers
537
592
  : false;
538
- // If excludeOutliers is true, use the extents without outliers. If extentsWithOutliers is undefined, fall back to extents.
539
- const newProjectExtents = excludeOutliers
540
- ? computedProjectExtents.extents
541
- : (computedProjectExtents.extentsWithOutliers ??
542
- computedProjectExtents.extents);
593
+ // If excludeOutliers is true, use the extents without outliers.
594
+ // Otherwise prefer extentsWithOutliers, but fall back to extents if it's undefined or a null range.
595
+ const extentsWithOutliers = computedProjectExtents.extentsWithOutliers;
596
+ const useExtentsWithOutliers = !excludeOutliers && extentsWithOutliers && !extentsWithOutliers.isNull;
597
+ const newProjectExtents = useExtentsWithOutliers
598
+ ? extentsWithOutliers
599
+ : computedProjectExtents.extents;
543
600
  if (!newProjectExtents.isAlmostEqual(this.targetDb.projectExtents)) {
544
- this.targetDb.updateProjectExtents(newProjectExtents);
601
+ this._editTxn.updateProjectExtents(newProjectExtents);
545
602
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated projectExtents=${JSON.stringify(this.targetDb.projectExtents)}`);
546
603
  }
547
604
  if (!excludeOutliers &&
@@ -566,7 +623,6 @@ class IModelImporter {
566
623
  */
567
624
  optimizeGeometry(options) {
568
625
  if (options.inlineUniqueGeometryParts) {
569
- /* eslint-disable-next-line @typescript-eslint/no-deprecated */
570
626
  const result = this.targetDb.inlineGeometryParts();
571
627
  core_bentley_1.Logger.logInfo(loggerCategory, `Inlined ${result.numRefsInlined} references to ${result.numCandidateParts} geometry parts and deleted ${result.numPartsDeleted} parts.`);
572
628
  }
@@ -575,7 +631,7 @@ class IModelImporter {
575
631
  for (const [elementId, codeValue] of this._duplicateCodeValueMap) {
576
632
  const element = this.targetDb.elements.getElement(elementId);
577
633
  element.code.value = codeValue;
578
- element.update();
634
+ this._editTxn.updateElement(element.toJSON());
579
635
  }
580
636
  this._duplicateCodeValueMap.clear();
581
637
  }
@@ -598,22 +654,26 @@ exports.IModelImporter = IModelImporter;
598
654
  */
599
655
  function hasEntityChanged(entity, entityProps, namesToIgnore) {
600
656
  let changed = false;
601
- entity.forEachProperty((propertyName, propertyMeta) => {
657
+ const entityClass = entity.iModel.schemaContext.getSchemaItemSync(entity.schemaItemKey);
658
+ assert(ecschema_metadata_1.EntityClass.isEntityClass(entityClass) ||
659
+ ecschema_metadata_1.RelationshipClass.isRelationshipClass(entityClass), `Cannot get metadata for ${entity.classFullName}.`);
660
+ for (const property of entityClass.getPropertiesSync()) {
661
+ const propertyName = property.name;
602
662
  if (!changed) {
603
663
  if (namesToIgnore && namesToIgnore.has(propertyName)) {
604
664
  // skip
605
665
  }
606
- else if (core_common_1.PrimitiveTypeCode.Binary === propertyMeta.primitiveType) {
607
- changed = hasBinaryValueChanged(entity.asAny[propertyName], entityProps[propertyName]);
666
+ else if (ecschema_metadata_1.PropertyType.Binary === property.propertyType) {
667
+ changed = hasBinaryValueChanged(entity.asAny[core_common_1.ECJsNames.toJsName(propertyName)], entityProps[core_common_1.ECJsNames.toJsName(propertyName)]);
608
668
  }
609
- else if (propertyMeta.isNavigation) {
610
- changed = hasNavigationValueChanged(entity.asAny[propertyName], entityProps[propertyName]);
669
+ else if (property.isNavigation()) {
670
+ changed = hasNavigationValueChanged(entity.asAny[core_common_1.ECJsNames.toJsName(propertyName)], entityProps[core_common_1.ECJsNames.toJsName(propertyName)]);
611
671
  }
612
672
  else {
613
- changed = hasValueChanged(entity.asAny[propertyName], entityProps[propertyName]);
673
+ changed = hasValueChanged(entity.asAny[core_common_1.ECJsNames.toJsName(propertyName)], entityProps[core_common_1.ECJsNames.toJsName(propertyName)]);
614
674
  }
615
675
  }
616
- });
676
+ }
617
677
  return changed;
618
678
  }
619
679
  /** Returns true if the specified binary values are different. */
@@ -643,9 +703,21 @@ function isDefaultSubCategory(props) {
643
703
  if (props.id === undefined)
644
704
  return false;
645
705
  if (!core_bentley_1.Id64.isId64(props.id))
646
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "subcategory had invalid id");
706
+ core_bentley_1.ITwinError.throwError({
707
+ iTwinErrorId: {
708
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
709
+ key: IModelTransformerError_1.IModelTransformerError.InvalidSubCategory,
710
+ },
711
+ message: "subcategory had invalid id",
712
+ });
647
713
  if (props.parent?.id === undefined)
648
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, `subcategory with id ${props.id} had no parent`);
714
+ core_bentley_1.ITwinError.throwError({
715
+ iTwinErrorId: {
716
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
717
+ key: IModelTransformerError_1.IModelTransformerError.InvalidSubCategory,
718
+ },
719
+ message: `subcategory with id ${props.id} had no parent`,
720
+ });
649
721
  return props.id === core_backend_1.IModelDb.getDefaultSubCategoryId(props.parent.id);
650
722
  }
651
723
  //# sourceMappingURL=IModelImporter.js.map