@itwin/imodel-transformer 2.0.0-dev.3 → 2.0.0-dev.30
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/BranchProvenanceInitializer.d.ts.map +1 -1
- package/lib/cjs/BranchProvenanceInitializer.js +24 -18
- package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
- package/lib/cjs/ChangesetScanner.d.ts +60 -0
- package/lib/cjs/ChangesetScanner.d.ts.map +1 -0
- package/lib/cjs/ChangesetScanner.js +101 -0
- package/lib/cjs/ChangesetScanner.js.map +1 -0
- package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.js +8 -6
- package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.js +60 -38
- package/lib/cjs/ECReferenceTypesCache.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 +12 -12
- package/lib/cjs/ElementCascadingDeleter.js.map +1 -1
- package/lib/cjs/EntityUnifier.d.ts +4 -8
- package/lib/cjs/EntityUnifier.d.ts.map +1 -1
- package/lib/cjs/EntityUnifier.js +6 -24
- package/lib/cjs/EntityUnifier.js.map +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.d.ts +4 -4
- package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.js +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +1 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +17 -16
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +1 -1
- package/lib/cjs/IModelCloneContext.d.ts +3 -3
- package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
- package/lib/cjs/IModelCloneContext.js +70 -54
- package/lib/cjs/IModelCloneContext.js.map +1 -1
- package/lib/cjs/IModelExporter.d.ts +33 -23
- package/lib/cjs/IModelExporter.d.ts.map +1 -1
- package/lib/cjs/IModelExporter.js +200 -152
- package/lib/cjs/IModelExporter.js.map +1 -1
- package/lib/cjs/IModelImporter.d.ts +32 -24
- package/lib/cjs/IModelImporter.d.ts.map +1 -1
- package/lib/cjs/IModelImporter.js +199 -127
- package/lib/cjs/IModelImporter.js.map +1 -1
- package/lib/cjs/IModelTransformer.d.ts +94 -186
- package/lib/cjs/IModelTransformer.d.ts.map +1 -1
- package/lib/cjs/IModelTransformer.js +502 -1075
- package/lib/cjs/IModelTransformer.js.map +1 -1
- package/lib/cjs/IModelTransformerError.d.ts +74 -0
- package/lib/cjs/IModelTransformerError.d.ts.map +1 -0
- package/lib/cjs/IModelTransformerError.js +83 -0
- package/lib/cjs/IModelTransformerError.js.map +1 -0
- package/lib/cjs/ProvenanceManager.d.ts +165 -0
- package/lib/cjs/ProvenanceManager.d.ts.map +1 -0
- package/lib/cjs/ProvenanceManager.js +735 -0
- package/lib/cjs/ProvenanceManager.js.map +1 -0
- package/lib/cjs/SyncTypeResolver.d.ts +34 -0
- package/lib/cjs/SyncTypeResolver.d.ts.map +1 -0
- package/lib/cjs/SyncTypeResolver.js +96 -0
- package/lib/cjs/SyncTypeResolver.js.map +1 -0
- package/lib/cjs/TransformerLoggerCategory.d.ts +2 -1
- package/lib/cjs/TransformerLoggerCategory.d.ts.map +1 -1
- package/lib/cjs/TransformerLoggerCategory.js +1 -0
- package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
- package/lib/cjs/imodel-transformer.d.ts +5 -0
- package/lib/cjs/imodel-transformer.d.ts.map +1 -1
- package/lib/cjs/imodel-transformer.js +26 -7
- package/lib/cjs/imodel-transformer.js.map +1 -1
- 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
|
|
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(
|
|
78
|
-
this.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
|
-
|
|
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.
|
|
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
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
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;
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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;
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
373
|
-
|
|
410
|
+
for (let index = 0; index < currentAspects.length; index++) {
|
|
411
|
+
const { props } = currentAspects[index];
|
|
374
412
|
if (index < proposedAspects.length) {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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) =>
|
|
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.
|
|
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
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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;
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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;
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|