@itwin/imodel-transformer 2.0.0-dev.6 → 2.0.0-dev.60
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/README.md +13 -27
- package/lib/cjs/Algo.js.map +1 -1
- package/lib/cjs/BigMap.d.ts.map +1 -1
- package/lib/cjs/BigMap.js.map +1 -1
- package/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
- package/lib/cjs/BranchProvenanceInitializer.js +34 -34
- package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
- package/lib/cjs/ChangedElementForest.d.ts +14 -0
- package/lib/cjs/ChangedElementForest.d.ts.map +1 -0
- package/lib/cjs/ChangedElementForest.js +86 -0
- package/lib/cjs/ChangedElementForest.js.map +1 -0
- package/lib/cjs/ChangesetScanner.d.ts +61 -0
- package/lib/cjs/ChangesetScanner.d.ts.map +1 -0
- package/lib/cjs/ChangesetScanner.js +104 -0
- package/lib/cjs/ChangesetScanner.js.map +1 -0
- package/lib/cjs/ECReferenceTypesCache.d.ts +0 -3
- package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.js +61 -104
- package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
- package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.d.ts.map +1 -1
- package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.js.map +1 -1
- package/lib/cjs/ElementAspectCleanup.d.ts +16 -0
- package/lib/cjs/ElementAspectCleanup.d.ts.map +1 -0
- package/lib/cjs/ElementAspectCleanup.js +87 -0
- package/lib/cjs/ElementAspectCleanup.js.map +1 -0
- package/lib/cjs/ElementAspectExportCoordinator.d.ts +44 -0
- package/lib/cjs/ElementAspectExportCoordinator.d.ts.map +1 -0
- package/lib/cjs/ElementAspectExportCoordinator.js +148 -0
- package/lib/cjs/ElementAspectExportCoordinator.js.map +1 -0
- package/lib/cjs/ElementAspectExportProcessor.d.ts +44 -0
- package/lib/cjs/ElementAspectExportProcessor.d.ts.map +1 -0
- package/lib/cjs/ElementAspectExportProcessor.js +152 -0
- package/lib/cjs/ElementAspectExportProcessor.js.map +1 -0
- package/lib/cjs/ElementBulkDelete.d.ts +11 -0
- package/lib/cjs/ElementBulkDelete.d.ts.map +1 -0
- package/lib/cjs/ElementBulkDelete.js +55 -0
- package/lib/cjs/ElementBulkDelete.js.map +1 -0
- package/lib/cjs/EntityExistenceCache.d.ts +43 -0
- package/lib/cjs/EntityExistenceCache.d.ts.map +1 -0
- package/lib/cjs/EntityExistenceCache.js +91 -0
- package/lib/cjs/EntityExistenceCache.js.map +1 -0
- package/lib/cjs/EntityMap.d.ts.map +1 -1
- package/lib/cjs/EntityMap.js.map +1 -1
- package/lib/cjs/EntityUnifier.d.ts +8 -8
- package/lib/cjs/EntityUnifier.d.ts.map +1 -1
- package/lib/cjs/EntityUnifier.js +56 -32
- package/lib/cjs/EntityUnifier.js.map +1 -1
- package/lib/cjs/IModelCloneContext.d.ts +9 -3
- package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
- package/lib/cjs/IModelCloneContext.js +66 -31
- package/lib/cjs/IModelCloneContext.js.map +1 -1
- package/lib/cjs/IModelExporter.d.ts +143 -30
- package/lib/cjs/IModelExporter.d.ts.map +1 -1
- package/lib/cjs/IModelExporter.js +893 -178
- package/lib/cjs/IModelExporter.js.map +1 -1
- package/lib/cjs/IModelImporter.d.ts +71 -30
- package/lib/cjs/IModelImporter.d.ts.map +1 -1
- package/lib/cjs/IModelImporter.js +269 -132
- package/lib/cjs/IModelImporter.js.map +1 -1
- package/lib/cjs/IModelTransformContext.d.ts +54 -0
- package/lib/cjs/IModelTransformContext.d.ts.map +1 -0
- package/lib/cjs/IModelTransformContext.js +3 -0
- package/lib/cjs/IModelTransformContext.js.map +1 -0
- package/lib/cjs/IModelTransformer.d.ts +130 -196
- package/lib/cjs/IModelTransformer.d.ts.map +1 -1
- package/lib/cjs/IModelTransformer.js +697 -1155
- package/lib/cjs/IModelTransformer.js.map +1 -1
- package/lib/cjs/IModelTransformerError.d.ts +84 -0
- package/lib/cjs/IModelTransformerError.d.ts.map +1 -0
- package/lib/cjs/IModelTransformerError.js +93 -0
- package/lib/cjs/IModelTransformerError.js.map +1 -0
- package/lib/cjs/ProvenanceManager.d.ts +177 -0
- package/lib/cjs/ProvenanceManager.d.ts.map +1 -0
- package/lib/cjs/ProvenanceManager.js +719 -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 +98 -0
- package/lib/cjs/SyncTypeResolver.js.map +1 -0
- package/lib/cjs/TransformerLoggerCategory.d.ts +1 -1
- package/lib/cjs/TransformerLoggerCategory.js +1 -1
- package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
- package/lib/cjs/TransformerPackageMetadata.d.ts +8 -0
- package/lib/cjs/TransformerPackageMetadata.d.ts.map +1 -0
- package/lib/cjs/TransformerPackageMetadata.js +47 -0
- package/lib/cjs/TransformerPackageMetadata.js.map +1 -0
- package/lib/cjs/imodel-transformer.d.ts +27 -0
- package/lib/cjs/imodel-transformer.d.ts.map +1 -1
- package/lib/cjs/imodel-transformer.js +61 -34
- package/lib/cjs/imodel-transformer.js.map +1 -1
- package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.d.ts +29 -0
- package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.d.ts.map +1 -0
- package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.js +212 -0
- package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.js.map +1 -0
- package/lib/cjs/schema-processing/SchemaProcessingCoordinator.d.ts +25 -0
- package/lib/cjs/schema-processing/SchemaProcessingCoordinator.d.ts.map +1 -0
- package/lib/cjs/schema-processing/SchemaProcessingCoordinator.js +196 -0
- package/lib/cjs/schema-processing/SchemaProcessingCoordinator.js.map +1 -0
- package/lib/cjs/schema-processing/SchemaProcessingStrategy.d.ts +59 -0
- package/lib/cjs/schema-processing/SchemaProcessingStrategy.d.ts.map +1 -0
- package/lib/cjs/schema-processing/SchemaProcessingStrategy.js +28 -0
- package/lib/cjs/schema-processing/SchemaProcessingStrategy.js.map +1 -0
- package/lib/cjs/schema-processing.d.ts +7 -0
- package/lib/cjs/schema-processing.d.ts.map +1 -0
- package/lib/cjs/schema-processing.js +27 -0
- package/lib/cjs/schema-processing.js.map +1 -0
- package/package.json +65 -43
- package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts +0 -16
- package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +0 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.js +0 -106
- package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +0 -1
- package/lib/cjs/ElementCascadingDeleter.d.ts +0 -22
- package/lib/cjs/ElementCascadingDeleter.d.ts.map +0 -1
- package/lib/cjs/ElementCascadingDeleter.js +0 -57
- package/lib/cjs/ElementCascadingDeleter.js.map +0 -1
- package/lib/cjs/ExportElementAspectsStrategy.d.ts +0 -35
- package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +0 -1
- package/lib/cjs/ExportElementAspectsStrategy.js +0 -52
- package/lib/cjs/ExportElementAspectsStrategy.js.map +0 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts +0 -12
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +0 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +0 -47
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +0 -1
|
@@ -13,9 +13,11 @@ const core_bentley_1 = require("@itwin/core-bentley");
|
|
|
13
13
|
const core_common_1 = require("@itwin/core-common");
|
|
14
14
|
const TransformerLoggerCategory_1 = require("./TransformerLoggerCategory");
|
|
15
15
|
const core_backend_1 = require("@itwin/core-backend");
|
|
16
|
-
const
|
|
17
|
-
const
|
|
16
|
+
const node_assert_1 = require("node:assert");
|
|
17
|
+
const ElementAspectCleanup_1 = require("./ElementAspectCleanup");
|
|
18
18
|
const ecschema_metadata_1 = require("@itwin/ecschema-metadata");
|
|
19
|
+
const IModelTransformerError_1 = require("./IModelTransformerError");
|
|
20
|
+
const ElementBulkDelete_1 = require("./ElementBulkDelete");
|
|
19
21
|
const loggerCategory = TransformerLoggerCategory_1.TransformerLoggerCategory.IModelImporter;
|
|
20
22
|
/** Base class for importing data into an iModel.
|
|
21
23
|
* @see [iModel Transformation and Data Exchange]($docs/learning/transformer/index.md)
|
|
@@ -26,6 +28,23 @@ const loggerCategory = TransformerLoggerCategory_1.TransformerLoggerCategory.IMo
|
|
|
26
28
|
class IModelImporter {
|
|
27
29
|
/** The read/write target iModel. */
|
|
28
30
|
targetDb;
|
|
31
|
+
/** The [[EditTxn]] used for write operations on the target iModel.
|
|
32
|
+
* @beta
|
|
33
|
+
*/
|
|
34
|
+
_editTxn;
|
|
35
|
+
/** The [[EditTxn]] used for write operations on the target iModel.
|
|
36
|
+
* @beta
|
|
37
|
+
*/
|
|
38
|
+
get editTxn() {
|
|
39
|
+
return this._editTxn;
|
|
40
|
+
}
|
|
41
|
+
_elementAspectCleanup;
|
|
42
|
+
/** Deletes replaceable ElementAspects through this importer's customized deletion callback.
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
get elementAspectCleanup() {
|
|
46
|
+
return this._elementAspectCleanup;
|
|
47
|
+
}
|
|
29
48
|
/** resolved initialization options for the importer
|
|
30
49
|
* @beta
|
|
31
50
|
*/
|
|
@@ -39,6 +58,7 @@ class IModelImporter {
|
|
|
39
58
|
* To resolve code values to their intended values call [[IModelImporter.resolveDuplicateCodeValues]].
|
|
40
59
|
*/
|
|
41
60
|
_duplicateCodeValueMap;
|
|
61
|
+
_entityExistenceCaches = new Set();
|
|
42
62
|
/**
|
|
43
63
|
* A set of elementIds that the transformer adds to while exporting elements to indicate that the element already exists in the target.
|
|
44
64
|
* Defaults to an empty set.
|
|
@@ -52,9 +72,10 @@ class IModelImporter {
|
|
|
52
72
|
* @note This set should stay small, as right after the transformer pushes to it, the importer will remove from the set.
|
|
53
73
|
*/
|
|
54
74
|
_elementsToUpdateDuringPreserveIds = new Set([]);
|
|
55
|
-
/** The set of elements that
|
|
56
|
-
* Defaults to an empty set.
|
|
75
|
+
/** The set of target elements that this importer must not directly update or request for deletion.
|
|
76
|
+
* Defaults to an empty set and is populated by consumers or custom transformers.
|
|
57
77
|
* @note Adding an element to this set is typically necessary when remapping a source element to one that already exists in the target and already has the desired properties.
|
|
78
|
+
* @note Deletion protection applies only when the exact element ID is passed to [[deleteElement]] or [[deleteElements]]. It does not protect the element from deletion through a parent, containing model, or other cascading dependency. Consumers that need to preserve such an element must also add every requested deletion root whose cascade would delete it.
|
|
58
79
|
*/
|
|
59
80
|
doNotUpdateElementIds = new Set([]);
|
|
60
81
|
/** This set is ONLY used for elements that are always present even in an "empty" iModel. We will use this set to filter out changes to root elements if [[IModelTransformOptions.skipPropagateChangesToRootElements]] is false. */
|
|
@@ -72,11 +93,12 @@ class IModelImporter {
|
|
|
72
93
|
"geometryGuid", // cannot compare GeometricModel.GeometryGuid values across iModels
|
|
73
94
|
]);
|
|
74
95
|
/** Construct a new IModelImporter
|
|
75
|
-
* @param
|
|
96
|
+
* @param editTxn The [[EditTxn]] to use for write operations on the target iModel. The target db is derived from `editTxn.iModel`.
|
|
76
97
|
* @param options The options that specify how the import should be done.
|
|
77
98
|
*/
|
|
78
|
-
constructor(
|
|
79
|
-
this.targetDb =
|
|
99
|
+
constructor(editTxn, options) {
|
|
100
|
+
this.targetDb = editTxn.iModel;
|
|
101
|
+
this._editTxn = editTxn;
|
|
80
102
|
this.options = {
|
|
81
103
|
autoExtendProjectExtents: options?.autoExtendProjectExtents ?? true,
|
|
82
104
|
preserveElementIdsForFiltering: options?.preserveElementIdsForFiltering ?? false,
|
|
@@ -84,6 +106,19 @@ class IModelImporter {
|
|
|
84
106
|
skipPropagateChangesToRootElements: options?.skipPropagateChangesToRootElements ?? true,
|
|
85
107
|
};
|
|
86
108
|
this._duplicateCodeValueMap = new Map();
|
|
109
|
+
this._elementAspectCleanup = new ElementAspectCleanup_1.ElementAspectCleanup(this.targetDb, this._editTxn, async (aspect) => this.onDeleteElementAspect(aspect));
|
|
110
|
+
}
|
|
111
|
+
/** Register a transformation-scoped existence cache for delete invalidation.
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
registerEntityExistenceCache(cache) {
|
|
115
|
+
this._entityExistenceCaches.add(cache);
|
|
116
|
+
}
|
|
117
|
+
/** Stop invalidating a transformation-scoped existence cache.
|
|
118
|
+
* @internal
|
|
119
|
+
*/
|
|
120
|
+
unregisterEntityExistenceCache(cache) {
|
|
121
|
+
this._entityExistenceCaches.delete(cache);
|
|
87
122
|
}
|
|
88
123
|
/**
|
|
89
124
|
* Checks [[IModelImportOptions.skipPropagateChangesToRootElements]], [[IModelImporter.rootElementIds]], and [[IModelImporter.doNotUpdateElementIds]] and returns true for 'do not update', false for do 'update'.
|
|
@@ -106,9 +141,15 @@ class IModelImporter {
|
|
|
106
141
|
this._elementsToUpdateDuringPreserveIds.add(elementId);
|
|
107
142
|
}
|
|
108
143
|
/** Import the specified ModelProps (either as an insert or an update) into the target iModel. */
|
|
109
|
-
importModel(modelProps) {
|
|
144
|
+
async importModel(modelProps) {
|
|
110
145
|
if (undefined === modelProps.id || !core_bentley_1.Id64.isValidId64(modelProps.id))
|
|
111
|
-
|
|
146
|
+
core_bentley_1.ITwinError.throwError({
|
|
147
|
+
iTwinErrorId: {
|
|
148
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
149
|
+
key: IModelTransformerError_1.IModelTransformerError.InvalidModelId,
|
|
150
|
+
},
|
|
151
|
+
message: "Model Id not provided, should be the same as the ModeledElementId",
|
|
152
|
+
});
|
|
112
153
|
if (this.doNotUpdateElement(modelProps.id)) {
|
|
113
154
|
core_bentley_1.Logger.logInfo(loggerCategory, `Do not update target model ${modelProps.id}`);
|
|
114
155
|
return;
|
|
@@ -116,14 +157,14 @@ class IModelImporter {
|
|
|
116
157
|
try {
|
|
117
158
|
const model = this.targetDb.models.getModel(modelProps.id); // throws IModelError.NotFound if model does not exist
|
|
118
159
|
if (hasEntityChanged(model, modelProps, this._modelPropertiesToIgnore)) {
|
|
119
|
-
this.onUpdateModel(modelProps);
|
|
160
|
+
await this.onUpdateModel(modelProps);
|
|
120
161
|
}
|
|
121
162
|
}
|
|
122
163
|
catch (error) {
|
|
123
164
|
// catch NotFound error and insertModel
|
|
124
165
|
if (error instanceof core_common_1.IModelError &&
|
|
125
166
|
error.errorNumber === core_bentley_1.IModelStatus.NotFound) {
|
|
126
|
-
this.onInsertModel(modelProps);
|
|
167
|
+
await this.onInsertModel(modelProps);
|
|
127
168
|
return;
|
|
128
169
|
}
|
|
129
170
|
throw error;
|
|
@@ -132,29 +173,35 @@ class IModelImporter {
|
|
|
132
173
|
/** Create a new Model from the specified ModelProps and insert it into the target iModel.
|
|
133
174
|
* @note A subclass may override this method to customize insert behavior but should call `super.onInsertModel`.
|
|
134
175
|
*/
|
|
135
|
-
onInsertModel(modelProps) {
|
|
176
|
+
async onInsertModel(modelProps) {
|
|
136
177
|
try {
|
|
137
|
-
const modelId = this.
|
|
178
|
+
const modelId = this._editTxn.insertModel(modelProps);
|
|
138
179
|
core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatModelForLogger(modelProps)}`);
|
|
139
|
-
this.trackProgress();
|
|
180
|
+
await this.trackProgress();
|
|
140
181
|
return modelId;
|
|
141
182
|
}
|
|
142
183
|
catch (error) {
|
|
143
184
|
if (!this.targetDb.containsClass(modelProps.classFullName)) {
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
185
|
+
// Translate only confirmed missing-class failures; otherwise retain the backend error's identity.
|
|
186
|
+
core_bentley_1.ITwinError.throwError({
|
|
187
|
+
iTwinErrorId: {
|
|
188
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
189
|
+
key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
|
|
190
|
+
},
|
|
191
|
+
message: `Model class "${modelProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
|
|
192
|
+
cause: error,
|
|
193
|
+
});
|
|
147
194
|
}
|
|
148
|
-
throw error;
|
|
195
|
+
throw error;
|
|
149
196
|
}
|
|
150
197
|
}
|
|
151
198
|
/** Update an existing Model in the target iModel from the specified ModelProps.
|
|
152
199
|
* @note A subclass may override this method to customize update behavior but should call `super.onUpdateModel`.
|
|
153
200
|
*/
|
|
154
|
-
onUpdateModel(modelProps) {
|
|
155
|
-
this.
|
|
201
|
+
async onUpdateModel(modelProps) {
|
|
202
|
+
this._editTxn.updateModel(modelProps);
|
|
156
203
|
core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatModelForLogger(modelProps)}`);
|
|
157
|
-
this.trackProgress();
|
|
204
|
+
await this.trackProgress();
|
|
158
205
|
}
|
|
159
206
|
/** Format a Model for the Logger. */
|
|
160
207
|
formatModelForLogger(modelProps) {
|
|
@@ -165,18 +212,18 @@ class IModelImporter {
|
|
|
165
212
|
* Tries to update an element with the specified element properties.
|
|
166
213
|
* If a duplicate code error occurs, it assigns a new unique code value and retries the update
|
|
167
214
|
*/
|
|
168
|
-
tryUpdateElement(elemProps) {
|
|
215
|
+
async tryUpdateElement(elemProps) {
|
|
169
216
|
try {
|
|
170
|
-
this.onUpdateElement(elemProps);
|
|
217
|
+
await this.onUpdateElement(elemProps);
|
|
171
218
|
}
|
|
172
219
|
catch (err) {
|
|
173
220
|
if (err.errorNumber === core_bentley_1.IModelStatus.DuplicateCode) {
|
|
174
|
-
|
|
221
|
+
(0, node_assert_1.strict)(elemProps.code.value !== undefined, "NULL code values are always considered unique and cannot clash");
|
|
175
222
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
176
223
|
this._duplicateCodeValueMap.set(elemProps.id, elemProps.code.value);
|
|
177
224
|
// Using NULL code values as an alternative is not valid because definition elements cannot have NULL code values.
|
|
178
225
|
elemProps.code.value = core_bentley_1.Guid.createValue();
|
|
179
|
-
this.onUpdateElement(elemProps);
|
|
226
|
+
await this.onUpdateElement(elemProps);
|
|
180
227
|
}
|
|
181
228
|
else {
|
|
182
229
|
throw err;
|
|
@@ -184,7 +231,7 @@ class IModelImporter {
|
|
|
184
231
|
}
|
|
185
232
|
}
|
|
186
233
|
/** Import the specified ElementProps (either as an insert or an update) into the target iModel. */
|
|
187
|
-
importElement(elementProps) {
|
|
234
|
+
async importElement(elementProps) {
|
|
188
235
|
if (undefined !== elementProps.id &&
|
|
189
236
|
this.doNotUpdateElement(elementProps.id)) {
|
|
190
237
|
core_bentley_1.Logger.logInfo(loggerCategory, `Do not update target element ${elementProps.id}`);
|
|
@@ -192,7 +239,13 @@ class IModelImporter {
|
|
|
192
239
|
}
|
|
193
240
|
if (this.options.preserveElementIdsForFiltering) {
|
|
194
241
|
if (elementProps.id === undefined) {
|
|
195
|
-
|
|
242
|
+
core_bentley_1.ITwinError.throwError({
|
|
243
|
+
iTwinErrorId: {
|
|
244
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
245
|
+
key: IModelTransformerError_1.IModelTransformerError.ElementIdRequired,
|
|
246
|
+
},
|
|
247
|
+
message: "elementProps.id must be defined during a preserveIds operation",
|
|
248
|
+
});
|
|
196
249
|
}
|
|
197
250
|
// Categories are the only element that onInserted will immediately insert a new element (their default subcategory)
|
|
198
251
|
// since default subcategories always exist and always will be inserted after their categories, we treat them as an update
|
|
@@ -200,24 +253,24 @@ class IModelImporter {
|
|
|
200
253
|
// Always present elements (0xe, 0x1, 0x10) also will be updated to prevent duplicate inserts.
|
|
201
254
|
if ((isSubCategory(elementProps) && isDefaultSubCategory(elementProps)) ||
|
|
202
255
|
this._rootElementIds.has(elementProps.id)) {
|
|
203
|
-
this.onUpdateElement(elementProps);
|
|
256
|
+
await this.onUpdateElement(elementProps);
|
|
204
257
|
}
|
|
205
258
|
else {
|
|
206
259
|
if (this._elementsToUpdateDuringPreserveIds.has(elementProps.id)) {
|
|
207
|
-
this.tryUpdateElement(elementProps);
|
|
260
|
+
await this.tryUpdateElement(elementProps);
|
|
208
261
|
this._elementsToUpdateDuringPreserveIds.delete(elementProps.id);
|
|
209
262
|
}
|
|
210
263
|
else {
|
|
211
|
-
this.onInsertElement(elementProps);
|
|
264
|
+
await this.onInsertElement(elementProps);
|
|
212
265
|
}
|
|
213
266
|
}
|
|
214
267
|
}
|
|
215
268
|
else {
|
|
216
269
|
if (undefined !== elementProps.id) {
|
|
217
|
-
this.tryUpdateElement(elementProps);
|
|
270
|
+
await this.tryUpdateElement(elementProps);
|
|
218
271
|
}
|
|
219
272
|
else {
|
|
220
|
-
elementProps.id = this.onInsertElement(elementProps); // targetElementProps.id assigned by insertElement
|
|
273
|
+
elementProps.id = await this.onInsertElement(elementProps); // targetElementProps.id assigned by insertElement
|
|
221
274
|
}
|
|
222
275
|
}
|
|
223
276
|
return elementProps.id;
|
|
@@ -226,15 +279,15 @@ class IModelImporter {
|
|
|
226
279
|
* @returns The Id of the newly inserted Element.
|
|
227
280
|
* @note A subclass may override this method to customize insert behavior but should call `super.onInsertElement`.
|
|
228
281
|
*/
|
|
229
|
-
onInsertElement(elementProps) {
|
|
282
|
+
async onInsertElement(elementProps) {
|
|
230
283
|
try {
|
|
231
|
-
const elementId = this.
|
|
284
|
+
const elementId = this._editTxn.insertElement(elementProps, {
|
|
232
285
|
forceUseId: this.options.preserveElementIdsForFiltering,
|
|
233
286
|
});
|
|
234
287
|
// set the id like [IModelDb.insertElement]($backend), does, the raw nativeDb method does not
|
|
235
288
|
elementProps.id = elementId;
|
|
236
289
|
core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatElementForLogger(elementProps)}`);
|
|
237
|
-
this.trackProgress();
|
|
290
|
+
await this.trackProgress();
|
|
238
291
|
if (this.options.simplifyElementGeometry) {
|
|
239
292
|
this.targetDb.simplifyElementGeometry({
|
|
240
293
|
id: elementId,
|
|
@@ -246,23 +299,34 @@ class IModelImporter {
|
|
|
246
299
|
}
|
|
247
300
|
catch (error) {
|
|
248
301
|
if (!this.targetDb.containsClass(elementProps.classFullName)) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
302
|
+
core_bentley_1.ITwinError.throwError({
|
|
303
|
+
iTwinErrorId: {
|
|
304
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
305
|
+
key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
|
|
306
|
+
},
|
|
307
|
+
message: `Element class "${elementProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
|
|
308
|
+
cause: error,
|
|
309
|
+
});
|
|
252
310
|
}
|
|
253
|
-
throw error;
|
|
311
|
+
throw error;
|
|
254
312
|
}
|
|
255
313
|
}
|
|
256
314
|
/** Update an existing Element in the target iModel from the specified ElementProps.
|
|
257
315
|
* @note A subclass may override this method to customize update behavior but should call `super.onUpdateElement`.
|
|
258
316
|
*/
|
|
259
|
-
onUpdateElement(elementProps) {
|
|
317
|
+
async onUpdateElement(elementProps) {
|
|
260
318
|
if (!elementProps.id) {
|
|
261
|
-
|
|
319
|
+
core_bentley_1.ITwinError.throwError({
|
|
320
|
+
iTwinErrorId: {
|
|
321
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
322
|
+
key: IModelTransformerError_1.IModelTransformerError.ElementIdRequired,
|
|
323
|
+
},
|
|
324
|
+
message: "ElementId not provided",
|
|
325
|
+
});
|
|
262
326
|
}
|
|
263
|
-
this.
|
|
327
|
+
this._editTxn.updateElement(elementProps);
|
|
264
328
|
core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatElementForLogger(elementProps)}`);
|
|
265
|
-
this.trackProgress();
|
|
329
|
+
await this.trackProgress();
|
|
266
330
|
if (this.options.simplifyElementGeometry) {
|
|
267
331
|
this.targetDb.simplifyElementGeometry({
|
|
268
332
|
id: elementProps.id,
|
|
@@ -271,34 +335,70 @@ class IModelImporter {
|
|
|
271
335
|
core_bentley_1.Logger.logInfo(loggerCategory, `Simplified element geometry for ${this.formatElementForLogger(elementProps)}`);
|
|
272
336
|
}
|
|
273
337
|
}
|
|
274
|
-
/**
|
|
275
|
-
|
|
276
|
-
|
|
338
|
+
/** Deletes one target element tree through [[deleteElements]]. */
|
|
339
|
+
async deleteElement(elementId) {
|
|
340
|
+
await this.deleteElements(new Set([elementId]));
|
|
341
|
+
}
|
|
342
|
+
/** Adds roots required by code-scope dependencies, then deletes the target element trees in one native operation.
|
|
343
|
+
* @note An override must call `super.onDeleteElements` once to perform the deletion.
|
|
277
344
|
*/
|
|
278
|
-
|
|
279
|
-
(0,
|
|
280
|
-
|
|
281
|
-
this.trackProgress();
|
|
282
|
-
}
|
|
283
|
-
/** Delete the specified Element from the target iModel. */
|
|
284
|
-
deleteElement(elementId) {
|
|
285
|
-
if (this.doNotUpdateElement(elementId)) {
|
|
286
|
-
core_bentley_1.Logger.logInfo(loggerCategory, `Do not delete target element ${elementId}`);
|
|
345
|
+
async onDeleteElements(elementIds) {
|
|
346
|
+
const deleteRoots = await (0, ElementBulkDelete_1.findBulkDeleteRoots)(this.targetDb, elementIds);
|
|
347
|
+
if (deleteRoots.size === 0)
|
|
287
348
|
return;
|
|
349
|
+
const result = this._editTxn.deleteElements([...deleteRoots]);
|
|
350
|
+
if (result.status !== core_backend_1.BulkDeleteElementsStatus.Success) {
|
|
351
|
+
core_bentley_1.ITwinError.throwError({
|
|
352
|
+
iTwinErrorId: {
|
|
353
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
354
|
+
key: IModelTransformerError_1.IModelTransformerError.ElementBulkDeleteFailed,
|
|
355
|
+
},
|
|
356
|
+
message: `Bulk element deletion failed: status ${core_backend_1.BulkDeleteElementsStatus[result.status]}, ` +
|
|
357
|
+
`SQLite status ${result.sqlDeleteStatus}, failed element IDs: ${[
|
|
358
|
+
...result.failedIds,
|
|
359
|
+
].join(", ")}`,
|
|
360
|
+
status: result.status,
|
|
361
|
+
sqlDeleteStatus: result.sqlDeleteStatus,
|
|
362
|
+
failedIds: new Set(result.failedIds),
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
core_bentley_1.Logger.logInfo(loggerCategory, `Deleted ${elementIds.size} requested element trees using ${deleteRoots.size} native deletion roots`);
|
|
366
|
+
await this.trackProgress(elementIds.size);
|
|
367
|
+
}
|
|
368
|
+
/** Deletes target element trees in one native operation.
|
|
369
|
+
* Requested roots in [[doNotUpdateElementIds]] are skipped. This does not prevent an element in that set from being deleted as part of another root's cascade.
|
|
370
|
+
* @throws [[ElementBulkDeleteError]] if native deletion fails for any root. A partial failure leaves successful deletions pending in the caller-owned transaction. Abandon the transaction before retrying.
|
|
371
|
+
*/
|
|
372
|
+
async deleteElements(elementIds) {
|
|
373
|
+
const idsToDelete = new Set();
|
|
374
|
+
for (const elementId of elementIds) {
|
|
375
|
+
if (!this.doNotUpdateElement(elementId)) {
|
|
376
|
+
idsToDelete.add(elementId);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
core_bentley_1.Logger.logInfo(loggerCategory, `Do not delete target element ${elementId}`);
|
|
288
380
|
}
|
|
289
|
-
|
|
381
|
+
if (idsToDelete.size === 0)
|
|
382
|
+
return;
|
|
383
|
+
await this.onDeleteElements(idsToDelete);
|
|
384
|
+
// Element deletion cascades can remove descendants and their submodels.
|
|
385
|
+
for (const cache of this._entityExistenceCaches)
|
|
386
|
+
cache.clearDb(this.targetDb);
|
|
290
387
|
}
|
|
291
388
|
/** Delete the specified Model from the target iModel.
|
|
292
389
|
* @note A subclass may override this method to customize delete behavior but should call `super.onDeleteModel`.
|
|
293
390
|
*/
|
|
294
391
|
async onDeleteModel(modelId) {
|
|
295
|
-
this.
|
|
392
|
+
this._editTxn.deleteModel(modelId);
|
|
296
393
|
core_bentley_1.Logger.logInfo(loggerCategory, `Deleted model ${modelId}`);
|
|
297
|
-
this.trackProgress();
|
|
394
|
+
await this.trackProgress();
|
|
298
395
|
}
|
|
299
396
|
/** Delete the specified Model from the target iModel. */
|
|
300
397
|
async deleteModel(modelId) {
|
|
301
398
|
await this.onDeleteModel(modelId);
|
|
399
|
+
const modelReference = core_backend_1.EntityReferences.fromEntityType(modelId, core_common_1.ConcreteEntityTypes.Model);
|
|
400
|
+
for (const cache of this._entityExistenceCaches)
|
|
401
|
+
cache.invalidate(this.targetDb, modelReference);
|
|
302
402
|
}
|
|
303
403
|
/** Format an Element for the Logger. */
|
|
304
404
|
formatElementForLogger(elementProps) {
|
|
@@ -310,14 +410,14 @@ class IModelImporter {
|
|
|
310
410
|
return `${elementProps.classFullName} ${namePiece}[${elementProps.id}]`;
|
|
311
411
|
}
|
|
312
412
|
/** Import an ElementUniqueAspect into the target iModel. */
|
|
313
|
-
importElementUniqueAspect(aspectProps) {
|
|
413
|
+
async importElementUniqueAspect(aspectProps) {
|
|
314
414
|
const aspects = this.targetDb.elements.getAspects(aspectProps.element.id, aspectProps.classFullName);
|
|
315
415
|
if (aspects.length === 0) {
|
|
316
416
|
return this.onInsertElementAspect(aspectProps);
|
|
317
417
|
}
|
|
318
418
|
else if (hasEntityChanged(aspects[0], aspectProps)) {
|
|
319
419
|
aspectProps.id = aspects[0].id;
|
|
320
|
-
this.onUpdateElementAspect(aspectProps);
|
|
420
|
+
await this.onUpdateElementAspect(aspectProps);
|
|
321
421
|
}
|
|
322
422
|
return aspects[0].id;
|
|
323
423
|
}
|
|
@@ -327,7 +427,7 @@ class IModelImporter {
|
|
|
327
427
|
* @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.
|
|
328
428
|
* @returns the array of ids of the resulting ElementMultiAspects, in the same order of the aspectPropsArray parameter
|
|
329
429
|
*/
|
|
330
|
-
importElementMultiAspects(aspectPropsArray,
|
|
430
|
+
async importElementMultiAspects(aspectPropsArray,
|
|
331
431
|
/** caller must use this to enforce any aspects added by IModelTransformer are not considered for update */
|
|
332
432
|
filterFunc = () => true) {
|
|
333
433
|
const result = new Array(aspectPropsArray.length).fill(undefined);
|
|
@@ -335,91 +435,96 @@ class IModelImporter {
|
|
|
335
435
|
return result;
|
|
336
436
|
}
|
|
337
437
|
const elementId = aspectPropsArray[0].element.id;
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
438
|
+
const proposedAspectsByClass = new Map();
|
|
439
|
+
aspectPropsArray.forEach((props, index) => {
|
|
440
|
+
const proposedAspects = proposedAspectsByClass.get(props.classFullName) ?? [];
|
|
441
|
+
proposedAspects.push({ props, index });
|
|
442
|
+
proposedAspectsByClass.set(props.classFullName, proposedAspects);
|
|
342
443
|
});
|
|
343
444
|
// Handle ElementMultiAspects in groups by class
|
|
344
|
-
|
|
345
|
-
const proposedAspects = aspectPropsArray
|
|
346
|
-
.map((props, index) => ({ props, index }))
|
|
347
|
-
.filter(({ props }) => aspectClassFullName === props.classFullName);
|
|
445
|
+
for (const [aspectClassFullName, proposedAspects,] of proposedAspectsByClass) {
|
|
348
446
|
const currentAspects = this.targetDb.elements
|
|
349
447
|
.getAspects(elementId, aspectClassFullName)
|
|
350
448
|
.map((props, index) => ({ props, index }))
|
|
351
449
|
.filter(({ props }) => filterFunc(props));
|
|
352
450
|
if (proposedAspects.length >= currentAspects.length) {
|
|
353
|
-
|
|
451
|
+
for (let index = 0; index < proposedAspects.length; index++) {
|
|
452
|
+
const { props, index: resultIndex } = proposedAspects[index];
|
|
354
453
|
let id;
|
|
355
454
|
if (index < currentAspects.length) {
|
|
356
455
|
id = currentAspects[index].props.id;
|
|
357
456
|
props.id = id;
|
|
358
457
|
if (hasEntityChanged(currentAspects[index].props, props)) {
|
|
359
|
-
this.onUpdateElementAspect(props);
|
|
458
|
+
await this.onUpdateElementAspect(props);
|
|
360
459
|
}
|
|
361
460
|
id = props.id;
|
|
362
461
|
}
|
|
363
462
|
else {
|
|
364
|
-
id = this.onInsertElementAspect(props);
|
|
463
|
+
id = await this.onInsertElementAspect(props);
|
|
365
464
|
}
|
|
366
465
|
result[resultIndex] = id;
|
|
367
|
-
}
|
|
466
|
+
}
|
|
368
467
|
}
|
|
369
468
|
else {
|
|
370
|
-
|
|
371
|
-
|
|
469
|
+
for (let index = 0; index < currentAspects.length; index++) {
|
|
470
|
+
const { props } = currentAspects[index];
|
|
372
471
|
if (index < proposedAspects.length) {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
472
|
+
const { props: proposedProps, index: resultIndex } = proposedAspects[index];
|
|
473
|
+
const id = props.id;
|
|
474
|
+
proposedProps.id = id;
|
|
475
|
+
if (hasEntityChanged(props, proposedProps)) {
|
|
476
|
+
await this.onUpdateElementAspect(proposedProps);
|
|
377
477
|
}
|
|
378
478
|
result[resultIndex] = id;
|
|
379
479
|
}
|
|
380
480
|
else {
|
|
381
|
-
this.onDeleteElementAspect(props);
|
|
481
|
+
await this.onDeleteElementAspect(props);
|
|
382
482
|
}
|
|
383
|
-
}
|
|
483
|
+
}
|
|
384
484
|
}
|
|
385
|
-
}
|
|
386
|
-
|
|
485
|
+
}
|
|
486
|
+
(0, node_assert_1.strict)(result.every((r) => r !== undefined));
|
|
387
487
|
return result;
|
|
388
488
|
}
|
|
389
489
|
/** Insert the ElementAspect into the target iModel.
|
|
390
490
|
* @note A subclass may override this method to customize insert behavior but should call `super.onInsertElementAspect`.
|
|
391
491
|
*/
|
|
392
|
-
onInsertElementAspect(aspectProps) {
|
|
492
|
+
async onInsertElementAspect(aspectProps) {
|
|
393
493
|
try {
|
|
394
|
-
const id = this.
|
|
494
|
+
const id = this._editTxn.insertAspect(aspectProps);
|
|
395
495
|
core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatElementAspectForLogger(aspectProps)}`);
|
|
396
|
-
this.trackProgress();
|
|
496
|
+
await this.trackProgress();
|
|
397
497
|
return id;
|
|
398
498
|
}
|
|
399
499
|
catch (error) {
|
|
400
500
|
if (!this.targetDb.containsClass(aspectProps.classFullName)) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
501
|
+
core_bentley_1.ITwinError.throwError({
|
|
502
|
+
iTwinErrorId: {
|
|
503
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
504
|
+
key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
|
|
505
|
+
},
|
|
506
|
+
message: `ElementAspect class "${aspectProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
|
|
507
|
+
cause: error,
|
|
508
|
+
});
|
|
404
509
|
}
|
|
405
|
-
throw error;
|
|
510
|
+
throw error;
|
|
406
511
|
}
|
|
407
512
|
}
|
|
408
513
|
/** Update the ElementAspect within the target iModel.
|
|
409
514
|
* @note A subclass may override this method to customize update behavior but should call `super.onUpdateElementAspect`.
|
|
410
515
|
*/
|
|
411
|
-
onUpdateElementAspect(aspectProps) {
|
|
412
|
-
this.
|
|
516
|
+
async onUpdateElementAspect(aspectProps) {
|
|
517
|
+
this._editTxn.updateAspect(aspectProps);
|
|
413
518
|
core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatElementAspectForLogger(aspectProps)}`);
|
|
414
|
-
this.trackProgress();
|
|
519
|
+
await this.trackProgress();
|
|
415
520
|
}
|
|
416
521
|
/** Delete the specified ElementAspect from the target iModel.
|
|
417
522
|
* @note A subclass may override this method to customize delete behavior but should call `super.onDeleteElementAspect`.
|
|
418
523
|
*/
|
|
419
|
-
onDeleteElementAspect(targetElementAspect) {
|
|
420
|
-
this.
|
|
524
|
+
async onDeleteElementAspect(targetElementAspect) {
|
|
525
|
+
this._editTxn.deleteAspect(targetElementAspect.id);
|
|
421
526
|
core_bentley_1.Logger.logInfo(loggerCategory, `Deleted ${this.formatElementAspectForLogger(targetElementAspect)}`);
|
|
422
|
-
this.trackProgress();
|
|
527
|
+
await this.trackProgress();
|
|
423
528
|
}
|
|
424
529
|
/** Format an ElementAspect for the Logger. */
|
|
425
530
|
formatElementAspectForLogger(elementAspectProps) {
|
|
@@ -428,7 +533,7 @@ class IModelImporter {
|
|
|
428
533
|
/** Import the specified RelationshipProps (either as an insert or an update) into the target iModel.
|
|
429
534
|
* @returns The instance Id of the inserted or updated Relationship.
|
|
430
535
|
*/
|
|
431
|
-
importRelationship(relationshipProps) {
|
|
536
|
+
async importRelationship(relationshipProps) {
|
|
432
537
|
if (undefined === relationshipProps.sourceId ||
|
|
433
538
|
!core_bentley_1.Id64.isValidId64(relationshipProps.sourceId)) {
|
|
434
539
|
core_bentley_1.Logger.logInfo(loggerCategory, `Ignoring ${relationshipProps.classFullName} instance because of invalid RelationshipProps.sourceId`);
|
|
@@ -449,7 +554,7 @@ class IModelImporter {
|
|
|
449
554
|
// if relationship found, update it
|
|
450
555
|
relationshipProps.id = relationship.id;
|
|
451
556
|
if (hasEntityChanged(relationship, relationshipProps)) {
|
|
452
|
-
this.onUpdateRelationship(relationshipProps);
|
|
557
|
+
await this.onUpdateRelationship(relationshipProps);
|
|
453
558
|
}
|
|
454
559
|
return relationshipProps.id;
|
|
455
560
|
}
|
|
@@ -461,62 +566,75 @@ class IModelImporter {
|
|
|
461
566
|
* @returns The instance Id of the newly inserted relationship.
|
|
462
567
|
* @note A subclass may override this method to customize insert behavior but should call `super.onInsertRelationship`.
|
|
463
568
|
*/
|
|
464
|
-
onInsertRelationship(relationshipProps) {
|
|
569
|
+
async onInsertRelationship(relationshipProps) {
|
|
465
570
|
try {
|
|
466
|
-
const targetRelInstanceId = this.
|
|
571
|
+
const targetRelInstanceId = this._editTxn.insertRelationship(relationshipProps);
|
|
467
572
|
core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatRelationshipForLogger(relationshipProps)}`);
|
|
468
|
-
this.trackProgress();
|
|
573
|
+
await this.trackProgress();
|
|
469
574
|
return targetRelInstanceId;
|
|
470
575
|
}
|
|
471
576
|
catch (error) {
|
|
472
577
|
if (!this.targetDb.containsClass(relationshipProps.classFullName)) {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
578
|
+
core_bentley_1.ITwinError.throwError({
|
|
579
|
+
iTwinErrorId: {
|
|
580
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
581
|
+
key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
|
|
582
|
+
},
|
|
583
|
+
message: `Relationship class "${relationshipProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
|
|
584
|
+
cause: error,
|
|
585
|
+
});
|
|
476
586
|
}
|
|
477
|
-
throw error;
|
|
587
|
+
throw error;
|
|
478
588
|
}
|
|
479
589
|
}
|
|
480
590
|
/** Update an existing Relationship in the target iModel from the specified RelationshipProps.
|
|
481
591
|
* @note A subclass may override this method to customize update behavior but should call `super.onUpdateRelationship`.
|
|
482
592
|
*/
|
|
483
|
-
onUpdateRelationship(relationshipProps) {
|
|
593
|
+
async onUpdateRelationship(relationshipProps) {
|
|
484
594
|
if (!relationshipProps.id) {
|
|
485
|
-
|
|
595
|
+
core_bentley_1.ITwinError.throwError({
|
|
596
|
+
iTwinErrorId: {
|
|
597
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
598
|
+
key: IModelTransformerError_1.IModelTransformerError.RelationshipIdRequired,
|
|
599
|
+
},
|
|
600
|
+
message: "Relationship instance Id not provided",
|
|
601
|
+
});
|
|
486
602
|
}
|
|
487
|
-
this.
|
|
603
|
+
this._editTxn.updateRelationship(relationshipProps);
|
|
488
604
|
core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatRelationshipForLogger(relationshipProps)}`);
|
|
489
|
-
this.trackProgress();
|
|
605
|
+
await this.trackProgress();
|
|
490
606
|
}
|
|
491
607
|
/** Delete the specified Relationship from the target iModel. */
|
|
492
|
-
onDeleteRelationship(relationshipProps) {
|
|
608
|
+
async onDeleteRelationship(relationshipProps) {
|
|
493
609
|
// Only passing in what deleteInstance actually uses, full relationshipProps is not necessary.
|
|
494
|
-
this.
|
|
610
|
+
this._editTxn.deleteRelationship({
|
|
495
611
|
id: relationshipProps.id,
|
|
496
612
|
classFullName: relationshipProps.classFullName,
|
|
497
613
|
});
|
|
498
614
|
core_bentley_1.Logger.logInfo(loggerCategory, `Deleted relationship ${relationshipProps.classFullName} id=${relationshipProps.id}`);
|
|
499
|
-
this.trackProgress();
|
|
615
|
+
await this.trackProgress();
|
|
500
616
|
}
|
|
501
617
|
/** Delete the specified Relationship from the target iModel. */
|
|
502
|
-
deleteRelationship(relationshipProps) {
|
|
503
|
-
this.onDeleteRelationship(relationshipProps);
|
|
618
|
+
async deleteRelationship(relationshipProps) {
|
|
619
|
+
await this.onDeleteRelationship(relationshipProps);
|
|
504
620
|
}
|
|
505
621
|
/** Format a Relationship for the Logger. */
|
|
506
622
|
formatRelationshipForLogger(relProps) {
|
|
507
623
|
return `${relProps.classFullName} sourceId=[${relProps.sourceId}] targetId=[${relProps.targetId}]`;
|
|
508
624
|
}
|
|
509
625
|
/** Tracks incremental progress */
|
|
510
|
-
trackProgress() {
|
|
511
|
-
this._progressCounter
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
626
|
+
async trackProgress(increment = 1) {
|
|
627
|
+
const previousProgressCounter = this._progressCounter;
|
|
628
|
+
this._progressCounter += increment;
|
|
629
|
+
const progressCallbacks = Math.floor(this._progressCounter / this.progressInterval) -
|
|
630
|
+
Math.floor(previousProgressCounter / this.progressInterval);
|
|
631
|
+
for (let i = 0; i < progressCallbacks; ++i)
|
|
632
|
+
await this.onProgress();
|
|
515
633
|
}
|
|
516
634
|
/** This method is called when IModelImporter has made incremental progress based on the [[progressInterval]] setting.
|
|
517
635
|
* @note A subclass may override this method to report custom progress but should call `super.onProgress`.
|
|
518
636
|
*/
|
|
519
|
-
onProgress() { }
|
|
637
|
+
async onProgress() { }
|
|
520
638
|
/** Optionally compute the projectExtents for the target iModel depending on the options for this IModelImporter.
|
|
521
639
|
* @note This method is automatically called from [IModelTransformer.process]($transformer).
|
|
522
640
|
* @see [IModelDb.computeProjectExtents]($backend), [[autoExtendProjectExtents]]
|
|
@@ -533,13 +651,15 @@ class IModelImporter {
|
|
|
533
651
|
const excludeOutliers = typeof this.options.autoExtendProjectExtents === "object"
|
|
534
652
|
? this.options.autoExtendProjectExtents.excludeOutliers
|
|
535
653
|
: false;
|
|
536
|
-
// If excludeOutliers is true, use the extents without outliers.
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
654
|
+
// If excludeOutliers is true, use the extents without outliers.
|
|
655
|
+
// Otherwise prefer extentsWithOutliers, but fall back to extents if it's undefined or a null range.
|
|
656
|
+
const extentsWithOutliers = computedProjectExtents.extentsWithOutliers;
|
|
657
|
+
const useExtentsWithOutliers = !excludeOutliers && extentsWithOutliers && !extentsWithOutliers.isNull;
|
|
658
|
+
const newProjectExtents = useExtentsWithOutliers
|
|
659
|
+
? extentsWithOutliers
|
|
660
|
+
: computedProjectExtents.extents;
|
|
541
661
|
if (!newProjectExtents.isAlmostEqual(this.targetDb.projectExtents)) {
|
|
542
|
-
this.
|
|
662
|
+
this._editTxn.updateProjectExtents(newProjectExtents);
|
|
543
663
|
core_bentley_1.Logger.logInfo(loggerCategory, `Updated projectExtents=${JSON.stringify(this.targetDb.projectExtents)}`);
|
|
544
664
|
}
|
|
545
665
|
if (!excludeOutliers &&
|
|
@@ -572,7 +692,7 @@ class IModelImporter {
|
|
|
572
692
|
for (const [elementId, codeValue] of this._duplicateCodeValueMap) {
|
|
573
693
|
const element = this.targetDb.elements.getElement(elementId);
|
|
574
694
|
element.code.value = codeValue;
|
|
575
|
-
element.
|
|
695
|
+
this._editTxn.updateElement(element.toJSON());
|
|
576
696
|
}
|
|
577
697
|
this._duplicateCodeValueMap.clear();
|
|
578
698
|
}
|
|
@@ -580,7 +700,8 @@ class IModelImporter {
|
|
|
580
700
|
* Needs to be called to perform necessary cleanup operations.
|
|
581
701
|
* By not calling `finalize` there is a risk that data imported into targetDb will not be as expected.
|
|
582
702
|
*
|
|
583
|
-
* @note
|
|
703
|
+
* @note [[IModelTransformer.process]] invokes this method automatically. Call it explicitly after
|
|
704
|
+
* composing the transformer's lower-level subset processing methods.
|
|
584
705
|
*/
|
|
585
706
|
finalize() {
|
|
586
707
|
this.resolveDuplicateCodeValues();
|
|
@@ -595,7 +716,11 @@ exports.IModelImporter = IModelImporter;
|
|
|
595
716
|
*/
|
|
596
717
|
function hasEntityChanged(entity, entityProps, namesToIgnore) {
|
|
597
718
|
let changed = false;
|
|
598
|
-
entity.
|
|
719
|
+
const entityClass = entity.iModel.schemaContext.getSchemaItemSync(entity.schemaItemKey);
|
|
720
|
+
(0, node_assert_1.strict)(ecschema_metadata_1.EntityClass.isEntityClass(entityClass) ||
|
|
721
|
+
ecschema_metadata_1.RelationshipClass.isRelationshipClass(entityClass), `Cannot get metadata for ${entity.classFullName}.`);
|
|
722
|
+
for (const property of entityClass.getPropertiesSync()) {
|
|
723
|
+
const propertyName = property.name;
|
|
599
724
|
if (!changed) {
|
|
600
725
|
if (namesToIgnore && namesToIgnore.has(propertyName)) {
|
|
601
726
|
// skip
|
|
@@ -610,7 +735,7 @@ function hasEntityChanged(entity, entityProps, namesToIgnore) {
|
|
|
610
735
|
changed = hasValueChanged(entity.asAny[core_common_1.ECJsNames.toJsName(propertyName)], entityProps[core_common_1.ECJsNames.toJsName(propertyName)]);
|
|
611
736
|
}
|
|
612
737
|
}
|
|
613
|
-
}
|
|
738
|
+
}
|
|
614
739
|
return changed;
|
|
615
740
|
}
|
|
616
741
|
/** Returns true if the specified binary values are different. */
|
|
@@ -640,9 +765,21 @@ function isDefaultSubCategory(props) {
|
|
|
640
765
|
if (props.id === undefined)
|
|
641
766
|
return false;
|
|
642
767
|
if (!core_bentley_1.Id64.isId64(props.id))
|
|
643
|
-
|
|
768
|
+
core_bentley_1.ITwinError.throwError({
|
|
769
|
+
iTwinErrorId: {
|
|
770
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
771
|
+
key: IModelTransformerError_1.IModelTransformerError.InvalidSubCategory,
|
|
772
|
+
},
|
|
773
|
+
message: "subcategory had invalid id",
|
|
774
|
+
});
|
|
644
775
|
if (props.parent?.id === undefined)
|
|
645
|
-
|
|
776
|
+
core_bentley_1.ITwinError.throwError({
|
|
777
|
+
iTwinErrorId: {
|
|
778
|
+
scope: IModelTransformerError_1.IModelTransformerErrorScope,
|
|
779
|
+
key: IModelTransformerError_1.IModelTransformerError.InvalidSubCategory,
|
|
780
|
+
},
|
|
781
|
+
message: `subcategory with id ${props.id} had no parent`,
|
|
782
|
+
});
|
|
646
783
|
return props.id === core_backend_1.IModelDb.getDefaultSubCategoryId(props.parent.id);
|
|
647
784
|
}
|
|
648
785
|
//# sourceMappingURL=IModelImporter.js.map
|