@itwin/imodel-transformer 2.0.0-dev.1 → 2.0.0-dev.10
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/CHANGELOG.md +9 -1
- package/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
- package/lib/cjs/BranchProvenanceInitializer.js +11 -13
- package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.js +5 -5
- package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.d.ts +3 -0
- package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.js +103 -40
- package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
- package/lib/cjs/ElementCascadingDeleter.js +1 -1
- package/lib/cjs/ElementCascadingDeleter.js.map +1 -1
- package/lib/cjs/EntityUnifier.d.ts +1 -1
- package/lib/cjs/EntityUnifier.d.ts.map +1 -1
- package/lib/cjs/EntityUnifier.js +5 -3
- package/lib/cjs/EntityUnifier.js.map +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.d.ts +2 -2
- package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/ExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +2 -2
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +1 -1
- package/lib/cjs/IModelCloneContext.d.ts +3 -3
- package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
- package/lib/cjs/IModelCloneContext.js +58 -52
- package/lib/cjs/IModelCloneContext.js.map +1 -1
- package/lib/cjs/IModelExporter.d.ts +16 -7
- package/lib/cjs/IModelExporter.d.ts.map +1 -1
- package/lib/cjs/IModelExporter.js +105 -96
- package/lib/cjs/IModelExporter.js.map +1 -1
- package/lib/cjs/IModelImporter.d.ts +2 -2
- package/lib/cjs/IModelImporter.d.ts.map +1 -1
- package/lib/cjs/IModelImporter.js +16 -15
- package/lib/cjs/IModelImporter.js.map +1 -1
- package/lib/cjs/IModelTransformer.d.ts +54 -45
- package/lib/cjs/IModelTransformer.d.ts.map +1 -1
- package/lib/cjs/IModelTransformer.js +445 -431
- package/lib/cjs/IModelTransformer.js.map +1 -1
- package/lib/cjs/TransformerLoggerCategory.d.ts +6 -5
- package/lib/cjs/TransformerLoggerCategory.d.ts.map +1 -1
- package/lib/cjs/TransformerLoggerCategory.js +6 -5
- package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
- package/lib/cjs/imodel-transformer.js +2 -2
- package/lib/cjs/imodel-transformer.js.map +1 -1
- package/package.json +33 -33
|
@@ -77,6 +77,15 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
77
77
|
targetDb;
|
|
78
78
|
/** The IModelTransformContext for this IModelTransformer. */
|
|
79
79
|
context;
|
|
80
|
+
/** The transform to be applied to the placement of spatial elements
|
|
81
|
+
* This transform should be applied when:
|
|
82
|
+
* - source and target db have different ECEF locations
|
|
83
|
+
* - source and target db have matching GCS/CRS data, but differing `geographicCoordinateSystem.additionalTransform.helmert2DWithZOffset`
|
|
84
|
+
* @note for ECEF transforms, this can only be used when source and target are linearly located imodels
|
|
85
|
+
* @note for non linearly located imodels, this transform will be a linear transform derived from Helmert Transforms from the src and target iModels.
|
|
86
|
+
* @beta
|
|
87
|
+
*/
|
|
88
|
+
_linearSpatialTransform;
|
|
80
89
|
_syncType;
|
|
81
90
|
/** The Id of the Element in the **target** iModel that represents the **source** repository as a whole and scopes its [ExternalSourceAspect]($backend) instances. */
|
|
82
91
|
get targetScopeElementId() {
|
|
@@ -101,7 +110,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
101
110
|
* @param dbToQuery db to run the query on for scope external source
|
|
102
111
|
* @param aspectProps aspectProps to search for @see ExternalSourceAspectProps
|
|
103
112
|
*/
|
|
104
|
-
static queryScopeExternalSourceAspect(dbToQuery, aspectProps) {
|
|
113
|
+
static async queryScopeExternalSourceAspect(dbToQuery, aspectProps) {
|
|
105
114
|
const sql = `
|
|
106
115
|
SELECT ECInstanceId, Version, JsonProperties
|
|
107
116
|
FROM ${core_backend_1.ExternalSourceAspect.classFullName}
|
|
@@ -111,7 +120,21 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
111
120
|
AND Identifier=:identifier
|
|
112
121
|
LIMIT 1
|
|
113
122
|
`;
|
|
114
|
-
//
|
|
123
|
+
// if (aspectProps.scope === undefined) return undefined;
|
|
124
|
+
// const params = new QueryBinder();
|
|
125
|
+
// params.bindId("elementId", aspectProps.element.id);
|
|
126
|
+
// params.bindId("scopeId", aspectProps.scope.id);
|
|
127
|
+
// params.bindString("kind", aspectProps.kind);
|
|
128
|
+
// params.bindString("identifier", aspectProps.identifier);
|
|
129
|
+
// const reader = dbToQuery.createQueryReader(sql, params, {usePrimaryConn: true});
|
|
130
|
+
// if (await reader.step()) {
|
|
131
|
+
// const aspectId = reader.current.id;
|
|
132
|
+
// const version = reader.current.version as string | undefined;
|
|
133
|
+
// const jsonProperties = reader.current.jsonProperties as string | undefined;
|
|
134
|
+
// return { aspectId, version, jsonProperties };
|
|
135
|
+
// }
|
|
136
|
+
// return undefined;
|
|
137
|
+
// eslint-disable-next-line @itwin/no-internal, @typescript-eslint/no-deprecated
|
|
115
138
|
return dbToQuery.withPreparedStatement(sql, (statement) => {
|
|
116
139
|
statement.bindId("elementId", aspectProps.element.id);
|
|
117
140
|
if (aspectProps.scope === undefined)
|
|
@@ -140,7 +163,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
140
163
|
* @throws if no scoping ESA can be found in either the sourceDb or targetDb which describes a master branch relationship between the two databases.
|
|
141
164
|
* @returns "forward" or "reverse"
|
|
142
165
|
*/
|
|
143
|
-
static determineSyncType(sourceDb, targetDb,
|
|
166
|
+
static async determineSyncType(sourceDb, targetDb,
|
|
144
167
|
/** @see [[IModelTransformOptions.targetScopeElementId]] */
|
|
145
168
|
targetScopeElementId) {
|
|
146
169
|
const aspectProps = {
|
|
@@ -157,19 +180,19 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
157
180
|
jsonProperties: undefined,
|
|
158
181
|
};
|
|
159
182
|
/** First check if the targetDb is the branch (branch is the @see provenanceDb) */
|
|
160
|
-
const esaPropsFromTargetDb = this.queryScopeExternalSourceAspect(targetDb, aspectProps);
|
|
183
|
+
const esaPropsFromTargetDb = await this.queryScopeExternalSourceAspect(targetDb, aspectProps);
|
|
161
184
|
if (esaPropsFromTargetDb !== undefined) {
|
|
162
185
|
return "forward"; // we found an esa assuming targetDb is the provenanceDb/branch so this is a forward sync.
|
|
163
186
|
}
|
|
164
187
|
// Now check if the sourceDb is the branch
|
|
165
188
|
aspectProps.identifier = targetDb.iModelId;
|
|
166
|
-
const esaPropsFromSourceDb = this.queryScopeExternalSourceAspect(sourceDb, aspectProps);
|
|
189
|
+
const esaPropsFromSourceDb = await this.queryScopeExternalSourceAspect(sourceDb, aspectProps);
|
|
167
190
|
if (esaPropsFromSourceDb !== undefined) {
|
|
168
191
|
return "reverse"; // we found an esa assuming sourceDb is the provenanceDb/branch so this is a reverse sync.
|
|
169
192
|
}
|
|
170
193
|
throw new Error(this.noEsaSyncDirectionErrorMessage);
|
|
171
194
|
}
|
|
172
|
-
determineSyncType() {
|
|
195
|
+
async determineSyncType() {
|
|
173
196
|
if (this._isProvenanceInitTransform) {
|
|
174
197
|
return "forward";
|
|
175
198
|
}
|
|
@@ -177,7 +200,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
177
200
|
return "not-sync";
|
|
178
201
|
}
|
|
179
202
|
try {
|
|
180
|
-
return IModelTransformer.determineSyncType(this.sourceDb, this.targetDb, this.targetScopeElementId);
|
|
203
|
+
return await IModelTransformer.determineSyncType(this.sourceDb, this.targetDb, this.targetScopeElementId);
|
|
181
204
|
}
|
|
182
205
|
catch (err) {
|
|
183
206
|
if (err instanceof Error &&
|
|
@@ -188,14 +211,14 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
188
211
|
throw err;
|
|
189
212
|
}
|
|
190
213
|
}
|
|
191
|
-
|
|
214
|
+
async getIsReverseSynchronization() {
|
|
192
215
|
if (this._syncType === undefined)
|
|
193
|
-
this._syncType = this.determineSyncType();
|
|
216
|
+
this._syncType = await this.determineSyncType();
|
|
194
217
|
return this._syncType === "reverse";
|
|
195
218
|
}
|
|
196
|
-
|
|
219
|
+
async getIsForwardSynchronization() {
|
|
197
220
|
if (this._syncType === undefined)
|
|
198
|
-
this._syncType = this.determineSyncType();
|
|
221
|
+
this._syncType = await this.determineSyncType();
|
|
199
222
|
return this._syncType === "forward";
|
|
200
223
|
}
|
|
201
224
|
_changesetRanges = undefined;
|
|
@@ -230,10 +253,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
230
253
|
// non-falsy defaults
|
|
231
254
|
cloneUsingBinaryGeometry: options?.cloneUsingBinaryGeometry ?? true,
|
|
232
255
|
targetScopeElementId: options?.targetScopeElementId ?? core_common_1.IModel.rootSubjectId,
|
|
233
|
-
// eslint-disable-next-line deprecation/deprecation
|
|
234
256
|
danglingReferencesBehavior: options?.danglingReferencesBehavior ?? "reject",
|
|
235
257
|
branchRelationshipDataBehavior: options?.branchRelationshipDataBehavior ?? "reject",
|
|
236
258
|
skipPropagateChangesToRootElements: options?.skipPropagateChangesToRootElements ?? true,
|
|
259
|
+
tryAlignGeolocation: options?.tryAlignGeolocation ?? false,
|
|
237
260
|
};
|
|
238
261
|
// check if authorization client is defined
|
|
239
262
|
if (core_backend_1.IModelHost.authorizationClient === undefined) {
|
|
@@ -289,6 +312,20 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
289
312
|
this.targetDb.codeValueBehavior = "exact";
|
|
290
313
|
}
|
|
291
314
|
/* eslint-enable @itwin/no-internal */
|
|
315
|
+
if (this._options.tryAlignGeolocation) {
|
|
316
|
+
if (this.sourceDb.geographicCoordinateSystem ||
|
|
317
|
+
this.targetDb.geographicCoordinateSystem) {
|
|
318
|
+
core_bentley_1.Logger.logTrace(loggerCategory, "Aligning Additional transforms between imodels due to imodels containing GeographicCoordinateSystem data");
|
|
319
|
+
this._linearSpatialTransform =
|
|
320
|
+
this.calculateTransformFromHelmertTransforms();
|
|
321
|
+
}
|
|
322
|
+
else if (this.sourceDb.ecefLocation && this.targetDb.ecefLocation) {
|
|
323
|
+
core_bentley_1.Logger.logTrace(loggerCategory, "Aligning ECEF Location's between imodels due to imodels not containing GeographicCoordinateSystem data");
|
|
324
|
+
this._linearSpatialTransform = this.calculateEcefTransform();
|
|
325
|
+
}
|
|
326
|
+
else
|
|
327
|
+
core_bentley_1.Logger.logTrace(loggerCategory, "No Geolcation data to align, both GCS and ECEF are undefined");
|
|
328
|
+
}
|
|
292
329
|
}
|
|
293
330
|
/** validates that the importer set on the transformer has the same values for its shared options as the transformer.
|
|
294
331
|
* @note This expects that the importer is already set on the transformer.
|
|
@@ -308,7 +345,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
308
345
|
/** Dispose any native resources associated with this IModelTransformer. */
|
|
309
346
|
dispose() {
|
|
310
347
|
core_bentley_1.Logger.logTrace(loggerCategory, "dispose()");
|
|
311
|
-
this.context.dispose();
|
|
348
|
+
this.context[Symbol.dispose]();
|
|
312
349
|
}
|
|
313
350
|
/** Log current settings that affect IModelTransformer's behavior. */
|
|
314
351
|
logSettings() {
|
|
@@ -328,14 +365,18 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
328
365
|
/** Return the IModelDb where IModelTransformer will store its provenance.
|
|
329
366
|
* @note This will be [[targetDb]] except when it is a reverse synchronization. In that case it be [[sourceDb]].
|
|
330
367
|
*/
|
|
331
|
-
|
|
332
|
-
return
|
|
368
|
+
async getProvenanceDb() {
|
|
369
|
+
return (await this.getIsReverseSynchronization())
|
|
370
|
+
? this.sourceDb
|
|
371
|
+
: this.targetDb;
|
|
333
372
|
}
|
|
334
373
|
/** Return the IModelDb where IModelTransformer looks for entities referred to by stored provenance.
|
|
335
374
|
* @note This will be [[sourceDb]] except when it is a reverse synchronization. In that case it be [[targetDb]].
|
|
336
375
|
*/
|
|
337
|
-
|
|
338
|
-
return
|
|
376
|
+
async getProvenanceSourceDb() {
|
|
377
|
+
return (await this.getIsReverseSynchronization())
|
|
378
|
+
? this.targetDb
|
|
379
|
+
: this.sourceDb;
|
|
339
380
|
}
|
|
340
381
|
/** Create an ExternalSourceAspectProps in a standard way for an Element in an iModel --> iModel transformation. */
|
|
341
382
|
static initElementProvenanceOptions(sourceElementId, targetElementId, args) {
|
|
@@ -361,7 +402,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
361
402
|
};
|
|
362
403
|
return aspectProps;
|
|
363
404
|
}
|
|
364
|
-
static initRelationshipProvenanceOptions(sourceRelInstanceId, targetRelInstanceId, args) {
|
|
405
|
+
static async initRelationshipProvenanceOptions(sourceRelInstanceId, targetRelInstanceId, args) {
|
|
365
406
|
const provenanceDb = args.isReverseSynchronization
|
|
366
407
|
? args.sourceDb
|
|
367
408
|
: args.targetDb;
|
|
@@ -371,12 +412,13 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
371
412
|
const provenanceRelInstanceId = args.isReverseSynchronization
|
|
372
413
|
? sourceRelInstanceId
|
|
373
414
|
: targetRelInstanceId;
|
|
374
|
-
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
return stmt.getValue(0).getId();
|
|
415
|
+
const sql = "SELECT SourceECInstanceId FROM bis.ElementRefersToElements WHERE ECInstanceId=?";
|
|
416
|
+
const params = new core_common_1.QueryBinder().bindId(1, provenanceRelInstanceId);
|
|
417
|
+
const reader = provenanceDb.createQueryReader(sql, params, {
|
|
418
|
+
usePrimaryConn: true,
|
|
379
419
|
});
|
|
420
|
+
nodeAssert(await reader.step(), "relationship provenance query returned no rows");
|
|
421
|
+
const elementId = reader.current[0];
|
|
380
422
|
const jsonProperties = args.forceOldRelationshipProvenanceMethod
|
|
381
423
|
? { targetRelInstanceId }
|
|
382
424
|
: { provenanceRelInstanceId };
|
|
@@ -401,9 +443,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
401
443
|
*/
|
|
402
444
|
_forceOldRelationshipProvenanceMethod = false;
|
|
403
445
|
/** Create an ExternalSourceAspectProps in a standard way for an Element in an iModel --> iModel transformation. */
|
|
404
|
-
initElementProvenance(sourceElementId, targetElementId) {
|
|
446
|
+
async initElementProvenance(sourceElementId, targetElementId) {
|
|
405
447
|
return IModelTransformer.initElementProvenanceOptions(sourceElementId, targetElementId, {
|
|
406
|
-
isReverseSynchronization: this.
|
|
448
|
+
isReverseSynchronization: await this.getIsReverseSynchronization(),
|
|
407
449
|
targetScopeElementId: this.targetScopeElementId,
|
|
408
450
|
sourceDb: this.sourceDb,
|
|
409
451
|
targetDb: this.targetDb,
|
|
@@ -414,11 +456,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
414
456
|
* The `identifier` property of the ExternalSourceAspect will be the ECInstanceId of the relationship in the master iModel.
|
|
415
457
|
* The ECInstanceId of the relationship in the branch iModel will be stored in the JsonProperties of the ExternalSourceAspect.
|
|
416
458
|
*/
|
|
417
|
-
initRelationshipProvenance(sourceRelationship, targetRelInstanceId) {
|
|
459
|
+
async initRelationshipProvenance(sourceRelationship, targetRelInstanceId) {
|
|
418
460
|
return IModelTransformer.initRelationshipProvenanceOptions(sourceRelationship.id, targetRelInstanceId, {
|
|
419
461
|
sourceDb: this.sourceDb,
|
|
420
462
|
targetDb: this.targetDb,
|
|
421
|
-
isReverseSynchronization: this.
|
|
463
|
+
isReverseSynchronization: await this.getIsReverseSynchronization(),
|
|
422
464
|
targetScopeElementId: this.targetScopeElementId,
|
|
423
465
|
forceOldRelationshipProvenanceMethod: this._forceOldRelationshipProvenanceMethod,
|
|
424
466
|
});
|
|
@@ -446,13 +488,13 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
446
488
|
* @note empty string and -1 for changeset and index if it was transformed before federation guid update (pre 1.x) and @see [[IModelTransformOptions.branchRelationshipDataBehavior]] === "unsafe-migrate".
|
|
447
489
|
* @throws if the version is not found in a preexisting scope aspect and @see [[IModelTransformOptions.branchRelationshipDataBehavior]] !== "unsafe-migrate"
|
|
448
490
|
*/
|
|
449
|
-
|
|
491
|
+
async getSynchronizationVersion() {
|
|
450
492
|
if (this._cachedSynchronizationVersion === undefined) {
|
|
451
|
-
const provenanceScopeAspect = this.tryGetProvenanceScopeAspect();
|
|
493
|
+
const provenanceScopeAspect = await this.tryGetProvenanceScopeAspect();
|
|
452
494
|
if (!provenanceScopeAspect) {
|
|
453
495
|
return { index: -1, id: "" }; // first synchronization.
|
|
454
496
|
}
|
|
455
|
-
const version = this.
|
|
497
|
+
const version = (await this.getIsReverseSynchronization())
|
|
456
498
|
? JSON.parse(provenanceScopeAspect.jsonProperties ?? "{}").reverseSyncVersion
|
|
457
499
|
: provenanceScopeAspect.version;
|
|
458
500
|
if (!version &&
|
|
@@ -474,17 +516,17 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
474
516
|
* @returns provenance scope aspect if it exists in the provenanceDb.
|
|
475
517
|
* Provenance scope aspect is created and inserted into provenanceDb when [[initScopeProvenance]] is invoked.
|
|
476
518
|
*/
|
|
477
|
-
tryGetProvenanceScopeAspect() {
|
|
478
|
-
const scopeProvenanceAspectProps = IModelTransformer.queryScopeExternalSourceAspect(this.
|
|
519
|
+
async tryGetProvenanceScopeAspect() {
|
|
520
|
+
const scopeProvenanceAspectProps = await IModelTransformer.queryScopeExternalSourceAspect(await this.getProvenanceDb(), {
|
|
479
521
|
id: undefined,
|
|
480
522
|
classFullName: core_backend_1.ExternalSourceAspect.classFullName,
|
|
481
523
|
scope: { id: core_common_1.IModel.rootSubjectId },
|
|
482
524
|
kind: core_backend_1.ExternalSourceAspect.Kind.Scope,
|
|
483
525
|
element: { id: this.targetScopeElementId ?? core_common_1.IModel.rootSubjectId },
|
|
484
|
-
identifier: this.
|
|
526
|
+
identifier: (await this.getProvenanceSourceDb()).iModelId,
|
|
485
527
|
});
|
|
486
528
|
return scopeProvenanceAspectProps !== undefined
|
|
487
|
-
? this.
|
|
529
|
+
? (await this.getProvenanceDb()).elements.getAspect(scopeProvenanceAspectProps.aspectId)
|
|
488
530
|
: undefined;
|
|
489
531
|
}
|
|
490
532
|
/**
|
|
@@ -493,7 +535,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
493
535
|
* @returns the last synced version (changesetId) on the target scope's external source aspect,
|
|
494
536
|
* if this was a [BriefcaseDb]($backend)
|
|
495
537
|
*/
|
|
496
|
-
initScopeProvenance() {
|
|
538
|
+
async initScopeProvenance() {
|
|
539
|
+
const provenanceDb = await this.getProvenanceDb();
|
|
540
|
+
const sourceProvenanceDb = await this.getProvenanceSourceDb();
|
|
497
541
|
const aspectProps = {
|
|
498
542
|
id: undefined,
|
|
499
543
|
version: undefined,
|
|
@@ -503,11 +547,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
503
547
|
relClassName: core_backend_1.ElementOwnsExternalSourceAspects.classFullName,
|
|
504
548
|
},
|
|
505
549
|
scope: { id: core_common_1.IModel.rootSubjectId }, // the root Subject scopes scope elements
|
|
506
|
-
identifier:
|
|
550
|
+
identifier: sourceProvenanceDb.iModelId,
|
|
507
551
|
kind: core_backend_1.ExternalSourceAspect.Kind.Scope,
|
|
508
552
|
jsonProperties: undefined,
|
|
509
553
|
};
|
|
510
|
-
const foundEsaProps = IModelTransformer.queryScopeExternalSourceAspect(
|
|
554
|
+
const foundEsaProps = await IModelTransformer.queryScopeExternalSourceAspect(provenanceDb, aspectProps); // this query includes "identifier"
|
|
511
555
|
if (foundEsaProps === undefined) {
|
|
512
556
|
aspectProps.version = ""; // empty since never before transformed. Will be updated in [[finalizeTransformation]]
|
|
513
557
|
aspectProps.jsonProperties = {
|
|
@@ -524,20 +568,19 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
524
568
|
AND Kind=:kind
|
|
525
569
|
LIMIT 1
|
|
526
570
|
`;
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
//
|
|
530
|
-
(
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
statement.bindString("kind", aspectProps.kind);
|
|
534
|
-
return core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step();
|
|
571
|
+
const params = new core_common_1.QueryBinder();
|
|
572
|
+
params.bindId("elementId", aspectProps.element.id);
|
|
573
|
+
params.bindId("scopeId", aspectProps.scope.id); // this scope.id can never be invalid, we create it above
|
|
574
|
+
params.bindString("kind", aspectProps.kind);
|
|
575
|
+
const reader = provenanceDb.createQueryReader(sql, params, {
|
|
576
|
+
usePrimaryConn: true,
|
|
535
577
|
});
|
|
578
|
+
const hasConflictingScope = await reader.step();
|
|
536
579
|
if (hasConflictingScope) {
|
|
537
580
|
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, "Provenance scope conflict");
|
|
538
581
|
}
|
|
539
582
|
if (!this._options.noProvenance) {
|
|
540
|
-
const id =
|
|
583
|
+
const id = provenanceDb.elements.insertAspect({
|
|
541
584
|
...aspectProps,
|
|
542
585
|
jsonProperties: JSON.stringify(aspectProps.jsonProperties),
|
|
543
586
|
});
|
|
@@ -557,7 +600,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
557
600
|
const oldProps = JSON.parse(JSON.stringify(aspectProps));
|
|
558
601
|
if (this.handleUnsafeMigrate(aspectProps)) {
|
|
559
602
|
core_bentley_1.Logger.logInfo(loggerCategory, "Unsafe migrate made a change to the target scope's external source aspect. Updating aspect in database.", { oldProps, newProps: aspectProps });
|
|
560
|
-
|
|
603
|
+
provenanceDb.elements.updateAspect({
|
|
561
604
|
...aspectProps,
|
|
562
605
|
jsonProperties: JSON.stringify(aspectProps.jsonProperties),
|
|
563
606
|
});
|
|
@@ -624,7 +667,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
624
667
|
* @note provenance is done by federation guids where possible
|
|
625
668
|
* @note this may execute on each element more than once! Only use in cases where that is handled
|
|
626
669
|
*/
|
|
627
|
-
static forEachTrackedElement(args) {
|
|
670
|
+
static async forEachTrackedElement(args) {
|
|
628
671
|
if (args.provenanceDb === args.provenanceSourceDb)
|
|
629
672
|
return;
|
|
630
673
|
if (!args.provenanceDb.containsClass(core_backend_1.ExternalSourceAspect.classFullName)) {
|
|
@@ -649,42 +692,28 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
649
692
|
// NOTE: if we exposed the native attach database support,
|
|
650
693
|
// we could get the intersection of fed guids in one query, not sure if it would be faster
|
|
651
694
|
// OR we could do a raw sqlite query...
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
if (
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
while (true) {
|
|
665
|
-
const currSourceRow = sourceRow, currTargetRow = targetRow;
|
|
666
|
-
if (currSourceRow.federationGuid !== undefined &&
|
|
667
|
-
currTargetRow.federationGuid !== undefined &&
|
|
668
|
-
currSourceRow.federationGuid === currTargetRow.federationGuid) {
|
|
669
|
-
// data flow direction is always sourceDb -> targetDb and it does not depend on where the explicit element provenance is stored
|
|
670
|
-
args.fn(sourceRow.id, targetRow.id);
|
|
671
|
-
}
|
|
672
|
-
if (currTargetRow.federationGuid === undefined ||
|
|
673
|
-
(currSourceRow.federationGuid !== undefined &&
|
|
674
|
-
currSourceRow.federationGuid >= currTargetRow.federationGuid)) {
|
|
675
|
-
if (targetStmt.step() !== core_bentley_1.DbResult.BE_SQLITE_ROW)
|
|
676
|
-
return;
|
|
677
|
-
targetRow = targetStmt.getRow();
|
|
678
|
-
}
|
|
679
|
-
if (currSourceRow.federationGuid === undefined ||
|
|
680
|
-
(currTargetRow.federationGuid !== undefined &&
|
|
681
|
-
currSourceRow.federationGuid <= currTargetRow.federationGuid)) {
|
|
682
|
-
if (sourceStmt.step() !== core_bentley_1.DbResult.BE_SQLITE_ROW)
|
|
683
|
-
return;
|
|
684
|
-
sourceRow = sourceStmt.getRow();
|
|
685
|
-
}
|
|
695
|
+
const sourceReader = sourceDb.createQueryReader(elementIdByFedGuidQuery, undefined, { usePrimaryConn: true });
|
|
696
|
+
const targetReader = targetDb.createQueryReader(elementIdByFedGuidQuery, undefined, { usePrimaryConn: true });
|
|
697
|
+
let hasSourceRow = await sourceReader.step();
|
|
698
|
+
let hasTargetRow = await targetReader.step();
|
|
699
|
+
while (hasSourceRow && hasTargetRow) {
|
|
700
|
+
const sourceFedGuid = sourceReader.current.federationGuid;
|
|
701
|
+
const targetFedGuid = targetReader.current.federationGuid;
|
|
702
|
+
if (sourceFedGuid !== undefined &&
|
|
703
|
+
targetFedGuid !== undefined &&
|
|
704
|
+
sourceFedGuid === targetFedGuid) {
|
|
705
|
+
// data flow direction is always sourceDb -> targetDb and it does not depend on where the explicit element provenance is stored
|
|
706
|
+
args.fn(sourceReader.current.id, targetReader.current.id);
|
|
686
707
|
}
|
|
687
|
-
|
|
708
|
+
if (targetFedGuid === undefined ||
|
|
709
|
+
(sourceFedGuid !== undefined && sourceFedGuid >= targetFedGuid)) {
|
|
710
|
+
hasTargetRow = await targetReader.step();
|
|
711
|
+
}
|
|
712
|
+
if (sourceFedGuid === undefined ||
|
|
713
|
+
(targetFedGuid !== undefined && sourceFedGuid <= targetFedGuid)) {
|
|
714
|
+
hasSourceRow = await sourceReader.step();
|
|
715
|
+
}
|
|
716
|
+
}
|
|
688
717
|
// query for provenanceDb
|
|
689
718
|
const provenanceAspectsQuery = `
|
|
690
719
|
SELECT esa.Identifier, Element.Id
|
|
@@ -695,27 +724,26 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
695
724
|
// Technically this will a second time call the function (as documented) on
|
|
696
725
|
// victims of the old provenance method that have both fedguids and an inserted aspect.
|
|
697
726
|
// But this is a private function with one known caller where that doesn't matter
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
forEachTrackedElement(fn) {
|
|
727
|
+
const runFnInDataFlowDirection = (sourceId, targetId) => args.isReverseSynchronization
|
|
728
|
+
? args.fn(sourceId, targetId)
|
|
729
|
+
: args.fn(targetId, sourceId);
|
|
730
|
+
const params = new core_common_1.QueryBinder();
|
|
731
|
+
params.bindId("scopeId", args.targetScopeElementId);
|
|
732
|
+
params.bindString("kind", core_backend_1.ExternalSourceAspect.Kind.Element);
|
|
733
|
+
const provenanceReader = args.provenanceDb.createQueryReader(provenanceAspectsQuery, params, { usePrimaryConn: true });
|
|
734
|
+
for await (const row of provenanceReader) {
|
|
735
|
+
// ExternalSourceAspect.Identifier is of type string
|
|
736
|
+
const aspectIdentifier = row[0];
|
|
737
|
+
const elementId = row.id;
|
|
738
|
+
runFnInDataFlowDirection(elementId, aspectIdentifier);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
async forEachTrackedElement(fn) {
|
|
714
742
|
return IModelTransformer.forEachTrackedElement({
|
|
715
|
-
provenanceSourceDb: this.
|
|
716
|
-
provenanceDb: this.
|
|
743
|
+
provenanceSourceDb: await this.getProvenanceSourceDb(),
|
|
744
|
+
provenanceDb: await this.getProvenanceDb(),
|
|
717
745
|
targetScopeElementId: this.targetScopeElementId,
|
|
718
|
-
isReverseSynchronization: this.
|
|
746
|
+
isReverseSynchronization: await this.getIsReverseSynchronization(),
|
|
719
747
|
fn,
|
|
720
748
|
skipPropagateChangesToRootElements: this._options.skipPropagateChangesToRootElements ?? true,
|
|
721
749
|
});
|
|
@@ -727,23 +755,26 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
727
755
|
* @param entityInProvenanceSourceId
|
|
728
756
|
* @returns the elementId that the ESA is stored on, esa.Element.Id
|
|
729
757
|
*/
|
|
730
|
-
_queryProvenanceForElement(entityInProvenanceSourceId) {
|
|
731
|
-
|
|
732
|
-
return this.provenanceDb.withPreparedStatement(`
|
|
758
|
+
async _queryProvenanceForElement(entityInProvenanceSourceId) {
|
|
759
|
+
const sql = `
|
|
733
760
|
SELECT esa.Element.Id
|
|
734
761
|
FROM Bis.ExternalSourceAspect esa
|
|
735
762
|
WHERE esa.Kind=?
|
|
736
763
|
AND esa.Scope.Id=?
|
|
737
764
|
AND esa.Identifier=?
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
return undefined;
|
|
765
|
+
`;
|
|
766
|
+
const params = new core_common_1.QueryBinder();
|
|
767
|
+
params.bindString(1, core_backend_1.ExternalSourceAspect.Kind.Element);
|
|
768
|
+
params.bindId(2, this.targetScopeElementId);
|
|
769
|
+
params.bindString(3, entityInProvenanceSourceId);
|
|
770
|
+
const result = (await this.getProvenanceDb()).createQueryReader(sql, params, {
|
|
771
|
+
usePrimaryConn: true,
|
|
746
772
|
});
|
|
773
|
+
if (await result.step()) {
|
|
774
|
+
return result.current.id;
|
|
775
|
+
}
|
|
776
|
+
else
|
|
777
|
+
return undefined;
|
|
747
778
|
}
|
|
748
779
|
/**
|
|
749
780
|
* Queries the provenanceDb for an ESA whose identifier is equal to the provided 'entityInProvenanceSourceId'.
|
|
@@ -752,35 +783,38 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
752
783
|
* @param entityInProvenanceSourceId
|
|
753
784
|
* @returns
|
|
754
785
|
*/
|
|
755
|
-
_queryProvenanceForRelationship(entityInProvenanceSourceId, sourceRelInfo) {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
const
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
|
|
786
|
+
async _queryProvenanceForRelationship(entityInProvenanceSourceId, sourceRelInfo) {
|
|
787
|
+
const sql = `
|
|
788
|
+
SELECT
|
|
789
|
+
ECInstanceId,
|
|
790
|
+
JSON_EXTRACT(JsonProperties, '$.provenanceRelInstanceId') AS provenanceRelInstId
|
|
791
|
+
FROM Bis.ExternalSourceAspect
|
|
792
|
+
WHERE Kind=?
|
|
793
|
+
AND Scope.Id=?
|
|
794
|
+
AND Identifier=?
|
|
795
|
+
`;
|
|
796
|
+
const params = new core_common_1.QueryBinder();
|
|
797
|
+
params.bindString(1, core_backend_1.ExternalSourceAspect.Kind.Relationship);
|
|
798
|
+
params.bindId(2, this.targetScopeElementId);
|
|
799
|
+
params.bindString(3, entityInProvenanceSourceId);
|
|
800
|
+
const result = (await this.getProvenanceDb()).createQueryReader(sql, params, {
|
|
801
|
+
usePrimaryConn: true,
|
|
802
|
+
});
|
|
803
|
+
if (await result.step()) {
|
|
804
|
+
const aspectId = result.current.id;
|
|
805
|
+
const provenanceRelInstId = result.current.provenanceRelInstId;
|
|
806
|
+
const provenanceRelInstanceId = provenanceRelInstId !== undefined
|
|
807
|
+
? provenanceRelInstId
|
|
808
|
+
: await this._queryTargetRelId(sourceRelInfo);
|
|
777
809
|
return {
|
|
778
810
|
aspectId,
|
|
779
811
|
relationshipId: provenanceRelInstanceId,
|
|
780
812
|
};
|
|
781
|
-
}
|
|
813
|
+
}
|
|
814
|
+
else
|
|
815
|
+
return undefined;
|
|
782
816
|
}
|
|
783
|
-
_queryTargetRelId(sourceRelInfo) {
|
|
817
|
+
async _queryTargetRelId(sourceRelInfo) {
|
|
784
818
|
const targetRelInfo = {
|
|
785
819
|
sourceId: this.context.findTargetElementId(sourceRelInfo.sourceId),
|
|
786
820
|
targetId: this.context.findTargetElementId(sourceRelInfo.targetId),
|
|
@@ -788,60 +822,53 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
788
822
|
if (targetRelInfo.sourceId === undefined ||
|
|
789
823
|
targetRelInfo.targetId === undefined)
|
|
790
824
|
return undefined; // couldn't find an element, rel is invalid or deleted
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
return stmt.getValue(0).getId();
|
|
825
|
+
const sql = `
|
|
826
|
+
select ecinstanceid
|
|
827
|
+
from bis.elementreferstoelements
|
|
828
|
+
where sourceecinstanceid=?
|
|
829
|
+
and targetecinstanceid=?
|
|
830
|
+
and ecclassid=?
|
|
831
|
+
`;
|
|
832
|
+
const params = new core_common_1.QueryBinder();
|
|
833
|
+
params.bindId(1, targetRelInfo.sourceId);
|
|
834
|
+
params.bindId(2, targetRelInfo.targetId);
|
|
835
|
+
params.bindId(3, await this._targetClassNameToClassId(sourceRelInfo.classFullName));
|
|
836
|
+
const result = this.targetDb.createQueryReader(sql, params, {
|
|
837
|
+
usePrimaryConn: true,
|
|
805
838
|
});
|
|
839
|
+
if (await result.step())
|
|
840
|
+
return result.current.id;
|
|
841
|
+
else
|
|
842
|
+
return undefined;
|
|
806
843
|
}
|
|
807
844
|
_targetClassNameToClassIdCache = new Map();
|
|
808
|
-
_targetClassNameToClassId(classFullName) {
|
|
845
|
+
async _targetClassNameToClassId(classFullName) {
|
|
809
846
|
let classId = this._targetClassNameToClassIdCache.get(classFullName);
|
|
810
847
|
if (classId === undefined) {
|
|
811
|
-
classId = this._getRelClassId(this.targetDb, classFullName);
|
|
848
|
+
classId = await this._getRelClassId(this.targetDb, classFullName);
|
|
812
849
|
this._targetClassNameToClassIdCache.set(classFullName, classId);
|
|
813
850
|
}
|
|
814
851
|
return classId;
|
|
815
852
|
}
|
|
816
853
|
// NOTE: this doesn't handle remapped element classes,
|
|
817
854
|
// but is only used for relationships rn
|
|
818
|
-
_getRelClassId(db, classFullName) {
|
|
819
|
-
|
|
820
|
-
return db.withPreparedStatement(`
|
|
855
|
+
async _getRelClassId(db, classFullName) {
|
|
856
|
+
const sql = `
|
|
821
857
|
SELECT c.ECInstanceId
|
|
822
858
|
FROM ECDbMeta.ECClassDef c
|
|
823
859
|
JOIN ECDbMeta.ECSchemaDef s ON c.Schema.Id=s.ECInstanceId
|
|
824
860
|
WHERE s.Name=? AND c.Name=?
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
_queryElemIdByFedGuid(db, fedGuid) {
|
|
837
|
-
// eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
|
|
838
|
-
return db.withPreparedStatement("SELECT ECInstanceId FROM Bis.Element WHERE FederationGuid=?", (stmt) => {
|
|
839
|
-
stmt.bindGuid(1, fedGuid);
|
|
840
|
-
if (stmt.step() === core_bentley_1.DbResult.BE_SQLITE_ROW)
|
|
841
|
-
return stmt.getValue(0).getId();
|
|
842
|
-
else
|
|
843
|
-
return undefined;
|
|
844
|
-
});
|
|
861
|
+
`;
|
|
862
|
+
const params = new core_common_1.QueryBinder();
|
|
863
|
+
const [schemaName, className] = classFullName.indexOf(".") !== -1
|
|
864
|
+
? classFullName.split(".")
|
|
865
|
+
: classFullName.split(":");
|
|
866
|
+
params.bindString(1, schemaName);
|
|
867
|
+
params.bindString(2, className);
|
|
868
|
+
const result = db.createQueryReader(sql, params, { usePrimaryConn: true });
|
|
869
|
+
if (await result.step())
|
|
870
|
+
return result.current.id;
|
|
871
|
+
(0, core_bentley_1.assert)(false, "relationship was not found");
|
|
845
872
|
}
|
|
846
873
|
/** Returns `true` if *brute force* delete detections should be run.
|
|
847
874
|
* @note This is only called if [[IModelTransformOptions.forceExternalSourceAspectProvenance]] option is true
|
|
@@ -851,53 +878,15 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
851
878
|
nodeAssert(this._syncType !== undefined);
|
|
852
879
|
return this._syncType === "not-sync";
|
|
853
880
|
}
|
|
854
|
-
/**
|
|
855
|
-
* Detect Element deletes using ExternalSourceAspects in the target iModel and a *brute force* comparison against Elements
|
|
856
|
-
* in the source iModel.
|
|
857
|
-
* @deprecated in 1.x. Do not use this. // FIXME<MIKE>: how to better explain this?
|
|
858
|
-
* This method is only called during [[process]] when [[IModelTransformOptions.argsForProcessChanges]] is undefined and the option
|
|
859
|
-
* [[IModelTransformOptions.forceExternalSourceAspectProvenance]] is enabled. It is not
|
|
860
|
-
* necessary when calling [[process]] with [[IModelTransformOptions.argsForProcessChanges]] defined, since changeset information is sufficient.
|
|
861
|
-
* @note you do not need to call this directly unless processing a subset of an iModel.
|
|
862
|
-
* @throws [[IModelError]] If the required provenance information is not available to detect deletes.
|
|
863
|
-
*/
|
|
864
|
-
async detectElementDeletes() {
|
|
865
|
-
const sql = `
|
|
866
|
-
SELECT Identifier, Element.Id
|
|
867
|
-
FROM BisCore.ExternalSourceAspect
|
|
868
|
-
WHERE Scope.Id=:scopeId
|
|
869
|
-
AND Kind=:kind
|
|
870
|
-
`;
|
|
871
|
-
nodeAssert(!this.isReverseSynchronization, "synchronizations with processChanges already detect element deletes, don't call detectElementDeletes");
|
|
872
|
-
// Reported issue: https://github.com/iTwin/itwinjs-core/issues/7989
|
|
873
|
-
// eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
|
|
874
|
-
this.provenanceDb.withPreparedStatement(sql, (stmt) => {
|
|
875
|
-
stmt.bindId("scopeId", this.targetScopeElementId);
|
|
876
|
-
stmt.bindString("kind", core_backend_1.ExternalSourceAspect.Kind.Element);
|
|
877
|
-
while (core_bentley_1.DbResult.BE_SQLITE_ROW === stmt.step()) {
|
|
878
|
-
// ExternalSourceAspect.Identifier is of type string
|
|
879
|
-
const aspectIdentifier = stmt.getValue(0).getString();
|
|
880
|
-
if (!core_bentley_1.Id64.isValidId64(aspectIdentifier)) {
|
|
881
|
-
continue;
|
|
882
|
-
}
|
|
883
|
-
const targetElemId = stmt.getValue(1).getId();
|
|
884
|
-
const wasDeletedInSource = !EntityUnifier_1.EntityUnifier.exists(this.sourceDb, {
|
|
885
|
-
entityReference: `e${aspectIdentifier}`,
|
|
886
|
-
});
|
|
887
|
-
if (wasDeletedInSource)
|
|
888
|
-
this.importer.deleteElement(targetElemId);
|
|
889
|
-
}
|
|
890
|
-
});
|
|
891
|
-
}
|
|
892
881
|
/** Transform the specified sourceElement into ElementProps for the target iModel.
|
|
893
882
|
* @param sourceElement The Element from the source iModel to transform.
|
|
894
883
|
* @returns ElementProps for the target iModel.
|
|
895
884
|
* @note A subclass can override this method to provide custom transform behavior.
|
|
896
885
|
* @note This can be called more than once for an element in arbitrary order, so it should not have side-effects.
|
|
897
886
|
*/
|
|
898
|
-
onTransformElement(sourceElement) {
|
|
887
|
+
async onTransformElement(sourceElement) {
|
|
899
888
|
core_bentley_1.Logger.logTrace(loggerCategory, `onTransformElement(${sourceElement.id}) "${sourceElement.getDisplayLabel()}"`);
|
|
900
|
-
const targetElementProps = this.context.cloneElement(sourceElement, { binaryGeometry: this._options.cloneUsingBinaryGeometry });
|
|
889
|
+
const targetElementProps = await this.context.cloneElement(sourceElement, { binaryGeometry: this._options.cloneUsingBinaryGeometry });
|
|
901
890
|
// Special case: source element is the root subject
|
|
902
891
|
if (sourceElement.id === core_common_1.IModel.rootSubjectId) {
|
|
903
892
|
const targetElementId = this.context.findTargetElementId(sourceElement.id);
|
|
@@ -918,8 +907,87 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
918
907
|
targetElementProps.jsonProperties.Subject.Job = undefined;
|
|
919
908
|
}
|
|
920
909
|
}
|
|
910
|
+
if (this._linearSpatialTransform !== undefined &&
|
|
911
|
+
sourceElement instanceof core_backend_1.GeometricElement3d) {
|
|
912
|
+
// can check the sourceElement since this IModelTransformer does not remap classes
|
|
913
|
+
const placement = core_common_1.Placement3d.fromJSON(targetElementProps.placement);
|
|
914
|
+
if (placement.isValid) {
|
|
915
|
+
placement.multiplyTransform(this._linearSpatialTransform);
|
|
916
|
+
targetElementProps.placement = placement;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
921
919
|
return targetElementProps;
|
|
922
920
|
}
|
|
921
|
+
/**
|
|
922
|
+
* Calculate the transform between two ECEF locations
|
|
923
|
+
* @param srcDb
|
|
924
|
+
* @param targetDb
|
|
925
|
+
* @returns Transform that converts relative coordinates in the source iModel to relative coordinates in the target iModel.
|
|
926
|
+
* @note This can only be used if both source and target iModels are linearly located
|
|
927
|
+
*/
|
|
928
|
+
calculateEcefTransform() {
|
|
929
|
+
const srcEcefLoc = this.sourceDb.ecefLocation;
|
|
930
|
+
const targetEcefLoc = this.targetDb.ecefLocation;
|
|
931
|
+
if (srcEcefLoc === undefined || targetEcefLoc === undefined) {
|
|
932
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NoGeoLocation, "Both source and target ECEF locations must be defined to calculate the transform.");
|
|
933
|
+
}
|
|
934
|
+
if (srcEcefLoc.getTransform().isAlmostEqual(targetEcefLoc.getTransform())) {
|
|
935
|
+
core_bentley_1.Logger.logTrace(loggerCategory, "ECEF data is already aligned. No spatial transforms needed.");
|
|
936
|
+
return undefined;
|
|
937
|
+
}
|
|
938
|
+
const srcSpatialToECEF = srcEcefLoc.getTransform(); // converts relative to ECEF in relation to source
|
|
939
|
+
const targetECEFToSpatial = targetEcefLoc.getTransform().inverse(); // converts ECEF to relative in relation to target
|
|
940
|
+
if (!targetECEFToSpatial) {
|
|
941
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NoGeoLocation, "Failed to invert target ECEF transform.");
|
|
942
|
+
}
|
|
943
|
+
const ecefTransform = targetECEFToSpatial.multiplyTransformTransform(srcSpatialToECEF); // chain both transforms
|
|
944
|
+
return ecefTransform;
|
|
945
|
+
}
|
|
946
|
+
static convertHelmertToTransform(helmert) {
|
|
947
|
+
if (!helmert) {
|
|
948
|
+
return core_geometry_1.Transform.createIdentity();
|
|
949
|
+
}
|
|
950
|
+
const rotationXY = core_geometry_1.Matrix3d.createRotationAroundAxisIndex(core_geometry_1.AxisIndex.Z, core_geometry_1.Angle.createDegrees(helmert?.rotDeg));
|
|
951
|
+
rotationXY.scaleColumnsInPlace(helmert.scale, helmert.scale, 1.0);
|
|
952
|
+
const translation = core_geometry_1.Vector3d.create(helmert.translationX, helmert.translationY, helmert.translationZ);
|
|
953
|
+
const helmertTransform = core_geometry_1.Transform.createRefs(translation, rotationXY);
|
|
954
|
+
return helmertTransform;
|
|
955
|
+
}
|
|
956
|
+
calculateTransformFromHelmertTransforms() {
|
|
957
|
+
if (this.sourceDb.geographicCoordinateSystem?.horizontalCRS === undefined ||
|
|
958
|
+
this.sourceDb.geographicCoordinateSystem?.verticalCRS === undefined) {
|
|
959
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "Source iModel does not have a geographic coordinate system defined.");
|
|
960
|
+
}
|
|
961
|
+
if (this.targetDb.geographicCoordinateSystem?.horizontalCRS === undefined ||
|
|
962
|
+
this.targetDb.geographicCoordinateSystem.verticalCRS === undefined) {
|
|
963
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "Target iModel does not have a geographic coordinate system defined.");
|
|
964
|
+
}
|
|
965
|
+
if (!this.sourceDb.geographicCoordinateSystem.horizontalCRS.equals(this.targetDb.geographicCoordinateSystem.horizontalCRS) ||
|
|
966
|
+
!this.sourceDb.geographicCoordinateSystem.verticalCRS.equals(this.targetDb.geographicCoordinateSystem.verticalCRS)) {
|
|
967
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.MismatchGcs, "Source and target geographic coordinate systems must match to calculate the spatial transform.");
|
|
968
|
+
}
|
|
969
|
+
if (this.sourceDb.geographicCoordinateSystem.additionalTransform ===
|
|
970
|
+
this.targetDb.geographicCoordinateSystem.additionalTransform) {
|
|
971
|
+
core_bentley_1.Logger.logTrace(loggerCategory, "Geolocation data is already aligned. No spatial transforms needed.");
|
|
972
|
+
return undefined;
|
|
973
|
+
}
|
|
974
|
+
const srcScale = this.sourceDb.geographicCoordinateSystem.additionalTransform
|
|
975
|
+
?.helmert2DWithZOffset?.scale ?? 1;
|
|
976
|
+
const targetScale = this.targetDb.geographicCoordinateSystem.additionalTransform
|
|
977
|
+
?.helmert2DWithZOffset?.scale ?? 1;
|
|
978
|
+
if (srcScale !== targetScale) {
|
|
979
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.MismatchGcs, "Spatial transform is non rigid. Source and target Helmert transforms must have the same scale to calculate a rigid spatial transform.");
|
|
980
|
+
}
|
|
981
|
+
const srcTransform = IModelTransformer.convertHelmertToTransform(this.sourceDb.geographicCoordinateSystem.additionalTransform
|
|
982
|
+
?.helmert2DWithZOffset); // moves elements to where src helmert transform would move them at render time
|
|
983
|
+
const targetTransformInv = IModelTransformer.convertHelmertToTransform(this.targetDb.geographicCoordinateSystem.additionalTransform
|
|
984
|
+
?.helmert2DWithZOffset).inverse(); // negates target helmert transform that is applied at render time
|
|
985
|
+
if (!targetTransformInv) {
|
|
986
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NoGeoLocation, "Failed to invert target Helmert transform.");
|
|
987
|
+
}
|
|
988
|
+
const combinedTransform = targetTransformInv.multiplyTransformTransform(srcTransform);
|
|
989
|
+
return combinedTransform;
|
|
990
|
+
}
|
|
923
991
|
// if undefined, it can be initialized by calling [[this.processChangesets]]
|
|
924
992
|
_deletedSourceRelationshipData = undefined;
|
|
925
993
|
/** Returns true if a change within sourceElement is detected.
|
|
@@ -932,7 +1000,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
932
1000
|
sourceDbChanges.element.insertIds.has(sourceElement.id) ||
|
|
933
1001
|
sourceDbChanges.element.updateIds.has(sourceElement.id));
|
|
934
1002
|
}
|
|
935
|
-
completePartiallyCommittedElements() {
|
|
1003
|
+
async completePartiallyCommittedElements() {
|
|
936
1004
|
for (const sourceElementId of this._partiallyCommittedElementIds) {
|
|
937
1005
|
const sourceElement = this.sourceDb.elements.getElement({
|
|
938
1006
|
id: sourceElementId,
|
|
@@ -943,25 +1011,25 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
943
1011
|
if (core_bentley_1.Id64.isInvalid(targetId)) {
|
|
944
1012
|
throw new Error(`source-target element mapping not found for element "${sourceElementId}" when completing partially committed elements. This is a bug.`);
|
|
945
1013
|
}
|
|
946
|
-
const targetProps = this.onTransformElement(sourceElement);
|
|
1014
|
+
const targetProps = await this.onTransformElement(sourceElement);
|
|
947
1015
|
this.targetDb.elements.updateElement({ ...targetProps, id: targetId });
|
|
948
1016
|
}
|
|
949
1017
|
}
|
|
950
|
-
completePartiallyCommittedAspects() {
|
|
1018
|
+
async completePartiallyCommittedAspects() {
|
|
951
1019
|
for (const sourceAspectId of this._partiallyCommittedAspectIds) {
|
|
952
1020
|
const sourceAspect = this.sourceDb.elements.getAspect(sourceAspectId);
|
|
953
1021
|
const targetAspectId = this.context.findTargetAspectId(sourceAspectId);
|
|
954
1022
|
if (core_bentley_1.Id64.isInvalid(targetAspectId)) {
|
|
955
1023
|
throw new Error(`source-target aspect mapping not found for aspect "${sourceAspectId}" when completing partially committed aspects. This is a bug.`);
|
|
956
1024
|
}
|
|
957
|
-
const targetAspectProps = this.onTransformElementAspect(sourceAspect);
|
|
1025
|
+
const targetAspectProps = await this.onTransformElementAspect(sourceAspect);
|
|
958
1026
|
this.targetDb.elements.updateAspect({
|
|
959
1027
|
...targetAspectProps,
|
|
960
1028
|
id: targetAspectId,
|
|
961
1029
|
});
|
|
962
1030
|
}
|
|
963
1031
|
}
|
|
964
|
-
doAllReferencesExistInTarget(entity) {
|
|
1032
|
+
async doAllReferencesExistInTarget(entity) {
|
|
965
1033
|
let allReferencesExist = true;
|
|
966
1034
|
for (const referenceId of entity.getReferenceIds()) {
|
|
967
1035
|
const referencedEntityId = core_backend_1.EntityReferences.toId64(referenceId);
|
|
@@ -971,7 +1039,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
971
1039
|
continue;
|
|
972
1040
|
}
|
|
973
1041
|
if (allReferencesExist &&
|
|
974
|
-
!core_backend_1.EntityReferences.isValid(this.context.findTargetEntityId(referenceId))) {
|
|
1042
|
+
!core_backend_1.EntityReferences.isValid(await this.context.findTargetEntityId(referenceId))) {
|
|
975
1043
|
// if we care about references existing then we cannot return early and must check all other references.
|
|
976
1044
|
if (this._options.danglingReferencesBehavior === "ignore") {
|
|
977
1045
|
return false;
|
|
@@ -979,13 +1047,13 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
979
1047
|
allReferencesExist = false;
|
|
980
1048
|
}
|
|
981
1049
|
if (this._options.danglingReferencesBehavior === "reject") {
|
|
982
|
-
this.assertReferenceExistsInSource(referenceId, entity);
|
|
1050
|
+
await this.assertReferenceExistsInSource(referenceId, entity);
|
|
983
1051
|
}
|
|
984
1052
|
}
|
|
985
1053
|
return allReferencesExist;
|
|
986
1054
|
}
|
|
987
|
-
assertReferenceExistsInSource(referenceId, entity) {
|
|
988
|
-
const referencedExistsInSource = EntityUnifier_1.EntityUnifier.exists(this.sourceDb, {
|
|
1055
|
+
async assertReferenceExistsInSource(referenceId, entity) {
|
|
1056
|
+
const referencedExistsInSource = await EntityUnifier_1.EntityUnifier.exists(this.sourceDb, {
|
|
989
1057
|
entityReference: referenceId,
|
|
990
1058
|
});
|
|
991
1059
|
if (!referencedExistsInSource) {
|
|
@@ -1027,6 +1095,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1027
1095
|
* @internal do not call, override or implement this, it will be removed
|
|
1028
1096
|
*/
|
|
1029
1097
|
async preExportElement(sourceElement) {
|
|
1098
|
+
if (!this.hasElementChanged(sourceElement)) {
|
|
1099
|
+
core_bentley_1.Logger.logTrace(loggerCategory, `Skipping unchanged element (${sourceElement.id}, ${sourceElement.getDisplayLabel()}).`);
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1030
1102
|
const elemClass = sourceElement.constructor;
|
|
1031
1103
|
const unresolvedReferences = elemClass.requiredReferenceKeys
|
|
1032
1104
|
.map((referenceKey) => {
|
|
@@ -1061,7 +1133,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1061
1133
|
.flat();
|
|
1062
1134
|
if (unresolvedReferences.length > 0) {
|
|
1063
1135
|
for (const reference of unresolvedReferences) {
|
|
1064
|
-
const processState = this.getElemTransformState(reference);
|
|
1136
|
+
const processState = await this.getElemTransformState(reference);
|
|
1065
1137
|
// must export element first
|
|
1066
1138
|
if (processState.needsElemImport)
|
|
1067
1139
|
await this.exporter.exportElement(reference);
|
|
@@ -1070,23 +1142,23 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1070
1142
|
}
|
|
1071
1143
|
}
|
|
1072
1144
|
}
|
|
1073
|
-
getElemTransformState(elementId) {
|
|
1074
|
-
const dbHasModel = (db, id) => {
|
|
1145
|
+
async getElemTransformState(elementId) {
|
|
1146
|
+
const dbHasModel = async (db, id) => {
|
|
1075
1147
|
const maybeModelId = core_backend_1.EntityReferences.fromEntityType(id, core_common_1.ConcreteEntityTypes.Model);
|
|
1076
1148
|
return EntityUnifier_1.EntityUnifier.exists(db, { entityReference: maybeModelId });
|
|
1077
1149
|
};
|
|
1078
|
-
const isSubModeled = dbHasModel(this.sourceDb, elementId);
|
|
1150
|
+
const isSubModeled = await dbHasModel(this.sourceDb, elementId);
|
|
1079
1151
|
const idOfElemInTarget = this.context.findTargetElementId(elementId);
|
|
1080
1152
|
const isElemInTarget = core_bentley_1.Id64.invalid !== idOfElemInTarget;
|
|
1081
1153
|
const needsModelImport = isSubModeled &&
|
|
1082
|
-
(!isElemInTarget || !dbHasModel(this.targetDb, idOfElemInTarget));
|
|
1154
|
+
(!isElemInTarget || !(await dbHasModel(this.targetDb, idOfElemInTarget)));
|
|
1083
1155
|
return { needsElemImport: !isElemInTarget, needsModelImport };
|
|
1084
1156
|
}
|
|
1085
1157
|
/** Override of [IModelExportHandler.onExportElement]($transformer) that imports an element into the target iModel when it is exported from the source iModel.
|
|
1086
1158
|
* This override calls [[onTransformElement]] and then [IModelImporter.importElement]($transformer) to update the target iModel.
|
|
1087
1159
|
*/
|
|
1088
|
-
onExportElement(sourceElement) {
|
|
1089
|
-
let targetElementId;
|
|
1160
|
+
async onExportElement(sourceElement) {
|
|
1161
|
+
let targetElementId = core_bentley_1.Id64.invalid;
|
|
1090
1162
|
let targetElementProps;
|
|
1091
1163
|
if (this._options.wasSourceIModelCopiedToTarget) {
|
|
1092
1164
|
targetElementId = sourceElement.id;
|
|
@@ -1095,14 +1167,14 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1095
1167
|
}
|
|
1096
1168
|
else {
|
|
1097
1169
|
targetElementId = this.context.findTargetElementId(sourceElement.id);
|
|
1098
|
-
targetElementProps = this.onTransformElement(sourceElement);
|
|
1170
|
+
targetElementProps = await this.onTransformElement(sourceElement);
|
|
1099
1171
|
}
|
|
1100
1172
|
// if an existing remapping was not yet found, check by FederationGuid
|
|
1101
1173
|
if (this.context.isBetweenIModels &&
|
|
1102
1174
|
!core_bentley_1.Id64.isValid(targetElementId) &&
|
|
1103
1175
|
sourceElement.federationGuid !== undefined) {
|
|
1104
1176
|
targetElementId =
|
|
1105
|
-
this.
|
|
1177
|
+
this.targetDb.elements.getIdFromFederationGuid(sourceElement.federationGuid) ?? core_bentley_1.Id64.invalid;
|
|
1106
1178
|
if (core_bentley_1.Id64.isValid(targetElementId))
|
|
1107
1179
|
this.context.remapElement(sourceElement.id, targetElementId); // record that the targetElement was found
|
|
1108
1180
|
}
|
|
@@ -1112,7 +1184,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1112
1184
|
// respond the same way to undefined code value as the @see Code class, but don't use that class because it trims
|
|
1113
1185
|
// whitespace from the value, and there are iModels out there with untrimmed whitespace that we ought not to trim
|
|
1114
1186
|
targetElementProps.code.value = targetElementProps.code.value ?? "";
|
|
1115
|
-
const maybeTargetElementId = this.
|
|
1187
|
+
const maybeTargetElementId = await this.queryElementIdByCode(this.targetDb, targetElementProps.code);
|
|
1116
1188
|
if (undefined !== maybeTargetElementId) {
|
|
1117
1189
|
const maybeTargetElem = this.targetDb.elements.getElement(maybeTargetElementId);
|
|
1118
1190
|
if (maybeTargetElem.classFullName === targetElementProps.classFullName) {
|
|
@@ -1129,7 +1201,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1129
1201
|
core_bentley_1.Logger.logTrace(loggerCategory, `Skipping unchanged element (${sourceElement.id}, ${sourceElement.getDisplayLabel()}).`);
|
|
1130
1202
|
return;
|
|
1131
1203
|
}
|
|
1132
|
-
if (!this.doAllReferencesExistInTarget(sourceElement)) {
|
|
1204
|
+
if (!(await this.doAllReferencesExistInTarget(sourceElement))) {
|
|
1133
1205
|
this._partiallyCommittedElementIds.add(sourceElement.id);
|
|
1134
1206
|
}
|
|
1135
1207
|
// targetElementId will be valid (indicating update) or undefined (indicating insert)
|
|
@@ -1162,7 +1234,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1162
1234
|
if (!this._options.wasSourceIModelCopiedToTarget) {
|
|
1163
1235
|
this.importer.importElement(targetElementProps); // don't need to import if iModel was copied
|
|
1164
1236
|
}
|
|
1165
|
-
|
|
1237
|
+
if (targetElementProps.id === undefined) {
|
|
1238
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "targetElementProps.id should be assigned by importElement");
|
|
1239
|
+
}
|
|
1240
|
+
this.context.remapElement(sourceElement.id, targetElementProps.id);
|
|
1166
1241
|
// the transformer does not currently 'split' or 'join' any elements, therefore, it does not
|
|
1167
1242
|
// insert external source aspects because federation guids are sufficient for this.
|
|
1168
1243
|
// Other transformer subclasses must insert the appropriate aspect (as provided by a TBD API)
|
|
@@ -1170,26 +1245,47 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1170
1245
|
// physical consolidation is an example of a 'joining' transform
|
|
1171
1246
|
// FIXME: verify at finalization time that we don't lose provenance on new elements
|
|
1172
1247
|
// FIXME: make public and improve `initElementProvenance` API for usage by consolidators
|
|
1248
|
+
const provenanceDb = await this.getProvenanceDb();
|
|
1173
1249
|
if (!this._options.noProvenance) {
|
|
1174
1250
|
let provenance = this._options.forceExternalSourceAspectProvenance ||
|
|
1175
1251
|
this._elementsWithExplicitlyTrackedProvenance.has(sourceElement.id)
|
|
1176
1252
|
? undefined
|
|
1177
1253
|
: sourceElement.federationGuid;
|
|
1178
1254
|
if (!provenance) {
|
|
1179
|
-
const aspectProps = this.initElementProvenance(sourceElement.id, targetElementProps.id);
|
|
1180
|
-
const foundEsaProps = IModelTransformer.queryScopeExternalSourceAspect(
|
|
1255
|
+
const aspectProps = await this.initElementProvenance(sourceElement.id, targetElementProps.id);
|
|
1256
|
+
const foundEsaProps = await IModelTransformer.queryScopeExternalSourceAspect(provenanceDb, aspectProps);
|
|
1181
1257
|
if (foundEsaProps === undefined)
|
|
1182
|
-
aspectProps.id =
|
|
1258
|
+
aspectProps.id = provenanceDb.elements.insertAspect(aspectProps);
|
|
1183
1259
|
else {
|
|
1184
1260
|
// Since initElementProvenance sets a property 'version' on the aspectProps that we wish to persist in the provenanceDb, only grab the id from the foundEsaProps.
|
|
1185
1261
|
aspectProps.id = foundEsaProps.aspectId;
|
|
1186
|
-
|
|
1262
|
+
provenanceDb.elements.updateAspect(aspectProps);
|
|
1187
1263
|
}
|
|
1188
1264
|
provenance = aspectProps;
|
|
1189
1265
|
}
|
|
1190
1266
|
this.markLastProvenance(provenance, { isRelationship: false });
|
|
1191
1267
|
}
|
|
1192
1268
|
}
|
|
1269
|
+
// In iTwin js 5.x Elements.queryElementIdByCode() uses Code class to query id:
|
|
1270
|
+
// https://github.com/iTwin/itwinjs-core/blob/master/core/backend/src/IModelDb.ts#L2779
|
|
1271
|
+
// Code class constructor trims white spaces from code value.
|
|
1272
|
+
// Custom implementation of queryElementIdByCode() was added to support querying elements with code values that have trailing whitespaces.
|
|
1273
|
+
// It mimicks 4.x implementation: https://github.com/iTwin/itwinjs-core/blob/9c8b394ec3878a39764be81f928fd8b0b9115d31/core/backend/src/IModelDb.ts#L1882
|
|
1274
|
+
async queryElementIdByCode(iModel, code) {
|
|
1275
|
+
if (core_bentley_1.Id64.isInvalid(code.spec))
|
|
1276
|
+
throw new Error("Invalid CodeSpec");
|
|
1277
|
+
if (code.value === undefined)
|
|
1278
|
+
throw new Error("Invalid Code");
|
|
1279
|
+
const query = "SELECT ECInstanceId FROM BisCore:Element WHERE CodeSpec.Id=? AND CodeScope.Id=? AND CodeValue=?";
|
|
1280
|
+
const queryBinder = new core_common_1.QueryBinder()
|
|
1281
|
+
.bindId(1, code.spec)
|
|
1282
|
+
.bindId(2, core_bentley_1.Id64.fromString(code.scope))
|
|
1283
|
+
.bindString(3, code.value);
|
|
1284
|
+
const queryReader = iModel.createQueryReader(query, queryBinder, {
|
|
1285
|
+
usePrimaryConn: true,
|
|
1286
|
+
});
|
|
1287
|
+
return (await queryReader.step()) ? queryReader.current[0] : undefined;
|
|
1288
|
+
}
|
|
1193
1289
|
/** Override of [IModelExportHandler.onDeleteElement]($transformer) that is called when [IModelExporter]($transformer) detects that an Element has been deleted from the source iModel.
|
|
1194
1290
|
* This override propagates the delete to the target iModel via [IModelImporter.deleteElement]($transformer).
|
|
1195
1291
|
*/
|
|
@@ -1216,7 +1312,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1216
1312
|
this.importer.importModel(targetModelProps);
|
|
1217
1313
|
}
|
|
1218
1314
|
/** Override of [IModelExportHandler.onDeleteModel]($transformer) that is called when [IModelExporter]($transformer) detects that a [Model]($backend) has been deleted from the source iModel. */
|
|
1219
|
-
onDeleteModel(sourceModelId) {
|
|
1315
|
+
async onDeleteModel(sourceModelId) {
|
|
1220
1316
|
// It is possible and apparently occasionally sensical to delete a model without deleting its underlying element.
|
|
1221
1317
|
// - If only the model is deleted, [[initFromExternalSourceAspects]] will have already remapped the underlying element since it still exists.
|
|
1222
1318
|
// - If both were deleted, [[remapDeletedSourceEntities]] will find and remap the deleted element making this operation valid
|
|
@@ -1233,19 +1329,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1233
1329
|
WHERE ECInstanceId=:targetModelId
|
|
1234
1330
|
`;
|
|
1235
1331
|
if (this.exporter.sourceDbChanges?.element.deleteIds.has(sourceModelId)) {
|
|
1236
|
-
|
|
1237
|
-
const
|
|
1238
|
-
|
|
1239
|
-
const val = stmt.step();
|
|
1240
|
-
switch (val) {
|
|
1241
|
-
case core_bentley_1.DbResult.BE_SQLITE_ROW:
|
|
1242
|
-
return true;
|
|
1243
|
-
case core_bentley_1.DbResult.BE_SQLITE_DONE:
|
|
1244
|
-
return false;
|
|
1245
|
-
default:
|
|
1246
|
-
(0, core_bentley_1.assert)(false, `unexpected db result: '${JSON.stringify(stmt)}'`);
|
|
1247
|
-
}
|
|
1332
|
+
const params = new core_common_1.QueryBinder().bindId("targetModelId", targetModelId);
|
|
1333
|
+
const reader = this.targetDb.createQueryReader(sql, params, {
|
|
1334
|
+
usePrimaryConn: true,
|
|
1248
1335
|
});
|
|
1336
|
+
const isDefinitionPartition = await reader.step();
|
|
1249
1337
|
if (isDefinitionPartition) {
|
|
1250
1338
|
// Skipping model deletion because model's partition will also be deleted.
|
|
1251
1339
|
// It expects that model will be present and will fail if it's missing.
|
|
@@ -1254,7 +1342,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1254
1342
|
}
|
|
1255
1343
|
}
|
|
1256
1344
|
try {
|
|
1257
|
-
this.importer.deleteModel(targetModelId);
|
|
1345
|
+
await this.importer.deleteModel(targetModelId);
|
|
1258
1346
|
}
|
|
1259
1347
|
catch (error) {
|
|
1260
1348
|
const isDeletionProhibitedErr = error instanceof core_common_1.IModelError &&
|
|
@@ -1301,41 +1389,24 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1301
1389
|
await this.initialize();
|
|
1302
1390
|
// import DefinitionModels first
|
|
1303
1391
|
const childDefinitionPartitionSql = `SELECT ECInstanceId FROM ${core_backend_1.DefinitionPartition.classFullName} WHERE Parent.Id=:subjectId`;
|
|
1304
|
-
|
|
1305
|
-
await this.sourceDb.
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
statement.bindId("subjectId", sourceSubjectId);
|
|
1309
|
-
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1310
|
-
await this.processModel(statement.getValue(0).getId());
|
|
1311
|
-
}
|
|
1312
|
-
});
|
|
1392
|
+
const params = new core_common_1.QueryBinder().bindId("subjectId", sourceSubjectId);
|
|
1393
|
+
for await (const row of this.sourceDb.createQueryReader(childDefinitionPartitionSql, params, { usePrimaryConn: true })) {
|
|
1394
|
+
await this.processModel(row.id);
|
|
1395
|
+
}
|
|
1313
1396
|
// import other partitions next
|
|
1314
1397
|
const childPartitionSql = `SELECT ECInstanceId FROM ${core_backend_1.InformationPartitionElement.classFullName} WHERE Parent.Id=:subjectId`;
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1321
|
-
const modelId = statement.getValue(0).getId();
|
|
1322
|
-
const model = this.sourceDb.models.getModel(modelId);
|
|
1323
|
-
if (!(model instanceof core_backend_1.DefinitionModel)) {
|
|
1324
|
-
await this.processModel(modelId);
|
|
1325
|
-
}
|
|
1398
|
+
for await (const row of this.sourceDb.createQueryReader(childPartitionSql, params, { usePrimaryConn: true })) {
|
|
1399
|
+
const modelId = row.id;
|
|
1400
|
+
const model = this.sourceDb.models.getModel(modelId);
|
|
1401
|
+
if (!(model instanceof core_backend_1.DefinitionModel)) {
|
|
1402
|
+
await this.processModel(modelId);
|
|
1326
1403
|
}
|
|
1327
|
-
}
|
|
1404
|
+
}
|
|
1328
1405
|
// recurse into child Subjects
|
|
1329
1406
|
const childSubjectSql = `SELECT ECInstanceId FROM ${core_backend_1.Subject.classFullName} WHERE Parent.Id=:subjectId`;
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
async (statement) => {
|
|
1334
|
-
statement.bindId("subjectId", sourceSubjectId);
|
|
1335
|
-
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1336
|
-
await this.processSubjectSubModels(statement.getValue(0).getId());
|
|
1337
|
-
}
|
|
1338
|
-
});
|
|
1407
|
+
for await (const row of this.sourceDb.createQueryReader(childSubjectSql, params, { usePrimaryConn: true })) {
|
|
1408
|
+
await this.processSubjectSubModels(row.id);
|
|
1409
|
+
}
|
|
1339
1410
|
}
|
|
1340
1411
|
/** Transform the specified sourceModel into ModelProps for the target iModel.
|
|
1341
1412
|
* @param sourceModel The Model from the source iModel to be transformed.
|
|
@@ -1351,6 +1422,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1351
1422
|
id: targetModeledElementId,
|
|
1352
1423
|
};
|
|
1353
1424
|
targetModelProps.id = targetModeledElementId;
|
|
1425
|
+
if (targetModelProps.parentModel === undefined) {
|
|
1426
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "targetElementProps must have a defined parentModel");
|
|
1427
|
+
}
|
|
1354
1428
|
targetModelProps.parentModel = this.context.findTargetElementId(targetModelProps.parentModel);
|
|
1355
1429
|
return targetModelProps;
|
|
1356
1430
|
}
|
|
@@ -1372,7 +1446,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1372
1446
|
* Note that typically, the reverseSyncVersion is saved as the last changeset merged from the branch into master.
|
|
1373
1447
|
* Setting initializeReverseSyncVersion to true during a forward transformation could overwrite this correct reverseSyncVersion and should only be done during the first transformation between a master and branch iModel.
|
|
1374
1448
|
*/
|
|
1375
|
-
updateSynchronizationVersion({ initializeReverseSyncVersion = false, } = {}) {
|
|
1449
|
+
async updateSynchronizationVersion({ initializeReverseSyncVersion = false, } = {}) {
|
|
1376
1450
|
const shouldSkipSyncVersionUpdate = !initializeReverseSyncVersion &&
|
|
1377
1451
|
this._sourceChangeDataState !== "has-changes";
|
|
1378
1452
|
if (shouldSkipSyncVersionUpdate)
|
|
@@ -1380,7 +1454,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1380
1454
|
nodeAssert(this._targetScopeProvenanceProps);
|
|
1381
1455
|
const sourceVersion = `${this.sourceDb.changeset.id};${this.sourceDb.changeset.index}`;
|
|
1382
1456
|
const targetVersion = `${this.targetDb.changeset.id};${this.targetDb.changeset.index}`;
|
|
1383
|
-
if (this.
|
|
1457
|
+
if (await this.getIsReverseSynchronization()) {
|
|
1384
1458
|
const oldVersion = this._targetScopeProvenanceProps.jsonProperties.reverseSyncVersion;
|
|
1385
1459
|
core_bentley_1.Logger.logInfo(loggerCategory, `updating reverse version from ${oldVersion} to ${sourceVersion}`);
|
|
1386
1460
|
this._targetScopeProvenanceProps.jsonProperties.reverseSyncVersion =
|
|
@@ -1400,6 +1474,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1400
1474
|
(this._startingChangesetIndices && initializeReverseSyncVersion)) {
|
|
1401
1475
|
nodeAssert(this.targetDb.changeset.index !== undefined &&
|
|
1402
1476
|
this._startingChangesetIndices !== undefined, "updateSynchronizationVersion was called without change history");
|
|
1477
|
+
// Store in a local variable, so typescript knows it's defined (due to the assert above)
|
|
1478
|
+
const startingChangesetIndices = this._startingChangesetIndices;
|
|
1403
1479
|
const jsonProps = this._targetScopeProvenanceProps.jsonProperties;
|
|
1404
1480
|
core_bentley_1.Logger.logTrace(loggerCategory, `previous pendingReverseSyncChanges: ${jsonProps.pendingReverseSyncChangesetIndices}`);
|
|
1405
1481
|
core_bentley_1.Logger.logTrace(loggerCategory, `previous pendingSyncChanges: ${jsonProps.pendingSyncChangesetIndices}`);
|
|
@@ -1408,7 +1484,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1408
1484
|
// Determine which keys to clear and update based on the synchronization direction
|
|
1409
1485
|
let syncChangesetsToClearKey;
|
|
1410
1486
|
let syncChangesetsToUpdateKey;
|
|
1411
|
-
if (this.
|
|
1487
|
+
if (await this.getIsReverseSynchronization()) {
|
|
1412
1488
|
syncChangesetsToClearKey = pendingReverseSyncChangesetIndicesKey;
|
|
1413
1489
|
syncChangesetsToUpdateKey = pendingSyncChangesetIndicesKey;
|
|
1414
1490
|
}
|
|
@@ -1420,31 +1496,31 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1420
1496
|
// transformation finalization, the work will be saved immediately, otherwise we've
|
|
1421
1497
|
// just marked this changeset as a synchronization to ignore, and the user can add other
|
|
1422
1498
|
// stuff to it which would break future synchronizations
|
|
1423
|
-
for (let i =
|
|
1499
|
+
for (let i = startingChangesetIndices.target + 1; i <= this.targetDb.changeset.index + 1; i++)
|
|
1424
1500
|
jsonProps[syncChangesetsToUpdateKey].push(i);
|
|
1425
1501
|
// Only keep the changeset indices which are greater than the source, this means they haven't been processed yet.
|
|
1426
1502
|
jsonProps[syncChangesetsToClearKey] = jsonProps[syncChangesetsToClearKey].filter((csIndex) => {
|
|
1427
|
-
return csIndex >
|
|
1503
|
+
return csIndex > startingChangesetIndices.source;
|
|
1428
1504
|
});
|
|
1429
1505
|
// if reverse sync then we may have received provenance changes which should be marked as sync changes
|
|
1430
|
-
if (this.
|
|
1506
|
+
if (await this.getIsReverseSynchronization()) {
|
|
1431
1507
|
nodeAssert(this.sourceDb.changeset.index !== undefined, "changeset didn't exist");
|
|
1432
|
-
for (let i =
|
|
1508
|
+
for (let i = startingChangesetIndices.source + 1; i <= this.sourceDb.changeset.index + 1; i++)
|
|
1433
1509
|
jsonProps.pendingReverseSyncChangesetIndices.push(i);
|
|
1434
1510
|
}
|
|
1435
1511
|
core_bentley_1.Logger.logTrace(loggerCategory, `new pendingReverseSyncChanges: ${jsonProps.pendingReverseSyncChangesetIndices}`);
|
|
1436
1512
|
core_bentley_1.Logger.logTrace(loggerCategory, `new pendingSyncChanges: ${jsonProps.pendingSyncChangesetIndices}`);
|
|
1437
1513
|
}
|
|
1438
|
-
this.
|
|
1514
|
+
(await this.getProvenanceDb()).elements.updateAspect({
|
|
1439
1515
|
...this._targetScopeProvenanceProps,
|
|
1440
1516
|
jsonProperties: JSON.stringify(this._targetScopeProvenanceProps.jsonProperties),
|
|
1441
1517
|
});
|
|
1442
1518
|
this.clearCachedSynchronizationVersion();
|
|
1443
1519
|
}
|
|
1444
1520
|
// FIXME<MIKE>: is this necessary when manually using low level transform APIs? (document if so)
|
|
1445
|
-
finalizeTransformation() {
|
|
1521
|
+
async finalizeTransformation() {
|
|
1446
1522
|
this.importer.finalize();
|
|
1447
|
-
this.updateSynchronizationVersion({
|
|
1523
|
+
await this.updateSynchronizationVersion({
|
|
1448
1524
|
initializeReverseSyncVersion: this._isProvenanceInitTransform,
|
|
1449
1525
|
});
|
|
1450
1526
|
// TODO: ignore if we remove change cache usage
|
|
@@ -1477,22 +1553,23 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1477
1553
|
/** Override of [IModelExportHandler.onExportRelationship]($transformer) that imports a relationship into the target iModel when it is exported from the source iModel.
|
|
1478
1554
|
* This override calls [[onTransformRelationship]] and then [IModelImporter.importRelationship]($transformer) to update the target iModel.
|
|
1479
1555
|
*/
|
|
1480
|
-
onExportRelationship(sourceRelationship) {
|
|
1481
|
-
const sourceFedGuid =
|
|
1482
|
-
const targetFedGuid =
|
|
1556
|
+
async onExportRelationship(sourceRelationship) {
|
|
1557
|
+
const sourceFedGuid = this.sourceDb.elements.getFederationGuidFromId(sourceRelationship.sourceId);
|
|
1558
|
+
const targetFedGuid = this.sourceDb.elements.getFederationGuidFromId(sourceRelationship.targetId);
|
|
1483
1559
|
const targetRelationshipProps = this.onTransformRelationship(sourceRelationship);
|
|
1484
1560
|
const targetRelationshipInstanceId = this.importer.importRelationship(targetRelationshipProps);
|
|
1561
|
+
const provenanceDb = await this.getProvenanceDb();
|
|
1485
1562
|
if (!this._options.noProvenance &&
|
|
1486
1563
|
core_bentley_1.Id64.isValid(targetRelationshipInstanceId)) {
|
|
1487
1564
|
let provenance = !this._options.forceExternalSourceAspectProvenance
|
|
1488
1565
|
? sourceFedGuid && targetFedGuid && `${sourceFedGuid}/${targetFedGuid}`
|
|
1489
1566
|
: undefined;
|
|
1490
1567
|
if (!provenance) {
|
|
1491
|
-
const aspectProps = this.initRelationshipProvenance(sourceRelationship, targetRelationshipInstanceId);
|
|
1492
|
-
const foundEsaProps = IModelTransformer.queryScopeExternalSourceAspect(
|
|
1568
|
+
const aspectProps = await this.initRelationshipProvenance(sourceRelationship, targetRelationshipInstanceId);
|
|
1569
|
+
const foundEsaProps = await IModelTransformer.queryScopeExternalSourceAspect(provenanceDb, aspectProps);
|
|
1493
1570
|
// onExportRelationship doesn't need to call updateAspect if esaProps were found, because relationship provenance doesn't have the same concept of a version as element provenance (which uses last mod time on the elements).
|
|
1494
1571
|
if (undefined === foundEsaProps) {
|
|
1495
|
-
aspectProps.id =
|
|
1572
|
+
aspectProps.id = provenanceDb.elements.insertAspect(aspectProps);
|
|
1496
1573
|
}
|
|
1497
1574
|
provenance = aspectProps;
|
|
1498
1575
|
}
|
|
@@ -1502,7 +1579,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1502
1579
|
/** Override of [IModelExportHandler.onDeleteRelationship]($transformer) that is called when [IModelExporter]($transformer) detects that a [Relationship]($backend) has been deleted from the source iModel.
|
|
1503
1580
|
* This override propagates the delete to the target iModel via [IModelImporter.deleteRelationship]($transformer).
|
|
1504
1581
|
*/
|
|
1505
|
-
onDeleteRelationship(sourceRelInstanceId) {
|
|
1582
|
+
async onDeleteRelationship(sourceRelInstanceId) {
|
|
1506
1583
|
nodeAssert(this._deletedSourceRelationshipData, "should be defined at initialization by now");
|
|
1507
1584
|
const deletedRelData = this._deletedSourceRelationshipData.get(sourceRelInstanceId);
|
|
1508
1585
|
if (!deletedRelData) {
|
|
@@ -1523,7 +1600,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1523
1600
|
}
|
|
1524
1601
|
if (deletedRelData.provenanceAspectId) {
|
|
1525
1602
|
try {
|
|
1526
|
-
this.
|
|
1603
|
+
(await this.getProvenanceDb()).elements.deleteAspect(deletedRelData.provenanceAspectId);
|
|
1527
1604
|
}
|
|
1528
1605
|
catch (error) {
|
|
1529
1606
|
// This aspect may no longer exist if it was deleted at some other point during the transformation. This is fine.
|
|
@@ -1534,51 +1611,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1534
1611
|
}
|
|
1535
1612
|
}
|
|
1536
1613
|
_yieldManager = new core_bentley_1.YieldManager();
|
|
1537
|
-
/** Detect Relationship deletes using ExternalSourceAspects in the target iModel and a *brute force* comparison against relationships in the source iModel.
|
|
1538
|
-
* @deprecated in 1.x. Don't use this anymore
|
|
1539
|
-
* @see [[process]] with [[IModelTransformOptions.argsForProcessChanges]] provided.
|
|
1540
|
-
* @note This method is called from [[process]] when [[IModelTransformOptions.argsForProcessChanges]] are undefined, so it only needs to be called directly when processing a subset of an iModel.
|
|
1541
|
-
* @throws [[IModelError]] If the required provenance information is not available to detect deletes.
|
|
1542
|
-
*/
|
|
1543
|
-
async detectRelationshipDeletes() {
|
|
1544
|
-
if (this.isReverseSynchronization) {
|
|
1545
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "Cannot detect deletes when isReverseSynchronization=true");
|
|
1546
|
-
}
|
|
1547
|
-
const aspectDeleteIds = [];
|
|
1548
|
-
const sql = `
|
|
1549
|
-
SELECT ECInstanceId, Identifier, JsonProperties
|
|
1550
|
-
FROM ${core_backend_1.ExternalSourceAspect.classFullName} aspect
|
|
1551
|
-
WHERE aspect.Scope.Id=:scopeId
|
|
1552
|
-
AND aspect.Kind=:kind
|
|
1553
|
-
`;
|
|
1554
|
-
// eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
|
|
1555
|
-
await this.targetDb.withPreparedStatement(sql,
|
|
1556
|
-
// eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
|
|
1557
|
-
async (statement) => {
|
|
1558
|
-
statement.bindId("scopeId", this.targetScopeElementId);
|
|
1559
|
-
statement.bindString("kind", core_backend_1.ExternalSourceAspect.Kind.Relationship);
|
|
1560
|
-
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1561
|
-
const sourceRelInstanceId = core_bentley_1.Id64.fromJSON(statement.getValue(1).getString());
|
|
1562
|
-
if (undefined ===
|
|
1563
|
-
this.sourceDb.relationships.tryGetInstanceProps(core_backend_1.ElementRefersToElements.classFullName, sourceRelInstanceId)) {
|
|
1564
|
-
// this function exists only to support some in-imodel transformations, which must
|
|
1565
|
-
// use the old (external source aspect) provenance method anyway so we don't need to support
|
|
1566
|
-
// new provenance
|
|
1567
|
-
const json = JSON.parse(statement.getValue(2).getString());
|
|
1568
|
-
const targetRelInstanceId = json.targetRelInstanceId ?? json.provenanceRelInstanceId;
|
|
1569
|
-
if (targetRelInstanceId) {
|
|
1570
|
-
this.importer.deleteRelationship({
|
|
1571
|
-
id: targetRelInstanceId,
|
|
1572
|
-
classFullName: core_backend_1.ElementRefersToElements.classFullName,
|
|
1573
|
-
});
|
|
1574
|
-
}
|
|
1575
|
-
aspectDeleteIds.push(statement.getValue(0).getId());
|
|
1576
|
-
}
|
|
1577
|
-
await this._yieldManager.allowYield();
|
|
1578
|
-
}
|
|
1579
|
-
});
|
|
1580
|
-
this.targetDb.elements.deleteAspect(aspectDeleteIds);
|
|
1581
|
-
}
|
|
1582
1614
|
/** Transform the specified sourceRelationship into RelationshipProps for the target iModel.
|
|
1583
1615
|
* @param sourceRelationship The Relationship from the source iModel to be transformed.
|
|
1584
1616
|
* @returns RelationshipProps for the target iModel.
|
|
@@ -1589,11 +1621,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1589
1621
|
targetRelationshipProps.sourceId = this.context.findTargetElementId(sourceRelationship.sourceId);
|
|
1590
1622
|
targetRelationshipProps.targetId = this.context.findTargetElementId(sourceRelationship.targetId);
|
|
1591
1623
|
// TODO: move to cloneRelationship in IModelCloneContext
|
|
1592
|
-
sourceRelationship.
|
|
1593
|
-
if (
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
this.context.findTargetElementId(sourceRelationship.asAny[propertyName]);
|
|
1624
|
+
sourceRelationship.forEach((propertyName, property) => {
|
|
1625
|
+
if (property.isPrimitive() && "Id" === property.extendedTypeName) {
|
|
1626
|
+
targetRelationshipProps[core_common_1.ECJsNames.toJsName(propertyName)] =
|
|
1627
|
+
this.context.findTargetElementId(sourceRelationship.asAny[core_common_1.ECJsNames.toJsName(propertyName)]);
|
|
1597
1628
|
}
|
|
1598
1629
|
});
|
|
1599
1630
|
return targetRelationshipProps;
|
|
@@ -1606,9 +1637,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1606
1637
|
/** Override of [IModelExportHandler.onExportElementUniqueAspect]($transformer) that imports an ElementUniqueAspect into the target iModel when it is exported from the source iModel.
|
|
1607
1638
|
* This override calls [[onTransformElementAspect]] and then [IModelImporter.importElementUniqueAspect]($transformer) to update the target iModel.
|
|
1608
1639
|
*/
|
|
1609
|
-
onExportElementUniqueAspect(sourceAspect) {
|
|
1610
|
-
const targetAspectProps = this.onTransformElementAspect(sourceAspect);
|
|
1611
|
-
if (!this.doAllReferencesExistInTarget(sourceAspect)) {
|
|
1640
|
+
async onExportElementUniqueAspect(sourceAspect) {
|
|
1641
|
+
const targetAspectProps = await this.onTransformElementAspect(sourceAspect);
|
|
1642
|
+
if (!(await this.doAllReferencesExistInTarget(sourceAspect))) {
|
|
1612
1643
|
this._partiallyCommittedAspectIds.add(sourceAspect.id);
|
|
1613
1644
|
}
|
|
1614
1645
|
const targetId = this.importer.importElementUniqueAspect(targetAspectProps);
|
|
@@ -1618,16 +1649,16 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1618
1649
|
* This override calls [[onTransformElementAspect]] for each ElementMultiAspect and then [IModelImporter.importElementMultiAspects]($transformer) to update the target iModel.
|
|
1619
1650
|
* @note ElementMultiAspects are handled as a group to make it easier to differentiate between insert, update, and delete.
|
|
1620
1651
|
*/
|
|
1621
|
-
onExportElementMultiAspects(sourceAspects) {
|
|
1652
|
+
async onExportElementMultiAspects(sourceAspects) {
|
|
1622
1653
|
// Transform source ElementMultiAspects into target ElementAspectProps
|
|
1623
|
-
const targetAspectPropsArray = sourceAspects.map((srcA) => this.onTransformElementAspect(srcA));
|
|
1624
|
-
sourceAspects.forEach((a) => {
|
|
1625
|
-
if (!this.doAllReferencesExistInTarget(a)) {
|
|
1654
|
+
const targetAspectPropsArray = sourceAspects.map(async (srcA) => this.onTransformElementAspect(srcA));
|
|
1655
|
+
sourceAspects.forEach(async (a) => {
|
|
1656
|
+
if (!(await this.doAllReferencesExistInTarget(a))) {
|
|
1626
1657
|
this._partiallyCommittedAspectIds.add(a.id);
|
|
1627
1658
|
}
|
|
1628
1659
|
});
|
|
1629
1660
|
// const targetAspectsToImport = targetAspectPropsArray.filter((targetAspect, i) => hasEntityChanged(sourceAspects[i], targetAspect));
|
|
1630
|
-
const targetIds = this.importer.importElementMultiAspects(targetAspectPropsArray, (a) => {
|
|
1661
|
+
const targetIds = this.importer.importElementMultiAspects(await Promise.all(targetAspectPropsArray), (a) => {
|
|
1631
1662
|
const isExternalSourceAspectFromTransformer = a instanceof core_backend_1.ExternalSourceAspect &&
|
|
1632
1663
|
a.scope?.id === this.targetScopeElementId;
|
|
1633
1664
|
return (!this._options.includeSourceProvenance ||
|
|
@@ -1642,8 +1673,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1642
1673
|
* @returns ElementAspectProps for the target iModel.
|
|
1643
1674
|
* @note A subclass can override this method to provide custom transform behavior.
|
|
1644
1675
|
*/
|
|
1645
|
-
onTransformElementAspect(sourceElementAspect) {
|
|
1646
|
-
const targetElementAspectProps = this.context.cloneElementAspect(sourceElementAspect);
|
|
1676
|
+
async onTransformElementAspect(sourceElementAspect) {
|
|
1677
|
+
const targetElementAspectProps = await this.context.cloneElementAspect(sourceElementAspect);
|
|
1647
1678
|
return targetElementAspectProps;
|
|
1648
1679
|
}
|
|
1649
1680
|
/** The directory where schemas will be exported, a random temporary directory */
|
|
@@ -1685,7 +1716,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1685
1716
|
nodeAssert(schemaFileName.length <= systemMaxPathSegmentSize, "Schema name was still long. This is a bug.");
|
|
1686
1717
|
this._longNamedSchemasMap.set(schema.name, schemaFileName);
|
|
1687
1718
|
}
|
|
1688
|
-
/* eslint-disable-next-line deprecation/deprecation */
|
|
1689
1719
|
this.sourceDb.exportSchema({
|
|
1690
1720
|
schemaName: schema.name,
|
|
1691
1721
|
outputDirectory: this._schemaExportDir,
|
|
@@ -1773,8 +1803,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1773
1803
|
this.context.remapElement(sourceSubjectId, targetSubjectId);
|
|
1774
1804
|
await this.processChildElements(sourceSubjectId);
|
|
1775
1805
|
await this.processSubjectSubModels(sourceSubjectId);
|
|
1776
|
-
this.completePartiallyCommittedElements();
|
|
1777
|
-
this.completePartiallyCommittedAspects();
|
|
1806
|
+
await this.completePartiallyCommittedElements();
|
|
1807
|
+
await this.completePartiallyCommittedAspects();
|
|
1778
1808
|
}
|
|
1779
1809
|
/** state to prevent reinitialization, @see [[initialize]] */
|
|
1780
1810
|
_initialized = false;
|
|
@@ -1790,11 +1820,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1790
1820
|
async initialize() {
|
|
1791
1821
|
if (this._initialized)
|
|
1792
1822
|
return;
|
|
1793
|
-
this.initScopeProvenance();
|
|
1823
|
+
await this.initScopeProvenance();
|
|
1794
1824
|
await this._tryInitChangesetData(this._options.argsForProcessChanges);
|
|
1795
1825
|
await this.context.initialize();
|
|
1796
1826
|
// need exporter initialized to do remapdeletedsourceentities.
|
|
1797
|
-
await this.exporter.initialize(this.getExportInitOpts(this._options.argsForProcessChanges ?? {}));
|
|
1827
|
+
await this.exporter.initialize(await this.getExportInitOpts(this._options.argsForProcessChanges ?? {}));
|
|
1798
1828
|
// Exporter must be initialized prior to processing changesets in order to properly handle entity recreations (an entity delete followed by an insert of that same entity).
|
|
1799
1829
|
await this.processChangesets();
|
|
1800
1830
|
this._initialized = true;
|
|
@@ -1806,7 +1836,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1806
1836
|
* @returns void
|
|
1807
1837
|
*/
|
|
1808
1838
|
async processChangesets() {
|
|
1809
|
-
this.forEachTrackedElement((sourceElementId, targetElementId) => {
|
|
1839
|
+
await this.forEachTrackedElement((sourceElementId, targetElementId) => {
|
|
1810
1840
|
this.context.remapElement(sourceElementId, targetElementId);
|
|
1811
1841
|
});
|
|
1812
1842
|
if (this.exporter.sourceDbChanges)
|
|
@@ -1820,11 +1850,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1820
1850
|
this._sourceChangeDataState = "has-changes";
|
|
1821
1851
|
}
|
|
1822
1852
|
const relationshipECClassIdsToSkip = new Set();
|
|
1823
|
-
for await (const row of this.sourceDb.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementDrivesElement)")) {
|
|
1853
|
+
for await (const row of this.sourceDb.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementDrivesElement)", undefined, { usePrimaryConn: true })) {
|
|
1824
1854
|
relationshipECClassIdsToSkip.add(row.ECInstanceId);
|
|
1825
1855
|
}
|
|
1826
1856
|
const relationshipECClassIds = new Set();
|
|
1827
|
-
for await (const row of this.sourceDb.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementRefersToElements)")) {
|
|
1857
|
+
for await (const row of this.sourceDb.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementRefersToElements)", undefined, { usePrimaryConn: true })) {
|
|
1828
1858
|
relationshipECClassIds.add(row.ECInstanceId);
|
|
1829
1859
|
}
|
|
1830
1860
|
// For later use when processing deletes.
|
|
@@ -1848,7 +1878,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1848
1878
|
disableSchemaCheck: true,
|
|
1849
1879
|
});
|
|
1850
1880
|
const csAdaptor = new core_backend_1.ChangesetECAdaptor(csReader);
|
|
1851
|
-
const ecChangeUnifier = new core_backend_1.PartialECChangeUnifier();
|
|
1881
|
+
const ecChangeUnifier = new core_backend_1.PartialECChangeUnifier(this.sourceDb);
|
|
1852
1882
|
while (csAdaptor.step()) {
|
|
1853
1883
|
ecChangeUnifier.appendFrom(csAdaptor);
|
|
1854
1884
|
}
|
|
@@ -1906,7 +1936,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1906
1936
|
async processDeletedOp(change, mapOfDeletedElemIdToScopeEsas, isRelationship, alreadyImportedElementInserts, alreadyImportedModelInserts) {
|
|
1907
1937
|
// we need a connected iModel with changes to remap elements with deletions
|
|
1908
1938
|
const notConnectedModel = this.sourceDb.iTwinId === undefined;
|
|
1909
|
-
const noChanges = this.
|
|
1939
|
+
const noChanges = (await this.getSynchronizationVersion()).index ===
|
|
1940
|
+
this.sourceDb.changeset.index &&
|
|
1910
1941
|
(this.exporter.sourceDbChanges === undefined ||
|
|
1911
1942
|
!this.exporter.sourceDbChanges.hasChanges);
|
|
1912
1943
|
if (notConnectedModel || noChanges)
|
|
@@ -1915,7 +1946,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1915
1946
|
* if our ChangedECInstance is in the provenanceDb, then we can use the ids we find in the ChangedECInstance to query for ESAs.
|
|
1916
1947
|
* This is because the ESAs are stored on an element Id thats present in the provenanceDb.
|
|
1917
1948
|
*/
|
|
1918
|
-
const changeDataInProvenanceDb = this.sourceDb === this.
|
|
1949
|
+
const changeDataInProvenanceDb = this.sourceDb === (await this.getProvenanceDb());
|
|
1919
1950
|
const getTargetIdFromSourceId = async (id) => {
|
|
1920
1951
|
let identifierValue;
|
|
1921
1952
|
let element;
|
|
@@ -1931,7 +1962,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1931
1962
|
this.targetScopeElementId,
|
|
1932
1963
|
core_backend_1.ExternalSourceAspect.Kind.Element,
|
|
1933
1964
|
id,
|
|
1934
|
-
]))) {
|
|
1965
|
+
]), { usePrimaryConn: true })) {
|
|
1935
1966
|
identifierValue = row.Identifier;
|
|
1936
1967
|
}
|
|
1937
1968
|
identifierValue =
|
|
@@ -1944,7 +1975,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1944
1975
|
}
|
|
1945
1976
|
// Check for targetId using sourceId's fedguid if we didn't find an esa.
|
|
1946
1977
|
if (fedGuid) {
|
|
1947
|
-
const targetId = this.
|
|
1978
|
+
const targetId = this.targetDb.elements.getIdFromFederationGuid(fedGuid);
|
|
1948
1979
|
return targetId;
|
|
1949
1980
|
}
|
|
1950
1981
|
return undefined;
|
|
@@ -1957,20 +1988,20 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1957
1988
|
const sourceIdOfRelationshipInTarget = await getTargetIdFromSourceId(sourceIdOfRelationshipInSource);
|
|
1958
1989
|
const targetIdOfRelationshipInTarget = await getTargetIdFromSourceId(targetIdOfRelationshipInSource);
|
|
1959
1990
|
if (sourceIdOfRelationshipInTarget && targetIdOfRelationshipInTarget) {
|
|
1960
|
-
this._deletedSourceRelationshipData
|
|
1991
|
+
this._deletedSourceRelationshipData?.set(changedInstanceId, {
|
|
1961
1992
|
classFullName: classFullName ?? "",
|
|
1962
1993
|
sourceIdInTarget: sourceIdOfRelationshipInTarget,
|
|
1963
1994
|
targetIdInTarget: targetIdOfRelationshipInTarget,
|
|
1964
1995
|
});
|
|
1965
1996
|
}
|
|
1966
|
-
else if (this.sourceDb === this.
|
|
1967
|
-
const relProvenance = this._queryProvenanceForRelationship(changedInstanceId, {
|
|
1997
|
+
else if (this.sourceDb === (await this.getProvenanceSourceDb())) {
|
|
1998
|
+
const relProvenance = await this._queryProvenanceForRelationship(changedInstanceId, {
|
|
1968
1999
|
classFullName: classFullName ?? "",
|
|
1969
2000
|
sourceId: sourceIdOfRelationshipInSource,
|
|
1970
2001
|
targetId: targetIdOfRelationshipInSource,
|
|
1971
2002
|
});
|
|
1972
2003
|
if (relProvenance && relProvenance.relationshipId)
|
|
1973
|
-
this._deletedSourceRelationshipData
|
|
2004
|
+
this._deletedSourceRelationshipData?.set(changedInstanceId, {
|
|
1974
2005
|
classFullName: classFullName ?? "",
|
|
1975
2006
|
relId: relProvenance.relationshipId,
|
|
1976
2007
|
provenanceAspectId: relProvenance.aspectId,
|
|
@@ -1979,14 +2010,18 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1979
2010
|
}
|
|
1980
2011
|
else {
|
|
1981
2012
|
let targetId = await getTargetIdFromSourceId(changedInstanceId);
|
|
1982
|
-
if (targetId === undefined &&
|
|
1983
|
-
|
|
2013
|
+
if (targetId === undefined &&
|
|
2014
|
+
this.sourceDb === (await this.getProvenanceSourceDb())) {
|
|
2015
|
+
targetId = await this._queryProvenanceForElement(changedInstanceId);
|
|
1984
2016
|
}
|
|
1985
2017
|
// since we are processing one changeset at a time, we can see local source deletes
|
|
1986
2018
|
// of entities that were never synced and can be safely ignored
|
|
1987
2019
|
const deletionNotInTarget = !targetId;
|
|
1988
2020
|
if (deletionNotInTarget)
|
|
1989
2021
|
return;
|
|
2022
|
+
if (targetId === undefined) {
|
|
2023
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "targetId should be acquired from source id or element provenance");
|
|
2024
|
+
}
|
|
1990
2025
|
this.context.remapElement(changedInstanceId, targetId);
|
|
1991
2026
|
// If an entity insert and an entity delete both point to the same entity in target iModel, that means that entity was recreated.
|
|
1992
2027
|
// In such case an entity update will be triggered and we no longer need to delete the entity.
|
|
@@ -2005,7 +2040,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2005
2040
|
this._sourceChangeDataState = "unconnected";
|
|
2006
2041
|
return;
|
|
2007
2042
|
}
|
|
2008
|
-
const
|
|
2043
|
+
const syncVersion = await this.getSynchronizationVersion();
|
|
2044
|
+
const noChanges = syncVersion.index === this.sourceDb.changeset.index;
|
|
2009
2045
|
if (noChanges) {
|
|
2010
2046
|
this._sourceChangeDataState = "no-changes";
|
|
2011
2047
|
this._csFileProps = [];
|
|
@@ -2014,34 +2050,29 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2014
2050
|
const startChangeset = "startChangeset" in args ? args.startChangeset : undefined;
|
|
2015
2051
|
// NOTE: that we do NOT download the changesummary for the last transformed version, we want
|
|
2016
2052
|
// to ignore those already processed changes
|
|
2017
|
-
const startChangesetIndexOrId = startChangeset?.index ??
|
|
2018
|
-
startChangeset?.id ??
|
|
2019
|
-
this.synchronizationVersion.index + 1;
|
|
2053
|
+
const startChangesetIndexOrId = startChangeset?.index ?? startChangeset?.id ?? syncVersion.index + 1;
|
|
2020
2054
|
const endChangesetId = this.sourceDb.changeset.id;
|
|
2021
2055
|
const [startChangesetIndex, endChangesetIndex] = await Promise.all([startChangesetIndexOrId, endChangesetId].map(async (indexOrId) => typeof indexOrId === "number"
|
|
2022
2056
|
? indexOrId
|
|
2023
|
-
: core_backend_1.BriefcaseManager
|
|
2024
|
-
.queryChangeset({
|
|
2057
|
+
: core_backend_1.BriefcaseManager.queryChangeset({
|
|
2025
2058
|
iModelId: this.sourceDb.iModelId,
|
|
2026
|
-
// eslint-disable-next-line deprecation/deprecation
|
|
2027
2059
|
changeset: { id: indexOrId },
|
|
2028
|
-
})
|
|
2029
|
-
|
|
2030
|
-
const missingChangesets = startChangesetIndex > this.synchronizationVersion.index + 1;
|
|
2060
|
+
}).then((changeset) => changeset.index)));
|
|
2061
|
+
const missingChangesets = startChangesetIndex > syncVersion.index + 1;
|
|
2031
2062
|
if (!this._options.argsForProcessChanges
|
|
2032
2063
|
?.ignoreMissingChangesetsInSynchronizations &&
|
|
2033
|
-
startChangesetIndex !==
|
|
2034
|
-
|
|
2064
|
+
startChangesetIndex !== syncVersion.index + 1 &&
|
|
2065
|
+
syncVersion.index !== -1) {
|
|
2035
2066
|
throw Error(`synchronization is ${missingChangesets ? "missing changesets" : ""},` +
|
|
2036
2067
|
" startChangesetId should be" +
|
|
2037
2068
|
" exactly the first changeset *after* the previous synchronization to not miss data." +
|
|
2038
2069
|
` You specified '${startChangesetIndexOrId}' which is changeset #${startChangesetIndex}` +
|
|
2039
|
-
` but the previous synchronization for this
|
|
2040
|
-
` which is changeset #${
|
|
2041
|
-
` #${
|
|
2070
|
+
` but the previous synchronization for this targetScopeElem ${syncVersion.id}'` +
|
|
2071
|
+
` which is changeset #${syncVersion.index}. The transformer expected` +
|
|
2072
|
+
` #${syncVersion.index + 1}.`);
|
|
2042
2073
|
}
|
|
2043
2074
|
nodeAssert(this._targetScopeProvenanceProps, "_targetScopeProvenanceProps should be set by now");
|
|
2044
|
-
const changesetsToSkip = this.
|
|
2075
|
+
const changesetsToSkip = (await this.getIsReverseSynchronization())
|
|
2045
2076
|
? this._targetScopeProvenanceProps.jsonProperties
|
|
2046
2077
|
.pendingReverseSyncChangesetIndices
|
|
2047
2078
|
: this._targetScopeProvenanceProps.jsonProperties
|
|
@@ -2109,21 +2140,18 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2109
2140
|
else {
|
|
2110
2141
|
await this.exporter.exportModel(core_common_1.IModel.repositoryModelId);
|
|
2111
2142
|
}
|
|
2112
|
-
this.completePartiallyCommittedElements();
|
|
2143
|
+
await this.completePartiallyCommittedElements();
|
|
2113
2144
|
await this.exporter["exportAllAspects"](); // eslint-disable-line @typescript-eslint/dot-notation
|
|
2114
|
-
this.completePartiallyCommittedAspects();
|
|
2145
|
+
await this.completePartiallyCommittedAspects();
|
|
2115
2146
|
await this.exporter.exportRelationships(core_backend_1.ElementRefersToElements.classFullName);
|
|
2116
2147
|
if (this._options.forceExternalSourceAspectProvenance &&
|
|
2117
2148
|
this.shouldDetectDeletes()) {
|
|
2118
|
-
|
|
2119
|
-
await this.detectElementDeletes();
|
|
2120
|
-
// eslint-disable-next-line deprecation/deprecation
|
|
2121
|
-
await this.detectRelationshipDeletes();
|
|
2149
|
+
core_bentley_1.Logger.logWarning(loggerCategory, "This workflows was deprecated in v1 and is no longer supported");
|
|
2122
2150
|
}
|
|
2123
2151
|
if (this._options.optimizeGeometry)
|
|
2124
2152
|
this.importer.optimizeGeometry(this._options.optimizeGeometry);
|
|
2125
2153
|
this.importer.computeProjectExtents();
|
|
2126
|
-
this.finalizeTransformation();
|
|
2154
|
+
await this.finalizeTransformation();
|
|
2127
2155
|
}
|
|
2128
2156
|
/** previous provenance, either a federation guid, a `${sourceFedGuid}/${targetFedGuid}` pair, or required aspect props */
|
|
2129
2157
|
_lastProvenanceEntityInfo = nullLastProvenanceEntityInfo;
|
|
@@ -2151,13 +2179,13 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2151
2179
|
*/
|
|
2152
2180
|
async processChanges(options) {
|
|
2153
2181
|
// must wait for initialization of synchronization provenance data
|
|
2154
|
-
await this.exporter.exportChanges(this.getExportInitOpts(options));
|
|
2155
|
-
this.completePartiallyCommittedElements();
|
|
2156
|
-
this.completePartiallyCommittedAspects();
|
|
2182
|
+
await this.exporter.exportChanges(await this.getExportInitOpts(options));
|
|
2183
|
+
await this.completePartiallyCommittedElements();
|
|
2184
|
+
await this.completePartiallyCommittedAspects();
|
|
2157
2185
|
if (this._options.optimizeGeometry)
|
|
2158
2186
|
this.importer.optimizeGeometry(this._options.optimizeGeometry);
|
|
2159
2187
|
this.importer.computeProjectExtents();
|
|
2160
|
-
this.finalizeTransformation();
|
|
2188
|
+
await this.finalizeTransformation();
|
|
2161
2189
|
const defaultSaveTargetChanges = () => {
|
|
2162
2190
|
this.targetDb.saveChanges();
|
|
2163
2191
|
};
|
|
@@ -2166,7 +2194,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2166
2194
|
/** Changeset data must be initialized in order to build correct changeOptions.
|
|
2167
2195
|
* Call [[IModelTransformer.initialize]] for initialization of synchronization provenance data
|
|
2168
2196
|
*/
|
|
2169
|
-
getExportInitOpts(opts) {
|
|
2197
|
+
async getExportInitOpts(opts) {
|
|
2170
2198
|
if (!this._options.argsForProcessChanges)
|
|
2171
2199
|
return {};
|
|
2172
2200
|
const startChangeset = "startChangeset" in opts ? opts.startChangeset : undefined;
|
|
@@ -2180,7 +2208,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2180
2208
|
? { startChangeset }
|
|
2181
2209
|
: {
|
|
2182
2210
|
startChangeset: {
|
|
2183
|
-
index: this.
|
|
2211
|
+
index: (await this.getSynchronizationVersion()).index + 1,
|
|
2184
2212
|
},
|
|
2185
2213
|
}),
|
|
2186
2214
|
};
|
|
@@ -2261,11 +2289,11 @@ class TemplateModelCloner extends IModelTransformer {
|
|
|
2261
2289
|
return this._sourceIdToTargetIdMap; // return the sourceElementId -> targetElementId Map in case further post-processing is required.
|
|
2262
2290
|
}
|
|
2263
2291
|
/** Cloning from a template requires this override of onTransformElement. */
|
|
2264
|
-
onTransformElement(sourceElement) {
|
|
2292
|
+
async onTransformElement(sourceElement) {
|
|
2265
2293
|
const referenceIds = sourceElement.getReferenceIds();
|
|
2266
|
-
|
|
2294
|
+
for (const referenceId of referenceIds) {
|
|
2267
2295
|
// TODO: consider going through all definition elements at once and remapping them to themselves
|
|
2268
|
-
if (!core_backend_1.EntityReferences.isValid(this.context.findTargetEntityId(referenceId))) {
|
|
2296
|
+
if (!core_backend_1.EntityReferences.isValid(await this.context.findTargetEntityId(referenceId))) {
|
|
2269
2297
|
if (this.context.isBetweenIModels) {
|
|
2270
2298
|
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, `Remapping for source dependency ${referenceId} not found for target iModel`);
|
|
2271
2299
|
}
|
|
@@ -2280,8 +2308,8 @@ class TemplateModelCloner extends IModelTransformer {
|
|
|
2280
2308
|
}
|
|
2281
2309
|
}
|
|
2282
2310
|
}
|
|
2283
|
-
}
|
|
2284
|
-
const targetElementProps = super.onTransformElement(sourceElement);
|
|
2311
|
+
}
|
|
2312
|
+
const targetElementProps = await super.onTransformElement(sourceElement);
|
|
2285
2313
|
targetElementProps.federationGuid = core_bentley_1.Guid.createValue(); // clone from template should create a new federationGuid
|
|
2286
2314
|
targetElementProps.code = core_common_1.Code.createEmpty(); // clone from template should not maintain codes
|
|
2287
2315
|
if (sourceElement instanceof core_backend_1.GeometricElement) {
|
|
@@ -2294,23 +2322,9 @@ class TemplateModelCloner extends IModelTransformer {
|
|
|
2294
2322
|
targetElementProps.placement = placement;
|
|
2295
2323
|
}
|
|
2296
2324
|
}
|
|
2297
|
-
this._sourceIdToTargetIdMap
|
|
2325
|
+
this._sourceIdToTargetIdMap?.set(sourceElement.id, core_bentley_1.Id64.invalid); // keep track of (source) elementIds from the template model, but the target hasn't been inserted yet
|
|
2298
2326
|
return targetElementProps;
|
|
2299
2327
|
}
|
|
2300
2328
|
}
|
|
2301
2329
|
exports.TemplateModelCloner = TemplateModelCloner;
|
|
2302
|
-
function queryElemFedGuid(db, elemId) {
|
|
2303
|
-
// eslint-disable-next-line @itwin/no-internal, deprecation/deprecation
|
|
2304
|
-
return db.withPreparedStatement(`
|
|
2305
|
-
SELECT FederationGuid
|
|
2306
|
-
FROM bis.Element
|
|
2307
|
-
WHERE ECInstanceId=?
|
|
2308
|
-
`, (stmt) => {
|
|
2309
|
-
stmt.bindId(1, elemId);
|
|
2310
|
-
(0, core_bentley_1.assert)(stmt.step() === core_bentley_1.DbResult.BE_SQLITE_ROW);
|
|
2311
|
-
const result = stmt.getValue(0).getGuid();
|
|
2312
|
-
(0, core_bentley_1.assert)(stmt.step() === core_bentley_1.DbResult.BE_SQLITE_DONE);
|
|
2313
|
-
return result;
|
|
2314
|
-
});
|
|
2315
|
-
}
|
|
2316
2330
|
//# sourceMappingURL=IModelTransformer.js.map
|