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