@itwin/imodel-transformer 2.0.0-dev.6 → 2.0.0-dev.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/BranchProvenanceInitializer.js +8 -8
- package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +1 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.js +3 -3
- package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.js +13 -15
- package/lib/cjs/ECReferenceTypesCache.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 -12
- package/lib/cjs/EntityUnifier.js.map +1 -1
- package/lib/cjs/IModelCloneContext.d.ts +1 -1
- package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
- package/lib/cjs/IModelCloneContext.js +28 -23
- package/lib/cjs/IModelCloneContext.js.map +1 -1
- package/lib/cjs/IModelExporter.d.ts.map +1 -1
- package/lib/cjs/IModelExporter.js +36 -20
- package/lib/cjs/IModelExporter.js.map +1 -1
- package/lib/cjs/IModelTransformer.d.ts +12 -31
- package/lib/cjs/IModelTransformer.d.ts.map +1 -1
- package/lib/cjs/IModelTransformer.js +121 -217
- package/lib/cjs/IModelTransformer.js.map +1 -1
- package/package.json +15 -15
|
@@ -110,7 +110,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
110
110
|
* @param dbToQuery db to run the query on for scope external source
|
|
111
111
|
* @param aspectProps aspectProps to search for @see ExternalSourceAspectProps
|
|
112
112
|
*/
|
|
113
|
-
static queryScopeExternalSourceAspect(dbToQuery, aspectProps) {
|
|
113
|
+
static async queryScopeExternalSourceAspect(dbToQuery, aspectProps) {
|
|
114
114
|
const sql = `
|
|
115
115
|
SELECT ECInstanceId, Version, JsonProperties
|
|
116
116
|
FROM ${core_backend_1.ExternalSourceAspect.classFullName}
|
|
@@ -121,25 +121,19 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
121
121
|
LIMIT 1
|
|
122
122
|
`;
|
|
123
123
|
// if (aspectProps.scope === undefined) return undefined;
|
|
124
|
-
// const params = new QueryBinder()
|
|
124
|
+
// const params = new QueryBinder();
|
|
125
|
+
// params.bindId("elementId", aspectProps.element.id);
|
|
125
126
|
// params.bindId("scopeId", aspectProps.scope.id);
|
|
126
127
|
// params.bindString("kind", aspectProps.kind);
|
|
127
128
|
// params.bindString("identifier", aspectProps.identifier);
|
|
128
|
-
// const
|
|
129
|
-
// if (
|
|
130
|
-
// const aspectId =
|
|
131
|
-
// const
|
|
132
|
-
// const
|
|
133
|
-
// ? undefined
|
|
134
|
-
// : versionValue.getString();
|
|
135
|
-
// const jsonPropsValue = result.current[2];
|
|
136
|
-
// const jsonProperties = jsonPropsValue.isNull
|
|
137
|
-
// ? undefined
|
|
138
|
-
// : jsonPropsValue.getString();
|
|
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;
|
|
139
134
|
// return { aspectId, version, jsonProperties };
|
|
140
135
|
// }
|
|
141
|
-
//
|
|
142
|
-
// return undefined;
|
|
136
|
+
// return undefined;
|
|
143
137
|
// eslint-disable-next-line @itwin/no-internal, @typescript-eslint/no-deprecated
|
|
144
138
|
return dbToQuery.withPreparedStatement(sql, (statement) => {
|
|
145
139
|
statement.bindId("elementId", aspectProps.element.id);
|
|
@@ -169,7 +163,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
169
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.
|
|
170
164
|
* @returns "forward" or "reverse"
|
|
171
165
|
*/
|
|
172
|
-
static determineSyncType(sourceDb, targetDb,
|
|
166
|
+
static async determineSyncType(sourceDb, targetDb,
|
|
173
167
|
/** @see [[IModelTransformOptions.targetScopeElementId]] */
|
|
174
168
|
targetScopeElementId) {
|
|
175
169
|
const aspectProps = {
|
|
@@ -186,19 +180,19 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
186
180
|
jsonProperties: undefined,
|
|
187
181
|
};
|
|
188
182
|
/** First check if the targetDb is the branch (branch is the @see provenanceDb) */
|
|
189
|
-
const esaPropsFromTargetDb = this.queryScopeExternalSourceAspect(targetDb, aspectProps);
|
|
183
|
+
const esaPropsFromTargetDb = await this.queryScopeExternalSourceAspect(targetDb, aspectProps);
|
|
190
184
|
if (esaPropsFromTargetDb !== undefined) {
|
|
191
185
|
return "forward"; // we found an esa assuming targetDb is the provenanceDb/branch so this is a forward sync.
|
|
192
186
|
}
|
|
193
187
|
// Now check if the sourceDb is the branch
|
|
194
188
|
aspectProps.identifier = targetDb.iModelId;
|
|
195
|
-
const esaPropsFromSourceDb = this.queryScopeExternalSourceAspect(sourceDb, aspectProps);
|
|
189
|
+
const esaPropsFromSourceDb = await this.queryScopeExternalSourceAspect(sourceDb, aspectProps);
|
|
196
190
|
if (esaPropsFromSourceDb !== undefined) {
|
|
197
191
|
return "reverse"; // we found an esa assuming sourceDb is the provenanceDb/branch so this is a reverse sync.
|
|
198
192
|
}
|
|
199
193
|
throw new Error(this.noEsaSyncDirectionErrorMessage);
|
|
200
194
|
}
|
|
201
|
-
determineSyncType() {
|
|
195
|
+
async determineSyncType() {
|
|
202
196
|
if (this._isProvenanceInitTransform) {
|
|
203
197
|
return "forward";
|
|
204
198
|
}
|
|
@@ -206,7 +200,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
206
200
|
return "not-sync";
|
|
207
201
|
}
|
|
208
202
|
try {
|
|
209
|
-
return IModelTransformer.determineSyncType(this.sourceDb, this.targetDb, this.targetScopeElementId);
|
|
203
|
+
return await IModelTransformer.determineSyncType(this.sourceDb, this.targetDb, this.targetScopeElementId);
|
|
210
204
|
}
|
|
211
205
|
catch (err) {
|
|
212
206
|
if (err instanceof Error &&
|
|
@@ -217,14 +211,14 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
217
211
|
throw err;
|
|
218
212
|
}
|
|
219
213
|
}
|
|
220
|
-
|
|
214
|
+
async getIsReverseSynchronization() {
|
|
221
215
|
if (this._syncType === undefined)
|
|
222
|
-
this._syncType = this.determineSyncType();
|
|
216
|
+
this._syncType = await this.determineSyncType();
|
|
223
217
|
return this._syncType === "reverse";
|
|
224
218
|
}
|
|
225
|
-
|
|
219
|
+
async getIsForwardSynchronization() {
|
|
226
220
|
if (this._syncType === undefined)
|
|
227
|
-
this._syncType = this.determineSyncType();
|
|
221
|
+
this._syncType = await this.determineSyncType();
|
|
228
222
|
return this._syncType === "forward";
|
|
229
223
|
}
|
|
230
224
|
_changesetRanges = undefined;
|
|
@@ -371,14 +365,18 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
371
365
|
/** Return the IModelDb where IModelTransformer will store its provenance.
|
|
372
366
|
* @note This will be [[targetDb]] except when it is a reverse synchronization. In that case it be [[sourceDb]].
|
|
373
367
|
*/
|
|
374
|
-
|
|
375
|
-
return
|
|
368
|
+
async getProvenanceDb() {
|
|
369
|
+
return (await this.getIsReverseSynchronization())
|
|
370
|
+
? this.sourceDb
|
|
371
|
+
: this.targetDb;
|
|
376
372
|
}
|
|
377
373
|
/** Return the IModelDb where IModelTransformer looks for entities referred to by stored provenance.
|
|
378
374
|
* @note This will be [[sourceDb]] except when it is a reverse synchronization. In that case it be [[targetDb]].
|
|
379
375
|
*/
|
|
380
|
-
|
|
381
|
-
return
|
|
376
|
+
async getProvenanceSourceDb() {
|
|
377
|
+
return (await this.getIsReverseSynchronization())
|
|
378
|
+
? this.targetDb
|
|
379
|
+
: this.sourceDb;
|
|
382
380
|
}
|
|
383
381
|
/** Create an ExternalSourceAspectProps in a standard way for an Element in an iModel --> iModel transformation. */
|
|
384
382
|
static initElementProvenanceOptions(sourceElementId, targetElementId, args) {
|
|
@@ -445,9 +443,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
445
443
|
*/
|
|
446
444
|
_forceOldRelationshipProvenanceMethod = false;
|
|
447
445
|
/** Create an ExternalSourceAspectProps in a standard way for an Element in an iModel --> iModel transformation. */
|
|
448
|
-
initElementProvenance(sourceElementId, targetElementId) {
|
|
446
|
+
async initElementProvenance(sourceElementId, targetElementId) {
|
|
449
447
|
return IModelTransformer.initElementProvenanceOptions(sourceElementId, targetElementId, {
|
|
450
|
-
isReverseSynchronization: this.
|
|
448
|
+
isReverseSynchronization: await this.getIsReverseSynchronization(),
|
|
451
449
|
targetScopeElementId: this.targetScopeElementId,
|
|
452
450
|
sourceDb: this.sourceDb,
|
|
453
451
|
targetDb: this.targetDb,
|
|
@@ -459,10 +457,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
459
457
|
* The ECInstanceId of the relationship in the branch iModel will be stored in the JsonProperties of the ExternalSourceAspect.
|
|
460
458
|
*/
|
|
461
459
|
async initRelationshipProvenance(sourceRelationship, targetRelInstanceId) {
|
|
462
|
-
return
|
|
460
|
+
return IModelTransformer.initRelationshipProvenanceOptions(sourceRelationship.id, targetRelInstanceId, {
|
|
463
461
|
sourceDb: this.sourceDb,
|
|
464
462
|
targetDb: this.targetDb,
|
|
465
|
-
isReverseSynchronization: this.
|
|
463
|
+
isReverseSynchronization: await this.getIsReverseSynchronization(),
|
|
466
464
|
targetScopeElementId: this.targetScopeElementId,
|
|
467
465
|
forceOldRelationshipProvenanceMethod: this._forceOldRelationshipProvenanceMethod,
|
|
468
466
|
});
|
|
@@ -490,13 +488,13 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
490
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".
|
|
491
489
|
* @throws if the version is not found in a preexisting scope aspect and @see [[IModelTransformOptions.branchRelationshipDataBehavior]] !== "unsafe-migrate"
|
|
492
490
|
*/
|
|
493
|
-
|
|
491
|
+
async getSynchronizationVersion() {
|
|
494
492
|
if (this._cachedSynchronizationVersion === undefined) {
|
|
495
|
-
const provenanceScopeAspect = this.tryGetProvenanceScopeAspect();
|
|
493
|
+
const provenanceScopeAspect = await this.tryGetProvenanceScopeAspect();
|
|
496
494
|
if (!provenanceScopeAspect) {
|
|
497
495
|
return { index: -1, id: "" }; // first synchronization.
|
|
498
496
|
}
|
|
499
|
-
const version = this.
|
|
497
|
+
const version = (await this.getIsReverseSynchronization())
|
|
500
498
|
? JSON.parse(provenanceScopeAspect.jsonProperties ?? "{}").reverseSyncVersion
|
|
501
499
|
: provenanceScopeAspect.version;
|
|
502
500
|
if (!version &&
|
|
@@ -518,17 +516,17 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
518
516
|
* @returns provenance scope aspect if it exists in the provenanceDb.
|
|
519
517
|
* Provenance scope aspect is created and inserted into provenanceDb when [[initScopeProvenance]] is invoked.
|
|
520
518
|
*/
|
|
521
|
-
tryGetProvenanceScopeAspect() {
|
|
522
|
-
const scopeProvenanceAspectProps = IModelTransformer.queryScopeExternalSourceAspect(this.
|
|
519
|
+
async tryGetProvenanceScopeAspect() {
|
|
520
|
+
const scopeProvenanceAspectProps = await IModelTransformer.queryScopeExternalSourceAspect(await this.getProvenanceDb(), {
|
|
523
521
|
id: undefined,
|
|
524
522
|
classFullName: core_backend_1.ExternalSourceAspect.classFullName,
|
|
525
523
|
scope: { id: core_common_1.IModel.rootSubjectId },
|
|
526
524
|
kind: core_backend_1.ExternalSourceAspect.Kind.Scope,
|
|
527
525
|
element: { id: this.targetScopeElementId ?? core_common_1.IModel.rootSubjectId },
|
|
528
|
-
identifier: this.
|
|
526
|
+
identifier: (await this.getProvenanceSourceDb()).iModelId,
|
|
529
527
|
});
|
|
530
528
|
return scopeProvenanceAspectProps !== undefined
|
|
531
|
-
? this.
|
|
529
|
+
? (await this.getProvenanceDb()).elements.getAspect(scopeProvenanceAspectProps.aspectId)
|
|
532
530
|
: undefined;
|
|
533
531
|
}
|
|
534
532
|
/**
|
|
@@ -538,6 +536,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
538
536
|
* if this was a [BriefcaseDb]($backend)
|
|
539
537
|
*/
|
|
540
538
|
async initScopeProvenance() {
|
|
539
|
+
const provenanceDb = await this.getProvenanceDb();
|
|
540
|
+
const sourceProvenanceDb = await this.getProvenanceSourceDb();
|
|
541
541
|
const aspectProps = {
|
|
542
542
|
id: undefined,
|
|
543
543
|
version: undefined,
|
|
@@ -547,11 +547,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
547
547
|
relClassName: core_backend_1.ElementOwnsExternalSourceAspects.classFullName,
|
|
548
548
|
},
|
|
549
549
|
scope: { id: core_common_1.IModel.rootSubjectId }, // the root Subject scopes scope elements
|
|
550
|
-
identifier:
|
|
550
|
+
identifier: sourceProvenanceDb.iModelId,
|
|
551
551
|
kind: core_backend_1.ExternalSourceAspect.Kind.Scope,
|
|
552
552
|
jsonProperties: undefined,
|
|
553
553
|
};
|
|
554
|
-
const foundEsaProps = IModelTransformer.queryScopeExternalSourceAspect(
|
|
554
|
+
const foundEsaProps = await IModelTransformer.queryScopeExternalSourceAspect(provenanceDb, aspectProps); // this query includes "identifier"
|
|
555
555
|
if (foundEsaProps === undefined) {
|
|
556
556
|
aspectProps.version = ""; // empty since never before transformed. Will be updated in [[finalizeTransformation]]
|
|
557
557
|
aspectProps.jsonProperties = {
|
|
@@ -572,7 +572,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
572
572
|
params.bindId("elementId", aspectProps.element.id);
|
|
573
573
|
params.bindId("scopeId", aspectProps.scope.id); // this scope.id can never be invalid, we create it above
|
|
574
574
|
params.bindString("kind", aspectProps.kind);
|
|
575
|
-
const reader =
|
|
575
|
+
const reader = provenanceDb.createQueryReader(sql, params, {
|
|
576
576
|
usePrimaryConn: true,
|
|
577
577
|
});
|
|
578
578
|
const hasConflictingScope = await reader.step();
|
|
@@ -580,7 +580,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
580
580
|
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, "Provenance scope conflict");
|
|
581
581
|
}
|
|
582
582
|
if (!this._options.noProvenance) {
|
|
583
|
-
const id =
|
|
583
|
+
const id = provenanceDb.elements.insertAspect({
|
|
584
584
|
...aspectProps,
|
|
585
585
|
jsonProperties: JSON.stringify(aspectProps.jsonProperties),
|
|
586
586
|
});
|
|
@@ -600,7 +600,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
600
600
|
const oldProps = JSON.parse(JSON.stringify(aspectProps));
|
|
601
601
|
if (this.handleUnsafeMigrate(aspectProps)) {
|
|
602
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 });
|
|
603
|
-
|
|
603
|
+
provenanceDb.elements.updateAspect({
|
|
604
604
|
...aspectProps,
|
|
605
605
|
jsonProperties: JSON.stringify(aspectProps.jsonProperties),
|
|
606
606
|
});
|
|
@@ -692,8 +692,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
692
692
|
// NOTE: if we exposed the native attach database support,
|
|
693
693
|
// we could get the intersection of fed guids in one query, not sure if it would be faster
|
|
694
694
|
// OR we could do a raw sqlite query...
|
|
695
|
-
const sourceReader = sourceDb.createQueryReader(elementIdByFedGuidQuery);
|
|
696
|
-
const targetReader = targetDb.createQueryReader(elementIdByFedGuidQuery);
|
|
695
|
+
const sourceReader = sourceDb.createQueryReader(elementIdByFedGuidQuery, undefined, { usePrimaryConn: true });
|
|
696
|
+
const targetReader = targetDb.createQueryReader(elementIdByFedGuidQuery, undefined, { usePrimaryConn: true });
|
|
697
697
|
let hasSourceRow = await sourceReader.step();
|
|
698
698
|
let hasTargetRow = await targetReader.step();
|
|
699
699
|
while (hasSourceRow && hasTargetRow) {
|
|
@@ -730,7 +730,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
730
730
|
const params = new core_common_1.QueryBinder();
|
|
731
731
|
params.bindId("scopeId", args.targetScopeElementId);
|
|
732
732
|
params.bindString("kind", core_backend_1.ExternalSourceAspect.Kind.Element);
|
|
733
|
-
const provenanceReader = args.provenanceDb.createQueryReader(provenanceAspectsQuery, params);
|
|
733
|
+
const provenanceReader = args.provenanceDb.createQueryReader(provenanceAspectsQuery, params, { usePrimaryConn: true });
|
|
734
734
|
for await (const row of provenanceReader) {
|
|
735
735
|
// ExternalSourceAspect.Identifier is of type string
|
|
736
736
|
const aspectIdentifier = row[0];
|
|
@@ -739,11 +739,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
739
739
|
}
|
|
740
740
|
}
|
|
741
741
|
async forEachTrackedElement(fn) {
|
|
742
|
-
return
|
|
743
|
-
provenanceSourceDb: this.
|
|
744
|
-
provenanceDb: this.
|
|
742
|
+
return IModelTransformer.forEachTrackedElement({
|
|
743
|
+
provenanceSourceDb: await this.getProvenanceSourceDb(),
|
|
744
|
+
provenanceDb: await this.getProvenanceDb(),
|
|
745
745
|
targetScopeElementId: this.targetScopeElementId,
|
|
746
|
-
isReverseSynchronization: this.
|
|
746
|
+
isReverseSynchronization: await this.getIsReverseSynchronization(),
|
|
747
747
|
fn,
|
|
748
748
|
skipPropagateChangesToRootElements: this._options.skipPropagateChangesToRootElements ?? true,
|
|
749
749
|
});
|
|
@@ -756,7 +756,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
756
756
|
* @returns the elementId that the ESA is stored on, esa.Element.Id
|
|
757
757
|
*/
|
|
758
758
|
async _queryProvenanceForElement(entityInProvenanceSourceId) {
|
|
759
|
-
// eslint-disable-next-line @itwin/no-internal, @typescript-eslint/no-deprecated
|
|
760
759
|
const sql = `
|
|
761
760
|
SELECT esa.Element.Id
|
|
762
761
|
FROM Bis.ExternalSourceAspect esa
|
|
@@ -768,7 +767,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
768
767
|
params.bindString(1, core_backend_1.ExternalSourceAspect.Kind.Element);
|
|
769
768
|
params.bindId(2, this.targetScopeElementId);
|
|
770
769
|
params.bindString(3, entityInProvenanceSourceId);
|
|
771
|
-
const result = this.
|
|
770
|
+
const result = (await this.getProvenanceDb()).createQueryReader(sql, params, {
|
|
771
|
+
usePrimaryConn: true,
|
|
772
|
+
});
|
|
772
773
|
if (await result.step()) {
|
|
773
774
|
return result.current.id;
|
|
774
775
|
}
|
|
@@ -796,7 +797,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
796
797
|
params.bindString(1, core_backend_1.ExternalSourceAspect.Kind.Relationship);
|
|
797
798
|
params.bindId(2, this.targetScopeElementId);
|
|
798
799
|
params.bindString(3, entityInProvenanceSourceId);
|
|
799
|
-
const result = this.
|
|
800
|
+
const result = (await this.getProvenanceDb()).createQueryReader(sql, params, {
|
|
801
|
+
usePrimaryConn: true,
|
|
802
|
+
});
|
|
800
803
|
if (await result.step()) {
|
|
801
804
|
const aspectId = result.current.id;
|
|
802
805
|
const provenanceRelInstId = result.current.provenanceRelInstId;
|
|
@@ -830,7 +833,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
830
833
|
params.bindId(1, targetRelInfo.sourceId);
|
|
831
834
|
params.bindId(2, targetRelInfo.targetId);
|
|
832
835
|
params.bindId(3, await this._targetClassNameToClassId(sourceRelInfo.classFullName));
|
|
833
|
-
const result = this.targetDb.createQueryReader(sql, params
|
|
836
|
+
const result = this.targetDb.createQueryReader(sql, params, {
|
|
837
|
+
usePrimaryConn: true,
|
|
838
|
+
});
|
|
834
839
|
if (await result.step())
|
|
835
840
|
return result.current.id;
|
|
836
841
|
else
|
|
@@ -860,21 +865,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
860
865
|
: classFullName.split(":");
|
|
861
866
|
params.bindString(1, schemaName);
|
|
862
867
|
params.bindString(2, className);
|
|
863
|
-
const result = db.createQueryReader(sql, params);
|
|
868
|
+
const result = db.createQueryReader(sql, params, { usePrimaryConn: true });
|
|
864
869
|
if (await result.step())
|
|
865
870
|
return result.current.id;
|
|
866
871
|
(0, core_bentley_1.assert)(false, "relationship was not found");
|
|
867
872
|
}
|
|
868
|
-
// Deprecate?
|
|
869
|
-
async _queryElemIdByFedGuid(db, fedGuid) {
|
|
870
|
-
const sql = "SELECT ECInstanceId FROM Bis.Element WHERE FederationGuid=:fedGuid";
|
|
871
|
-
const params = new core_common_1.QueryBinder().bindString("fedGuid", fedGuid);
|
|
872
|
-
const reader = db.createQueryReader(sql, params);
|
|
873
|
-
if (await reader.step()) {
|
|
874
|
-
return reader.current.ecinstanceid;
|
|
875
|
-
}
|
|
876
|
-
return undefined;
|
|
877
|
-
}
|
|
878
873
|
/** Returns `true` if *brute force* delete detections should be run.
|
|
879
874
|
* @note This is only called if [[IModelTransformOptions.forceExternalSourceAspectProvenance]] option is true
|
|
880
875
|
* @note Not relevant for [[process]] when [[IModelTransformOptions.argsForProcessChanges]] are provided and change history is known.
|
|
@@ -883,44 +878,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
883
878
|
nodeAssert(this._syncType !== undefined);
|
|
884
879
|
return this._syncType === "not-sync";
|
|
885
880
|
}
|
|
886
|
-
/**
|
|
887
|
-
* Detect Element deletes using ExternalSourceAspects in the target iModel and a *brute force* comparison against Elements
|
|
888
|
-
* in the source iModel.
|
|
889
|
-
* @deprecated in 1.x. Do not use this. // FIXME<MIKE>: how to better explain this?
|
|
890
|
-
* This method is only called during [[process]] when [[IModelTransformOptions.argsForProcessChanges]] is undefined and the option
|
|
891
|
-
* [[IModelTransformOptions.forceExternalSourceAspectProvenance]] is enabled. It is not
|
|
892
|
-
* necessary when calling [[process]] with [[IModelTransformOptions.argsForProcessChanges]] defined, since changeset information is sufficient.
|
|
893
|
-
* @note you do not need to call this directly unless processing a subset of an iModel.
|
|
894
|
-
* @throws [[IModelError]] If the required provenance information is not available to detect deletes.
|
|
895
|
-
*/
|
|
896
|
-
async detectElementDeletes() {
|
|
897
|
-
const sql = `
|
|
898
|
-
SELECT Identifier, Element.Id
|
|
899
|
-
FROM BisCore.ExternalSourceAspect
|
|
900
|
-
WHERE Scope.Id=:scopeId
|
|
901
|
-
AND Kind=:kind
|
|
902
|
-
`;
|
|
903
|
-
nodeAssert(!this.isReverseSynchronization, "synchronizations with processChanges already detect element deletes, don't call detectElementDeletes");
|
|
904
|
-
// Reported issue: https://github.com/iTwin/itwinjs-core/issues/7989
|
|
905
|
-
// eslint-disable-next-line @itwin/no-internal, @typescript-eslint/no-deprecated
|
|
906
|
-
this.provenanceDb.withPreparedStatement(sql, (stmt) => {
|
|
907
|
-
stmt.bindId("scopeId", this.targetScopeElementId);
|
|
908
|
-
stmt.bindString("kind", core_backend_1.ExternalSourceAspect.Kind.Element);
|
|
909
|
-
while (core_bentley_1.DbResult.BE_SQLITE_ROW === stmt.step()) {
|
|
910
|
-
// ExternalSourceAspect.Identifier is of type string
|
|
911
|
-
const aspectIdentifier = stmt.getValue(0).getString();
|
|
912
|
-
if (!core_bentley_1.Id64.isValidId64(aspectIdentifier)) {
|
|
913
|
-
continue;
|
|
914
|
-
}
|
|
915
|
-
const targetElemId = stmt.getValue(1).getId();
|
|
916
|
-
const wasDeletedInSource = !EntityUnifier_1.EntityUnifier.exists(this.sourceDb, {
|
|
917
|
-
entityReference: `e${aspectIdentifier}`,
|
|
918
|
-
});
|
|
919
|
-
if (wasDeletedInSource)
|
|
920
|
-
this.importer.deleteElement(targetElemId);
|
|
921
|
-
}
|
|
922
|
-
});
|
|
923
|
-
}
|
|
924
881
|
/** Transform the specified sourceElement into ElementProps for the target iModel.
|
|
925
882
|
* @param sourceElement The Element from the source iModel to transform.
|
|
926
883
|
* @returns ElementProps for the target iModel.
|
|
@@ -929,7 +886,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
929
886
|
*/
|
|
930
887
|
async onTransformElement(sourceElement) {
|
|
931
888
|
core_bentley_1.Logger.logTrace(loggerCategory, `onTransformElement(${sourceElement.id}) "${sourceElement.getDisplayLabel()}"`);
|
|
932
|
-
const targetElementProps = this.context.cloneElement(sourceElement, { binaryGeometry: this._options.cloneUsingBinaryGeometry });
|
|
889
|
+
const targetElementProps = await this.context.cloneElement(sourceElement, { binaryGeometry: this._options.cloneUsingBinaryGeometry });
|
|
933
890
|
// Special case: source element is the root subject
|
|
934
891
|
if (sourceElement.id === core_common_1.IModel.rootSubjectId) {
|
|
935
892
|
const targetElementId = this.context.findTargetElementId(sourceElement.id);
|
|
@@ -1090,13 +1047,13 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1090
1047
|
allReferencesExist = false;
|
|
1091
1048
|
}
|
|
1092
1049
|
if (this._options.danglingReferencesBehavior === "reject") {
|
|
1093
|
-
this.assertReferenceExistsInSource(referenceId, entity);
|
|
1050
|
+
await this.assertReferenceExistsInSource(referenceId, entity);
|
|
1094
1051
|
}
|
|
1095
1052
|
}
|
|
1096
1053
|
return allReferencesExist;
|
|
1097
1054
|
}
|
|
1098
|
-
assertReferenceExistsInSource(referenceId, entity) {
|
|
1099
|
-
const referencedExistsInSource = EntityUnifier_1.EntityUnifier.exists(this.sourceDb, {
|
|
1055
|
+
async assertReferenceExistsInSource(referenceId, entity) {
|
|
1056
|
+
const referencedExistsInSource = await EntityUnifier_1.EntityUnifier.exists(this.sourceDb, {
|
|
1100
1057
|
entityReference: referenceId,
|
|
1101
1058
|
});
|
|
1102
1059
|
if (!referencedExistsInSource) {
|
|
@@ -1176,7 +1133,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1176
1133
|
.flat();
|
|
1177
1134
|
if (unresolvedReferences.length > 0) {
|
|
1178
1135
|
for (const reference of unresolvedReferences) {
|
|
1179
|
-
const processState = this.getElemTransformState(reference);
|
|
1136
|
+
const processState = await this.getElemTransformState(reference);
|
|
1180
1137
|
// must export element first
|
|
1181
1138
|
if (processState.needsElemImport)
|
|
1182
1139
|
await this.exporter.exportElement(reference);
|
|
@@ -1185,16 +1142,16 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1185
1142
|
}
|
|
1186
1143
|
}
|
|
1187
1144
|
}
|
|
1188
|
-
getElemTransformState(elementId) {
|
|
1189
|
-
const dbHasModel = (db, id) => {
|
|
1145
|
+
async getElemTransformState(elementId) {
|
|
1146
|
+
const dbHasModel = async (db, id) => {
|
|
1190
1147
|
const maybeModelId = core_backend_1.EntityReferences.fromEntityType(id, core_common_1.ConcreteEntityTypes.Model);
|
|
1191
1148
|
return EntityUnifier_1.EntityUnifier.exists(db, { entityReference: maybeModelId });
|
|
1192
1149
|
};
|
|
1193
|
-
const isSubModeled = dbHasModel(this.sourceDb, elementId);
|
|
1150
|
+
const isSubModeled = await dbHasModel(this.sourceDb, elementId);
|
|
1194
1151
|
const idOfElemInTarget = this.context.findTargetElementId(elementId);
|
|
1195
1152
|
const isElemInTarget = core_bentley_1.Id64.invalid !== idOfElemInTarget;
|
|
1196
1153
|
const needsModelImport = isSubModeled &&
|
|
1197
|
-
(!isElemInTarget || !dbHasModel(this.targetDb, idOfElemInTarget));
|
|
1154
|
+
(!isElemInTarget || !(await dbHasModel(this.targetDb, idOfElemInTarget)));
|
|
1198
1155
|
return { needsElemImport: !isElemInTarget, needsModelImport };
|
|
1199
1156
|
}
|
|
1200
1157
|
/** Override of [IModelExportHandler.onExportElement]($transformer) that imports an element into the target iModel when it is exported from the source iModel.
|
|
@@ -1288,20 +1245,21 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1288
1245
|
// physical consolidation is an example of a 'joining' transform
|
|
1289
1246
|
// FIXME: verify at finalization time that we don't lose provenance on new elements
|
|
1290
1247
|
// FIXME: make public and improve `initElementProvenance` API for usage by consolidators
|
|
1248
|
+
const provenanceDb = await this.getProvenanceDb();
|
|
1291
1249
|
if (!this._options.noProvenance) {
|
|
1292
1250
|
let provenance = this._options.forceExternalSourceAspectProvenance ||
|
|
1293
1251
|
this._elementsWithExplicitlyTrackedProvenance.has(sourceElement.id)
|
|
1294
1252
|
? undefined
|
|
1295
1253
|
: sourceElement.federationGuid;
|
|
1296
1254
|
if (!provenance) {
|
|
1297
|
-
const aspectProps = this.initElementProvenance(sourceElement.id, targetElementProps.id);
|
|
1298
|
-
const foundEsaProps = IModelTransformer.queryScopeExternalSourceAspect(
|
|
1255
|
+
const aspectProps = await this.initElementProvenance(sourceElement.id, targetElementProps.id);
|
|
1256
|
+
const foundEsaProps = await IModelTransformer.queryScopeExternalSourceAspect(provenanceDb, aspectProps);
|
|
1299
1257
|
if (foundEsaProps === undefined)
|
|
1300
|
-
aspectProps.id =
|
|
1258
|
+
aspectProps.id = provenanceDb.elements.insertAspect(aspectProps);
|
|
1301
1259
|
else {
|
|
1302
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.
|
|
1303
1261
|
aspectProps.id = foundEsaProps.aspectId;
|
|
1304
|
-
|
|
1262
|
+
provenanceDb.elements.updateAspect(aspectProps);
|
|
1305
1263
|
}
|
|
1306
1264
|
provenance = aspectProps;
|
|
1307
1265
|
}
|
|
@@ -1352,7 +1310,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1352
1310
|
`;
|
|
1353
1311
|
if (this.exporter.sourceDbChanges?.element.deleteIds.has(sourceModelId)) {
|
|
1354
1312
|
const params = new core_common_1.QueryBinder().bindId("targetModelId", targetModelId);
|
|
1355
|
-
const reader = this.targetDb.createQueryReader(sql, params
|
|
1313
|
+
const reader = this.targetDb.createQueryReader(sql, params, {
|
|
1314
|
+
usePrimaryConn: true,
|
|
1315
|
+
});
|
|
1356
1316
|
const isDefinitionPartition = await reader.step();
|
|
1357
1317
|
if (isDefinitionPartition) {
|
|
1358
1318
|
// Skipping model deletion because model's partition will also be deleted.
|
|
@@ -1410,12 +1370,12 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1410
1370
|
// import DefinitionModels first
|
|
1411
1371
|
const childDefinitionPartitionSql = `SELECT ECInstanceId FROM ${core_backend_1.DefinitionPartition.classFullName} WHERE Parent.Id=:subjectId`;
|
|
1412
1372
|
const params = new core_common_1.QueryBinder().bindId("subjectId", sourceSubjectId);
|
|
1413
|
-
for await (const row of this.sourceDb.createQueryReader(childDefinitionPartitionSql, params)) {
|
|
1373
|
+
for await (const row of this.sourceDb.createQueryReader(childDefinitionPartitionSql, params, { usePrimaryConn: true })) {
|
|
1414
1374
|
await this.processModel(row.id);
|
|
1415
1375
|
}
|
|
1416
1376
|
// import other partitions next
|
|
1417
1377
|
const childPartitionSql = `SELECT ECInstanceId FROM ${core_backend_1.InformationPartitionElement.classFullName} WHERE Parent.Id=:subjectId`;
|
|
1418
|
-
for await (const row of this.sourceDb.createQueryReader(childPartitionSql, params)) {
|
|
1378
|
+
for await (const row of this.sourceDb.createQueryReader(childPartitionSql, params, { usePrimaryConn: true })) {
|
|
1419
1379
|
const modelId = row.id;
|
|
1420
1380
|
const model = this.sourceDb.models.getModel(modelId);
|
|
1421
1381
|
if (!(model instanceof core_backend_1.DefinitionModel)) {
|
|
@@ -1424,7 +1384,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1424
1384
|
}
|
|
1425
1385
|
// recurse into child Subjects
|
|
1426
1386
|
const childSubjectSql = `SELECT ECInstanceId FROM ${core_backend_1.Subject.classFullName} WHERE Parent.Id=:subjectId`;
|
|
1427
|
-
for await (const row of this.sourceDb.createQueryReader(childSubjectSql, params)) {
|
|
1387
|
+
for await (const row of this.sourceDb.createQueryReader(childSubjectSql, params, { usePrimaryConn: true })) {
|
|
1428
1388
|
await this.processSubjectSubModels(row.id);
|
|
1429
1389
|
}
|
|
1430
1390
|
}
|
|
@@ -1466,7 +1426,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1466
1426
|
* Note that typically, the reverseSyncVersion is saved as the last changeset merged from the branch into master.
|
|
1467
1427
|
* 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.
|
|
1468
1428
|
*/
|
|
1469
|
-
updateSynchronizationVersion({ initializeReverseSyncVersion = false, } = {}) {
|
|
1429
|
+
async updateSynchronizationVersion({ initializeReverseSyncVersion = false, } = {}) {
|
|
1470
1430
|
const shouldSkipSyncVersionUpdate = !initializeReverseSyncVersion &&
|
|
1471
1431
|
this._sourceChangeDataState !== "has-changes";
|
|
1472
1432
|
if (shouldSkipSyncVersionUpdate)
|
|
@@ -1474,7 +1434,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1474
1434
|
nodeAssert(this._targetScopeProvenanceProps);
|
|
1475
1435
|
const sourceVersion = `${this.sourceDb.changeset.id};${this.sourceDb.changeset.index}`;
|
|
1476
1436
|
const targetVersion = `${this.targetDb.changeset.id};${this.targetDb.changeset.index}`;
|
|
1477
|
-
if (this.
|
|
1437
|
+
if (await this.getIsReverseSynchronization()) {
|
|
1478
1438
|
const oldVersion = this._targetScopeProvenanceProps.jsonProperties.reverseSyncVersion;
|
|
1479
1439
|
core_bentley_1.Logger.logInfo(loggerCategory, `updating reverse version from ${oldVersion} to ${sourceVersion}`);
|
|
1480
1440
|
this._targetScopeProvenanceProps.jsonProperties.reverseSyncVersion =
|
|
@@ -1504,7 +1464,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1504
1464
|
// Determine which keys to clear and update based on the synchronization direction
|
|
1505
1465
|
let syncChangesetsToClearKey;
|
|
1506
1466
|
let syncChangesetsToUpdateKey;
|
|
1507
|
-
if (this.
|
|
1467
|
+
if (await this.getIsReverseSynchronization()) {
|
|
1508
1468
|
syncChangesetsToClearKey = pendingReverseSyncChangesetIndicesKey;
|
|
1509
1469
|
syncChangesetsToUpdateKey = pendingSyncChangesetIndicesKey;
|
|
1510
1470
|
}
|
|
@@ -1523,7 +1483,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1523
1483
|
return csIndex > startingChangesetIndices.source;
|
|
1524
1484
|
});
|
|
1525
1485
|
// if reverse sync then we may have received provenance changes which should be marked as sync changes
|
|
1526
|
-
if (this.
|
|
1486
|
+
if (await this.getIsReverseSynchronization()) {
|
|
1527
1487
|
nodeAssert(this.sourceDb.changeset.index !== undefined, "changeset didn't exist");
|
|
1528
1488
|
for (let i = startingChangesetIndices.source + 1; i <= this.sourceDb.changeset.index + 1; i++)
|
|
1529
1489
|
jsonProps.pendingReverseSyncChangesetIndices.push(i);
|
|
@@ -1531,16 +1491,16 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1531
1491
|
core_bentley_1.Logger.logTrace(loggerCategory, `new pendingReverseSyncChanges: ${jsonProps.pendingReverseSyncChangesetIndices}`);
|
|
1532
1492
|
core_bentley_1.Logger.logTrace(loggerCategory, `new pendingSyncChanges: ${jsonProps.pendingSyncChangesetIndices}`);
|
|
1533
1493
|
}
|
|
1534
|
-
this.
|
|
1494
|
+
(await this.getProvenanceDb()).elements.updateAspect({
|
|
1535
1495
|
...this._targetScopeProvenanceProps,
|
|
1536
1496
|
jsonProperties: JSON.stringify(this._targetScopeProvenanceProps.jsonProperties),
|
|
1537
1497
|
});
|
|
1538
1498
|
this.clearCachedSynchronizationVersion();
|
|
1539
1499
|
}
|
|
1540
1500
|
// FIXME<MIKE>: is this necessary when manually using low level transform APIs? (document if so)
|
|
1541
|
-
finalizeTransformation() {
|
|
1501
|
+
async finalizeTransformation() {
|
|
1542
1502
|
this.importer.finalize();
|
|
1543
|
-
this.updateSynchronizationVersion({
|
|
1503
|
+
await this.updateSynchronizationVersion({
|
|
1544
1504
|
initializeReverseSyncVersion: this._isProvenanceInitTransform,
|
|
1545
1505
|
});
|
|
1546
1506
|
// TODO: ignore if we remove change cache usage
|
|
@@ -1578,6 +1538,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1578
1538
|
const targetFedGuid = this.sourceDb.elements.getFederationGuidFromId(sourceRelationship.targetId);
|
|
1579
1539
|
const targetRelationshipProps = this.onTransformRelationship(sourceRelationship);
|
|
1580
1540
|
const targetRelationshipInstanceId = this.importer.importRelationship(targetRelationshipProps);
|
|
1541
|
+
const provenanceDb = await this.getProvenanceDb();
|
|
1581
1542
|
if (!this._options.noProvenance &&
|
|
1582
1543
|
core_bentley_1.Id64.isValid(targetRelationshipInstanceId)) {
|
|
1583
1544
|
let provenance = !this._options.forceExternalSourceAspectProvenance
|
|
@@ -1585,10 +1546,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1585
1546
|
: undefined;
|
|
1586
1547
|
if (!provenance) {
|
|
1587
1548
|
const aspectProps = await this.initRelationshipProvenance(sourceRelationship, targetRelationshipInstanceId);
|
|
1588
|
-
const foundEsaProps = IModelTransformer.queryScopeExternalSourceAspect(
|
|
1549
|
+
const foundEsaProps = await IModelTransformer.queryScopeExternalSourceAspect(provenanceDb, aspectProps);
|
|
1589
1550
|
// 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).
|
|
1590
1551
|
if (undefined === foundEsaProps) {
|
|
1591
|
-
aspectProps.id =
|
|
1552
|
+
aspectProps.id = provenanceDb.elements.insertAspect(aspectProps);
|
|
1592
1553
|
}
|
|
1593
1554
|
provenance = aspectProps;
|
|
1594
1555
|
}
|
|
@@ -1598,7 +1559,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1598
1559
|
/** Override of [IModelExportHandler.onDeleteRelationship]($transformer) that is called when [IModelExporter]($transformer) detects that a [Relationship]($backend) has been deleted from the source iModel.
|
|
1599
1560
|
* This override propagates the delete to the target iModel via [IModelImporter.deleteRelationship]($transformer).
|
|
1600
1561
|
*/
|
|
1601
|
-
onDeleteRelationship(sourceRelInstanceId) {
|
|
1562
|
+
async onDeleteRelationship(sourceRelInstanceId) {
|
|
1602
1563
|
nodeAssert(this._deletedSourceRelationshipData, "should be defined at initialization by now");
|
|
1603
1564
|
const deletedRelData = this._deletedSourceRelationshipData.get(sourceRelInstanceId);
|
|
1604
1565
|
if (!deletedRelData) {
|
|
@@ -1619,7 +1580,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1619
1580
|
}
|
|
1620
1581
|
if (deletedRelData.provenanceAspectId) {
|
|
1621
1582
|
try {
|
|
1622
|
-
this.
|
|
1583
|
+
(await this.getProvenanceDb()).elements.deleteAspect(deletedRelData.provenanceAspectId);
|
|
1623
1584
|
}
|
|
1624
1585
|
catch (error) {
|
|
1625
1586
|
// This aspect may no longer exist if it was deleted at some other point during the transformation. This is fine.
|
|
@@ -1630,46 +1591,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1630
1591
|
}
|
|
1631
1592
|
}
|
|
1632
1593
|
_yieldManager = new core_bentley_1.YieldManager();
|
|
1633
|
-
/** Detect Relationship deletes using ExternalSourceAspects in the target iModel and a *brute force* comparison against relationships in the source iModel.
|
|
1634
|
-
* @deprecated in 1.x. Don't use this anymore
|
|
1635
|
-
* @see [[process]] with [[IModelTransformOptions.argsForProcessChanges]] provided.
|
|
1636
|
-
* @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.
|
|
1637
|
-
* @throws [[IModelError]] If the required provenance information is not available to detect deletes.
|
|
1638
|
-
*/
|
|
1639
|
-
async detectRelationshipDeletes() {
|
|
1640
|
-
if (this.isReverseSynchronization) {
|
|
1641
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "Cannot detect deletes when isReverseSynchronization=true");
|
|
1642
|
-
}
|
|
1643
|
-
const aspectDeleteIds = [];
|
|
1644
|
-
const sql = `
|
|
1645
|
-
SELECT ECInstanceId, Identifier, JsonProperties
|
|
1646
|
-
FROM ${core_backend_1.ExternalSourceAspect.classFullName} aspect
|
|
1647
|
-
WHERE aspect.Scope.Id=:scopeId
|
|
1648
|
-
AND aspect.Kind=:kind
|
|
1649
|
-
`;
|
|
1650
|
-
const params = new core_common_1.QueryBinder().bindId("scopeId", this.targetScopeElementId);
|
|
1651
|
-
params.bindString("kind", core_backend_1.ExternalSourceAspect.Kind.Relationship);
|
|
1652
|
-
for await (const row of this.targetDb.createQueryReader(sql, params)) {
|
|
1653
|
-
const sourceRelInstanceId = core_bentley_1.Id64.fromJSON(row[1]);
|
|
1654
|
-
if (undefined ===
|
|
1655
|
-
this.sourceDb.relationships.tryGetInstanceProps(core_backend_1.ElementRefersToElements.classFullName, sourceRelInstanceId)) {
|
|
1656
|
-
// this function exists only to support some in-imodel transformations, which must
|
|
1657
|
-
// use the old (external source aspect) provenance method anyway so we don't need to support
|
|
1658
|
-
// new provenance
|
|
1659
|
-
const json = JSON.parse(row[2]);
|
|
1660
|
-
const targetRelInstanceId = json.targetRelInstanceId ?? json.provenanceRelInstanceId;
|
|
1661
|
-
if (targetRelInstanceId) {
|
|
1662
|
-
this.importer.deleteRelationship({
|
|
1663
|
-
id: targetRelInstanceId,
|
|
1664
|
-
classFullName: core_backend_1.ElementRefersToElements.classFullName,
|
|
1665
|
-
});
|
|
1666
|
-
}
|
|
1667
|
-
aspectDeleteIds.push(row.id);
|
|
1668
|
-
}
|
|
1669
|
-
await this._yieldManager.allowYield();
|
|
1670
|
-
}
|
|
1671
|
-
this.targetDb.elements.deleteAspect(aspectDeleteIds);
|
|
1672
|
-
}
|
|
1673
1594
|
/** Transform the specified sourceRelationship into RelationshipProps for the target iModel.
|
|
1674
1595
|
* @param sourceRelationship The Relationship from the source iModel to be transformed.
|
|
1675
1596
|
* @returns RelationshipProps for the target iModel.
|
|
@@ -1710,7 +1631,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1710
1631
|
*/
|
|
1711
1632
|
async onExportElementMultiAspects(sourceAspects) {
|
|
1712
1633
|
// Transform source ElementMultiAspects into target ElementAspectProps
|
|
1713
|
-
const targetAspectPropsArray = sourceAspects.map(async (srcA) =>
|
|
1634
|
+
const targetAspectPropsArray = sourceAspects.map(async (srcA) => this.onTransformElementAspect(srcA));
|
|
1714
1635
|
sourceAspects.forEach(async (a) => {
|
|
1715
1636
|
if (!(await this.doAllReferencesExistInTarget(a))) {
|
|
1716
1637
|
this._partiallyCommittedAspectIds.add(a.id);
|
|
@@ -1883,7 +1804,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1883
1804
|
await this._tryInitChangesetData(this._options.argsForProcessChanges);
|
|
1884
1805
|
await this.context.initialize();
|
|
1885
1806
|
// need exporter initialized to do remapdeletedsourceentities.
|
|
1886
|
-
await this.exporter.initialize(this.getExportInitOpts(this._options.argsForProcessChanges ?? {}));
|
|
1807
|
+
await this.exporter.initialize(await this.getExportInitOpts(this._options.argsForProcessChanges ?? {}));
|
|
1887
1808
|
// 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).
|
|
1888
1809
|
await this.processChangesets();
|
|
1889
1810
|
this._initialized = true;
|
|
@@ -1909,11 +1830,11 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1909
1830
|
this._sourceChangeDataState = "has-changes";
|
|
1910
1831
|
}
|
|
1911
1832
|
const relationshipECClassIdsToSkip = new Set();
|
|
1912
|
-
for await (const row of this.sourceDb.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementDrivesElement)")) {
|
|
1833
|
+
for await (const row of this.sourceDb.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementDrivesElement)", undefined, { usePrimaryConn: true })) {
|
|
1913
1834
|
relationshipECClassIdsToSkip.add(row.ECInstanceId);
|
|
1914
1835
|
}
|
|
1915
1836
|
const relationshipECClassIds = new Set();
|
|
1916
|
-
for await (const row of this.sourceDb.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementRefersToElements)")) {
|
|
1837
|
+
for await (const row of this.sourceDb.createQueryReader("SELECT ECInstanceId FROM ECDbMeta.ECClassDef where ECInstanceId IS (BisCore.ElementRefersToElements)", undefined, { usePrimaryConn: true })) {
|
|
1917
1838
|
relationshipECClassIds.add(row.ECInstanceId);
|
|
1918
1839
|
}
|
|
1919
1840
|
// For later use when processing deletes.
|
|
@@ -1995,7 +1916,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1995
1916
|
async processDeletedOp(change, mapOfDeletedElemIdToScopeEsas, isRelationship, alreadyImportedElementInserts, alreadyImportedModelInserts) {
|
|
1996
1917
|
// we need a connected iModel with changes to remap elements with deletions
|
|
1997
1918
|
const notConnectedModel = this.sourceDb.iTwinId === undefined;
|
|
1998
|
-
const noChanges = this.
|
|
1919
|
+
const noChanges = (await this.getSynchronizationVersion()).index ===
|
|
1920
|
+
this.sourceDb.changeset.index &&
|
|
1999
1921
|
(this.exporter.sourceDbChanges === undefined ||
|
|
2000
1922
|
!this.exporter.sourceDbChanges.hasChanges);
|
|
2001
1923
|
if (notConnectedModel || noChanges)
|
|
@@ -2004,7 +1926,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2004
1926
|
* if our ChangedECInstance is in the provenanceDb, then we can use the ids we find in the ChangedECInstance to query for ESAs.
|
|
2005
1927
|
* This is because the ESAs are stored on an element Id thats present in the provenanceDb.
|
|
2006
1928
|
*/
|
|
2007
|
-
const changeDataInProvenanceDb = this.sourceDb === this.
|
|
1929
|
+
const changeDataInProvenanceDb = this.sourceDb === (await this.getProvenanceDb());
|
|
2008
1930
|
const getTargetIdFromSourceId = async (id) => {
|
|
2009
1931
|
let identifierValue;
|
|
2010
1932
|
let element;
|
|
@@ -2020,7 +1942,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2020
1942
|
this.targetScopeElementId,
|
|
2021
1943
|
core_backend_1.ExternalSourceAspect.Kind.Element,
|
|
2022
1944
|
id,
|
|
2023
|
-
]))) {
|
|
1945
|
+
]), { usePrimaryConn: true })) {
|
|
2024
1946
|
identifierValue = row.Identifier;
|
|
2025
1947
|
}
|
|
2026
1948
|
identifierValue =
|
|
@@ -2052,7 +1974,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2052
1974
|
targetIdInTarget: targetIdOfRelationshipInTarget,
|
|
2053
1975
|
});
|
|
2054
1976
|
}
|
|
2055
|
-
else if (this.sourceDb === this.
|
|
1977
|
+
else if (this.sourceDb === (await this.getProvenanceSourceDb())) {
|
|
2056
1978
|
const relProvenance = await this._queryProvenanceForRelationship(changedInstanceId, {
|
|
2057
1979
|
classFullName: classFullName ?? "",
|
|
2058
1980
|
sourceId: sourceIdOfRelationshipInSource,
|
|
@@ -2068,7 +1990,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2068
1990
|
}
|
|
2069
1991
|
else {
|
|
2070
1992
|
let targetId = await getTargetIdFromSourceId(changedInstanceId);
|
|
2071
|
-
if (targetId === undefined &&
|
|
1993
|
+
if (targetId === undefined &&
|
|
1994
|
+
this.sourceDb === (await this.getProvenanceSourceDb())) {
|
|
2072
1995
|
targetId = await this._queryProvenanceForElement(changedInstanceId);
|
|
2073
1996
|
}
|
|
2074
1997
|
// since we are processing one changeset at a time, we can see local source deletes
|
|
@@ -2077,7 +2000,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2077
2000
|
if (deletionNotInTarget)
|
|
2078
2001
|
return;
|
|
2079
2002
|
if (targetId === undefined) {
|
|
2080
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "targetId should
|
|
2003
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "targetId should be acquired from source id or element provenance");
|
|
2081
2004
|
}
|
|
2082
2005
|
this.context.remapElement(changedInstanceId, targetId);
|
|
2083
2006
|
// If an entity insert and an entity delete both point to the same entity in target iModel, that means that entity was recreated.
|
|
@@ -2097,7 +2020,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2097
2020
|
this._sourceChangeDataState = "unconnected";
|
|
2098
2021
|
return;
|
|
2099
2022
|
}
|
|
2100
|
-
const
|
|
2023
|
+
const syncVersion = await this.getSynchronizationVersion();
|
|
2024
|
+
const noChanges = syncVersion.index === this.sourceDb.changeset.index;
|
|
2101
2025
|
if (noChanges) {
|
|
2102
2026
|
this._sourceChangeDataState = "no-changes";
|
|
2103
2027
|
this._csFileProps = [];
|
|
@@ -2106,9 +2030,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2106
2030
|
const startChangeset = "startChangeset" in args ? args.startChangeset : undefined;
|
|
2107
2031
|
// NOTE: that we do NOT download the changesummary for the last transformed version, we want
|
|
2108
2032
|
// to ignore those already processed changes
|
|
2109
|
-
const startChangesetIndexOrId = startChangeset?.index ??
|
|
2110
|
-
startChangeset?.id ??
|
|
2111
|
-
this.synchronizationVersion.index + 1;
|
|
2033
|
+
const startChangesetIndexOrId = startChangeset?.index ?? startChangeset?.id ?? syncVersion.index + 1;
|
|
2112
2034
|
const endChangesetId = this.sourceDb.changeset.id;
|
|
2113
2035
|
const [startChangesetIndex, endChangesetIndex] = await Promise.all([startChangesetIndexOrId, endChangesetId].map(async (indexOrId) => typeof indexOrId === "number"
|
|
2114
2036
|
? indexOrId
|
|
@@ -2116,21 +2038,21 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2116
2038
|
iModelId: this.sourceDb.iModelId,
|
|
2117
2039
|
changeset: { id: indexOrId },
|
|
2118
2040
|
}).then((changeset) => changeset.index)));
|
|
2119
|
-
const missingChangesets = startChangesetIndex >
|
|
2041
|
+
const missingChangesets = startChangesetIndex > syncVersion.index + 1;
|
|
2120
2042
|
if (!this._options.argsForProcessChanges
|
|
2121
2043
|
?.ignoreMissingChangesetsInSynchronizations &&
|
|
2122
|
-
startChangesetIndex !==
|
|
2123
|
-
|
|
2044
|
+
startChangesetIndex !== syncVersion.index + 1 &&
|
|
2045
|
+
syncVersion.index !== -1) {
|
|
2124
2046
|
throw Error(`synchronization is ${missingChangesets ? "missing changesets" : ""},` +
|
|
2125
2047
|
" startChangesetId should be" +
|
|
2126
2048
|
" exactly the first changeset *after* the previous synchronization to not miss data." +
|
|
2127
2049
|
` You specified '${startChangesetIndexOrId}' which is changeset #${startChangesetIndex}` +
|
|
2128
|
-
` but the previous synchronization for this
|
|
2129
|
-
` which is changeset #${
|
|
2130
|
-
` #${
|
|
2050
|
+
` but the previous synchronization for this targetScopeElem ${syncVersion.id}'` +
|
|
2051
|
+
` which is changeset #${syncVersion.index}. The transformer expected` +
|
|
2052
|
+
` #${syncVersion.index + 1}.`);
|
|
2131
2053
|
}
|
|
2132
2054
|
nodeAssert(this._targetScopeProvenanceProps, "_targetScopeProvenanceProps should be set by now");
|
|
2133
|
-
const changesetsToSkip = this.
|
|
2055
|
+
const changesetsToSkip = (await this.getIsReverseSynchronization())
|
|
2134
2056
|
? this._targetScopeProvenanceProps.jsonProperties
|
|
2135
2057
|
.pendingReverseSyncChangesetIndices
|
|
2136
2058
|
: this._targetScopeProvenanceProps.jsonProperties
|
|
@@ -2204,15 +2126,12 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2204
2126
|
await this.exporter.exportRelationships(core_backend_1.ElementRefersToElements.classFullName);
|
|
2205
2127
|
if (this._options.forceExternalSourceAspectProvenance &&
|
|
2206
2128
|
this.shouldDetectDeletes()) {
|
|
2207
|
-
|
|
2208
|
-
await this.detectElementDeletes();
|
|
2209
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
2210
|
-
await this.detectRelationshipDeletes();
|
|
2129
|
+
core_bentley_1.Logger.logWarning(loggerCategory, "This workflows was deprecated in v1 and is no longer supported");
|
|
2211
2130
|
}
|
|
2212
2131
|
if (this._options.optimizeGeometry)
|
|
2213
2132
|
this.importer.optimizeGeometry(this._options.optimizeGeometry);
|
|
2214
2133
|
this.importer.computeProjectExtents();
|
|
2215
|
-
this.finalizeTransformation();
|
|
2134
|
+
await this.finalizeTransformation();
|
|
2216
2135
|
}
|
|
2217
2136
|
/** previous provenance, either a federation guid, a `${sourceFedGuid}/${targetFedGuid}` pair, or required aspect props */
|
|
2218
2137
|
_lastProvenanceEntityInfo = nullLastProvenanceEntityInfo;
|
|
@@ -2240,13 +2159,13 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2240
2159
|
*/
|
|
2241
2160
|
async processChanges(options) {
|
|
2242
2161
|
// must wait for initialization of synchronization provenance data
|
|
2243
|
-
await this.exporter.exportChanges(this.getExportInitOpts(options));
|
|
2162
|
+
await this.exporter.exportChanges(await this.getExportInitOpts(options));
|
|
2244
2163
|
await this.completePartiallyCommittedElements();
|
|
2245
2164
|
await this.completePartiallyCommittedAspects();
|
|
2246
2165
|
if (this._options.optimizeGeometry)
|
|
2247
2166
|
this.importer.optimizeGeometry(this._options.optimizeGeometry);
|
|
2248
2167
|
this.importer.computeProjectExtents();
|
|
2249
|
-
this.finalizeTransformation();
|
|
2168
|
+
await this.finalizeTransformation();
|
|
2250
2169
|
const defaultSaveTargetChanges = () => {
|
|
2251
2170
|
this.targetDb.saveChanges();
|
|
2252
2171
|
};
|
|
@@ -2255,7 +2174,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2255
2174
|
/** Changeset data must be initialized in order to build correct changeOptions.
|
|
2256
2175
|
* Call [[IModelTransformer.initialize]] for initialization of synchronization provenance data
|
|
2257
2176
|
*/
|
|
2258
|
-
getExportInitOpts(opts) {
|
|
2177
|
+
async getExportInitOpts(opts) {
|
|
2259
2178
|
if (!this._options.argsForProcessChanges)
|
|
2260
2179
|
return {};
|
|
2261
2180
|
const startChangeset = "startChangeset" in opts ? opts.startChangeset : undefined;
|
|
@@ -2269,7 +2188,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2269
2188
|
? { startChangeset }
|
|
2270
2189
|
: {
|
|
2271
2190
|
startChangeset: {
|
|
2272
|
-
index: this.
|
|
2191
|
+
index: (await this.getSynchronizationVersion()).index + 1,
|
|
2273
2192
|
},
|
|
2274
2193
|
}),
|
|
2275
2194
|
};
|
|
@@ -2388,19 +2307,4 @@ class TemplateModelCloner extends IModelTransformer {
|
|
|
2388
2307
|
}
|
|
2389
2308
|
}
|
|
2390
2309
|
exports.TemplateModelCloner = TemplateModelCloner;
|
|
2391
|
-
//Deprecate in preference of imodeldb.elements.getFederationGuidFromId()?
|
|
2392
|
-
function queryElemFedGuid(db, elemId) {
|
|
2393
|
-
// eslint-disable-next-line @itwin/no-internal, @typescript-eslint/no-deprecated
|
|
2394
|
-
return db.withPreparedStatement(`
|
|
2395
|
-
SELECT FederationGuid
|
|
2396
|
-
FROM bis.Element
|
|
2397
|
-
WHERE ECInstanceId=?
|
|
2398
|
-
`, (stmt) => {
|
|
2399
|
-
stmt.bindId(1, elemId);
|
|
2400
|
-
(0, core_bentley_1.assert)(stmt.step() === core_bentley_1.DbResult.BE_SQLITE_ROW);
|
|
2401
|
-
const result = stmt.getValue(0).getGuid();
|
|
2402
|
-
(0, core_bentley_1.assert)(stmt.step() === core_bentley_1.DbResult.BE_SQLITE_DONE);
|
|
2403
|
-
return result;
|
|
2404
|
-
});
|
|
2405
|
-
}
|
|
2406
2310
|
//# sourceMappingURL=IModelTransformer.js.map
|