@itwin/imodel-transformer 1.0.0-dev.8 → 1.0.0
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 +29 -1
- package/README.md +17 -0
- package/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
- package/lib/cjs/BranchProvenanceInitializer.js +2 -0
- package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
- package/lib/cjs/IModelExporter.d.ts +9 -9
- package/lib/cjs/IModelExporter.d.ts.map +1 -1
- package/lib/cjs/IModelExporter.js +18 -14
- package/lib/cjs/IModelExporter.js.map +1 -1
- package/lib/cjs/IModelImporter.d.ts +26 -3
- package/lib/cjs/IModelImporter.d.ts.map +1 -1
- package/lib/cjs/IModelImporter.js +58 -20
- package/lib/cjs/IModelImporter.js.map +1 -1
- package/lib/cjs/IModelTransformer.d.ts +107 -147
- package/lib/cjs/IModelTransformer.d.ts.map +1 -1
- package/lib/cjs/IModelTransformer.js +292 -318
- package/lib/cjs/IModelTransformer.js.map +1 -1
- package/lib/cjs/{transformer.d.ts → imodel-transformer.d.ts} +1 -1
- package/lib/cjs/imodel-transformer.d.ts.map +1 -0
- package/lib/cjs/{transformer.js → imodel-transformer.js} +11 -2
- package/lib/cjs/imodel-transformer.js.map +1 -0
- package/package.json +6 -5
- package/lib/cjs/PendingReferenceMap.d.ts +0 -37
- package/lib/cjs/PendingReferenceMap.d.ts.map +0 -1
- package/lib/cjs/PendingReferenceMap.js +0 -92
- package/lib/cjs/PendingReferenceMap.js.map +0 -1
- package/lib/cjs/transformer.d.ts.map +0 -1
- package/lib/cjs/transformer.js.map +0 -1
|
@@ -19,8 +19,6 @@ const core_common_1 = require("@itwin/core-common");
|
|
|
19
19
|
const IModelExporter_1 = require("./IModelExporter");
|
|
20
20
|
const IModelImporter_1 = require("./IModelImporter");
|
|
21
21
|
const TransformerLoggerCategory_1 = require("./TransformerLoggerCategory");
|
|
22
|
-
const PendingReferenceMap_1 = require("./PendingReferenceMap");
|
|
23
|
-
const EntityMap_1 = require("./EntityMap");
|
|
24
22
|
const IModelCloneContext_1 = require("./IModelCloneContext");
|
|
25
23
|
const EntityUnifier_1 = require("./EntityUnifier");
|
|
26
24
|
const Algo_1 = require("./Algo");
|
|
@@ -31,30 +29,6 @@ const nullLastProvenanceEntityInfo = {
|
|
|
31
29
|
aspectVersion: "",
|
|
32
30
|
aspectKind: core_backend_1.ExternalSourceAspect.Kind.Element,
|
|
33
31
|
};
|
|
34
|
-
/**
|
|
35
|
-
* A container for tracking the state of a partially committed entity and finalizing it when it's ready to be fully committed
|
|
36
|
-
* @internal
|
|
37
|
-
*/
|
|
38
|
-
class PartiallyCommittedEntity {
|
|
39
|
-
constructor(
|
|
40
|
-
/**
|
|
41
|
-
* A set of "model|element ++ ID64" pairs, (e.g. `model0x11` or `element0x12`)
|
|
42
|
-
* It is possible for the submodel of an element to be separately resolved from the actual element,
|
|
43
|
-
* so its resolution must be tracked separately
|
|
44
|
-
*/
|
|
45
|
-
_missingReferences, _onComplete) {
|
|
46
|
-
this._missingReferences = _missingReferences;
|
|
47
|
-
this._onComplete = _onComplete;
|
|
48
|
-
}
|
|
49
|
-
resolveReference(id) {
|
|
50
|
-
this._missingReferences.delete(id);
|
|
51
|
-
if (this._missingReferences.size === 0)
|
|
52
|
-
this._onComplete();
|
|
53
|
-
}
|
|
54
|
-
forceComplete() {
|
|
55
|
-
this._onComplete();
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
32
|
/**
|
|
59
33
|
* Apply a function to each Id64 in a supported container type of Id64s.
|
|
60
34
|
* Currently only supports raw Id64String or RelatedElement-like objects containing an `id` property that is a Id64String,
|
|
@@ -172,7 +146,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
172
146
|
if (this._isProvenanceInitTransform) {
|
|
173
147
|
return "forward";
|
|
174
148
|
}
|
|
175
|
-
if (!this.
|
|
149
|
+
if (!this._options.argsForProcessChanges) {
|
|
176
150
|
return "not-sync";
|
|
177
151
|
}
|
|
178
152
|
try {
|
|
@@ -217,14 +191,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
217
191
|
*/
|
|
218
192
|
constructor(source, target, options) {
|
|
219
193
|
super();
|
|
220
|
-
/** map of (unprocessed element, referencing processed element) pairs to the partially committed element that needs the reference resolved
|
|
221
|
-
* and have some helper methods below for now */
|
|
222
|
-
this._pendingReferences = new PendingReferenceMap_1.PendingReferenceMap();
|
|
223
194
|
/** a set of elements for which source provenance will be explicitly tracked by ExternalSourceAspects */
|
|
224
195
|
this._elementsWithExplicitlyTrackedProvenance = new Set();
|
|
225
|
-
|
|
226
|
-
this.
|
|
227
|
-
this._isSynchronization = false;
|
|
196
|
+
this._partiallyCommittedElementIds = new Set();
|
|
197
|
+
this._partiallyCommittedAspectIds = new Set();
|
|
228
198
|
/**
|
|
229
199
|
* A private variable meant to be set by tests which have an outdated way of setting up transforms. In all synchronizations today we expect to find an ESA in the branch db which describes the master -> branch relationship.
|
|
230
200
|
* The exception to this is the first transform aka the provenance initializing transform which requires that the master imodel and the branch imodel are identical at the time of provenance initialization.
|
|
@@ -233,10 +203,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
233
203
|
*/
|
|
234
204
|
this._allowNoScopingESA = false;
|
|
235
205
|
this._changesetRanges = undefined;
|
|
236
|
-
/** Set of entity keys which were not exported and don't need to be tracked for pending reference resolution.
|
|
237
|
-
* @note Currently only tracks elements which were not exported.
|
|
238
|
-
*/
|
|
239
|
-
this._skippedEntities = new Set();
|
|
240
206
|
/**
|
|
241
207
|
* Previously the transformer would insert provenance always pointing to the "target" relationship.
|
|
242
208
|
* It should (and now by default does) instead insert provenance pointing to the provenanceSource
|
|
@@ -276,7 +242,12 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
276
242
|
// eslint-disable-next-line deprecation/deprecation
|
|
277
243
|
danglingReferencesBehavior: options?.danglingReferencesBehavior ?? "reject",
|
|
278
244
|
branchRelationshipDataBehavior: options?.branchRelationshipDataBehavior ?? "reject",
|
|
245
|
+
skipPropagateChangesToRootElements: options?.skipPropagateChangesToRootElements ?? true,
|
|
279
246
|
};
|
|
247
|
+
// check if authorization client is defined
|
|
248
|
+
if (core_backend_1.IModelHost.authorizationClient === undefined) {
|
|
249
|
+
core_bentley_1.Logger.logWarning(loggerCategory, "Authorization client is not set in IModelHost. If the transformer needs an accessToken, then it will fail.");
|
|
250
|
+
}
|
|
280
251
|
this._isProvenanceInitTransform = this._options
|
|
281
252
|
.wasSourceIModelCopiedToTarget
|
|
282
253
|
? true
|
|
@@ -360,9 +331,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
360
331
|
core_bentley_1.Logger.logInfo(loggerCategory, `this._includeSourceProvenance=${this._options.includeSourceProvenance}`);
|
|
361
332
|
core_bentley_1.Logger.logInfo(loggerCategory, `this._cloneUsingBinaryGeometry=${this._options.cloneUsingBinaryGeometry}`);
|
|
362
333
|
core_bentley_1.Logger.logInfo(loggerCategory, `this._wasSourceIModelCopiedToTarget=${this._options.wasSourceIModelCopiedToTarget}`);
|
|
363
|
-
core_bentley_1.Logger.logInfo(loggerCategory,
|
|
364
|
-
// eslint-disable-next-line deprecation/deprecation
|
|
365
|
-
`this._isReverseSynchronization=${this._options.isReverseSynchronization}`);
|
|
366
334
|
core_bentley_1.Logger.logInfo(TransformerLoggerCategory_1.TransformerLoggerCategory.IModelImporter, `this.importer.autoExtendProjectExtents=${JSON.stringify(this.importer.options.autoExtendProjectExtents)}`);
|
|
367
335
|
core_bentley_1.Logger.logInfo(TransformerLoggerCategory_1.TransformerLoggerCategory.IModelImporter, `this.importer.simplifyElementGeometry=${this.importer.options.simplifyElementGeometry}`);
|
|
368
336
|
}
|
|
@@ -456,25 +424,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
456
424
|
forceOldRelationshipProvenanceMethod: this._forceOldRelationshipProvenanceMethod,
|
|
457
425
|
});
|
|
458
426
|
}
|
|
459
|
-
/** the changeset in the scoping element's source version found for this transformation
|
|
460
|
-
* @note: the version depends on whether this is a reverse synchronization or not, as
|
|
461
|
-
* it is stored separately for both synchronization directions.
|
|
462
|
-
* @note: must call [[initScopeProvenance]] before using this property.
|
|
463
|
-
* @note: empty string and -1 for changeset and index if it has never been transformed or was transformed before federation guid update (pre 1.x).
|
|
464
|
-
*/
|
|
465
|
-
get _synchronizationVersion() {
|
|
466
|
-
if (!this._cachedSynchronizationVersion) {
|
|
467
|
-
nodeAssert(this._targetScopeProvenanceProps, "_targetScopeProvenanceProps was not set yet");
|
|
468
|
-
const version = this.isReverseSynchronization
|
|
469
|
-
? this._targetScopeProvenanceProps.jsonProperties?.reverseSyncVersion
|
|
470
|
-
: this._targetScopeProvenanceProps.version;
|
|
471
|
-
nodeAssert(version !== undefined, "no version contained in target scope");
|
|
472
|
-
const [id, index] = version === "" ? ["", -1] : version.split(";");
|
|
473
|
-
this._cachedSynchronizationVersion = { index: Number(index), id };
|
|
474
|
-
nodeAssert(!Number.isNaN(this._cachedSynchronizationVersion.index), "bad parse: invalid index in version");
|
|
475
|
-
}
|
|
476
|
-
return this._cachedSynchronizationVersion;
|
|
477
|
-
}
|
|
478
427
|
/**
|
|
479
428
|
* As of itwinjs 4.6.0, definitionContainers are now deleted as if they were DefinitionPartitions as opposed to Definitions.
|
|
480
429
|
* This variable being true will be used to special case the deletion of DefinitionContainers the same way DefinitionPartitions are deleted.
|
|
@@ -485,10 +434,20 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
485
434
|
}
|
|
486
435
|
return this._hasDefinitionContainerDeletionFeature;
|
|
487
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* We cache the synchronization version to avoid querying the target scoping ESA multiple times.
|
|
439
|
+
* If the target scoping ESA is ever updated we need to clear any potentially cached sync version otherwise we will get stale values.
|
|
440
|
+
* Sets this._cachedSynchronizationVersion to undefined.
|
|
441
|
+
*/
|
|
442
|
+
clearCachedSynchronizationVersion() {
|
|
443
|
+
this._cachedSynchronizationVersion = undefined;
|
|
444
|
+
}
|
|
488
445
|
/** the changeset in the scoping element's source version found for this transformation
|
|
489
|
-
* @note
|
|
446
|
+
* @note the version depends on whether this is a reverse synchronization or not, as
|
|
490
447
|
* it is stored separately for both synchronization directions.
|
|
491
|
-
* @note
|
|
448
|
+
* @note empty string and -1 for changeset and index if it has never been transformed
|
|
449
|
+
* @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".
|
|
450
|
+
* @throws if the version is not found in a preexisting scope aspect and @see [[IModelTransformOptions.branchRelationshipDataBehavior]] !== "unsafe-migrate"
|
|
492
451
|
*/
|
|
493
452
|
get synchronizationVersion() {
|
|
494
453
|
if (this._cachedSynchronizationVersion === undefined) {
|
|
@@ -499,10 +458,15 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
499
458
|
const version = this.isReverseSynchronization
|
|
500
459
|
? JSON.parse(provenanceScopeAspect.jsonProperties ?? "{}").reverseSyncVersion
|
|
501
460
|
: provenanceScopeAspect.version;
|
|
502
|
-
if (!version
|
|
461
|
+
if (!version &&
|
|
462
|
+
this._options.branchRelationshipDataBehavior === "unsafe-migrate") {
|
|
503
463
|
return { index: -1, id: "" }; // previous synchronization was done before fed guid update.
|
|
504
464
|
}
|
|
505
|
-
|
|
465
|
+
if (version === undefined) {
|
|
466
|
+
throw new Error(`Could not find synchronization version in scope aspect. This may be due to the last successful run of the transformer being done with an older version.
|
|
467
|
+
Consider running the transformer with branchRelationshipDataBehavior set to 'unsafe-migrate'`);
|
|
468
|
+
}
|
|
469
|
+
const [id, index] = version === "" ? ["", -1] : version.split(";");
|
|
506
470
|
if (Number.isNaN(Number(index)))
|
|
507
471
|
throw new Error("Could not parse version data from scope aspect");
|
|
508
472
|
this._cachedSynchronizationVersion = { index: Number(index), id }; // synchronization version found and cached.
|
|
@@ -578,6 +542,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
578
542
|
jsonProperties: JSON.stringify(aspectProps.jsonProperties),
|
|
579
543
|
});
|
|
580
544
|
aspectProps.id = id;
|
|
545
|
+
// Busting a potential cached version
|
|
546
|
+
this.clearCachedSynchronizationVersion();
|
|
581
547
|
}
|
|
582
548
|
}
|
|
583
549
|
else {
|
|
@@ -587,29 +553,50 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
587
553
|
aspectProps.jsonProperties = foundEsaProps.jsonProperties
|
|
588
554
|
? JSON.parse(foundEsaProps.jsonProperties)
|
|
589
555
|
: undefined;
|
|
590
|
-
|
|
556
|
+
// Clone oldProps incase they're changed for logging purposes
|
|
557
|
+
const oldProps = JSON.parse(JSON.stringify(aspectProps));
|
|
558
|
+
if (this.handleUnsafeMigrate(aspectProps)) {
|
|
559
|
+
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
|
+
this.provenanceDb.elements.updateAspect({
|
|
561
|
+
...aspectProps,
|
|
562
|
+
jsonProperties: JSON.stringify(aspectProps.jsonProperties),
|
|
563
|
+
});
|
|
564
|
+
// Busting a potential cached version
|
|
565
|
+
this.clearCachedSynchronizationVersion();
|
|
566
|
+
}
|
|
591
567
|
}
|
|
592
568
|
this._targetScopeProvenanceProps =
|
|
593
569
|
aspectProps;
|
|
594
570
|
}
|
|
571
|
+
/** Returns true if a change was made to the aspectProps. */
|
|
595
572
|
handleUnsafeMigrate(aspectProps) {
|
|
573
|
+
let madeChange = false;
|
|
596
574
|
if (this._options.branchRelationshipDataBehavior !== "unsafe-migrate")
|
|
597
|
-
return;
|
|
598
|
-
const fallbackSyncVersionToUse = this._options.unsafeFallbackSyncVersion ?? "";
|
|
599
|
-
const fallbackReverseSyncVersionToUse = this._options.unsafeFallbackReverseSyncVersion ??
|
|
600
|
-
|
|
575
|
+
return madeChange;
|
|
576
|
+
const fallbackSyncVersionToUse = this._options.argsForProcessChanges?.unsafeFallbackSyncVersion ?? "";
|
|
577
|
+
const fallbackReverseSyncVersionToUse = this._options.argsForProcessChanges?.unsafeFallbackReverseSyncVersion ??
|
|
578
|
+
"";
|
|
579
|
+
if (aspectProps.version === undefined ||
|
|
580
|
+
(aspectProps.version === "" &&
|
|
581
|
+
aspectProps.version !== fallbackSyncVersionToUse)) {
|
|
601
582
|
aspectProps.version = fallbackSyncVersionToUse;
|
|
583
|
+
madeChange = true;
|
|
584
|
+
}
|
|
602
585
|
if (aspectProps.jsonProperties === undefined) {
|
|
603
586
|
aspectProps.jsonProperties = {
|
|
604
587
|
pendingReverseSyncChangesetIndices: [],
|
|
605
588
|
pendingSyncChangesetIndices: [],
|
|
606
589
|
reverseSyncVersion: fallbackReverseSyncVersionToUse,
|
|
607
590
|
};
|
|
591
|
+
madeChange = true;
|
|
608
592
|
}
|
|
609
593
|
else if (aspectProps.jsonProperties.reverseSyncVersion === undefined ||
|
|
610
|
-
aspectProps.jsonProperties.reverseSyncVersion === ""
|
|
594
|
+
(aspectProps.jsonProperties.reverseSyncVersion === "" &&
|
|
595
|
+
aspectProps.jsonProperties.reverseSyncVersion !==
|
|
596
|
+
fallbackReverseSyncVersionToUse)) {
|
|
611
597
|
aspectProps.jsonProperties.reverseSyncVersion =
|
|
612
598
|
fallbackReverseSyncVersionToUse;
|
|
599
|
+
madeChange = true;
|
|
613
600
|
}
|
|
614
601
|
/**
|
|
615
602
|
* This case will only be hit when:
|
|
@@ -622,11 +609,14 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
622
609
|
undefined) {
|
|
623
610
|
core_bentley_1.Logger.logWarning(loggerCategory, "Property pendingReverseSyncChangesetIndices missing on the jsonProperties of the scoping ESA. Setting to [].");
|
|
624
611
|
aspectProps.jsonProperties.pendingReverseSyncChangesetIndices = [];
|
|
612
|
+
madeChange = true;
|
|
625
613
|
}
|
|
626
614
|
if (aspectProps.jsonProperties.pendingSyncChangesetIndices === undefined) {
|
|
627
615
|
core_bentley_1.Logger.logWarning(loggerCategory, "Property pendingSyncChangesetIndices missing on the jsonProperties of the scoping ESA. Setting to [].");
|
|
628
616
|
aspectProps.jsonProperties.pendingSyncChangesetIndices = [];
|
|
617
|
+
madeChange = true;
|
|
629
618
|
}
|
|
619
|
+
return madeChange;
|
|
630
620
|
}
|
|
631
621
|
/**
|
|
632
622
|
* Iterate all matching federation guids and ExternalSourceAspects in the provenance iModel (target unless reverse sync)
|
|
@@ -723,7 +713,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
723
713
|
targetScopeElementId: this.targetScopeElementId,
|
|
724
714
|
isReverseSynchronization: this.isReverseSynchronization,
|
|
725
715
|
fn,
|
|
726
|
-
skipPropagateChangesToRootElements: this._options.skipPropagateChangesToRootElements ??
|
|
716
|
+
skipPropagateChangesToRootElements: this._options.skipPropagateChangesToRootElements ?? true,
|
|
727
717
|
});
|
|
728
718
|
}
|
|
729
719
|
/**
|
|
@@ -845,7 +835,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
845
835
|
}
|
|
846
836
|
/** Returns `true` if *brute force* delete detections should be run.
|
|
847
837
|
* @note This is only called if [[IModelTransformOptions.forceExternalSourceAspectProvenance]] option is true
|
|
848
|
-
* @note Not relevant for
|
|
838
|
+
* @note Not relevant for [[process]] when [[IModelTransformOptions.argsForProcessChanges]] are provided and change history is known.
|
|
849
839
|
*/
|
|
850
840
|
shouldDetectDeletes() {
|
|
851
841
|
nodeAssert(this._syncType !== undefined);
|
|
@@ -855,9 +845,9 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
855
845
|
* Detect Element deletes using ExternalSourceAspects in the target iModel and a *brute force* comparison against Elements
|
|
856
846
|
* in the source iModel.
|
|
857
847
|
* @deprecated in 1.x. Do not use this. // FIXME<MIKE>: how to better explain this?
|
|
858
|
-
* This method is only called during [[
|
|
848
|
+
* This method is only called during [[process]] when [[IModelTransformOptions.argsForProcessChanges]] is undefined and the option
|
|
859
849
|
* [[IModelTransformOptions.forceExternalSourceAspectProvenance]] is enabled. It is not
|
|
860
|
-
* necessary when
|
|
850
|
+
* necessary when calling [[process]] with [[IModelTransformOptions.argsForProcessChanges]] defined, since changeset information is sufficient.
|
|
861
851
|
* @note you do not need to call this directly unless processing a subset of an iModel.
|
|
862
852
|
* @throws [[IModelError]] If the required provenance information is not available to detect deletes.
|
|
863
853
|
*/
|
|
@@ -887,12 +877,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
887
877
|
}
|
|
888
878
|
});
|
|
889
879
|
}
|
|
890
|
-
/**
|
|
891
|
-
* @deprecated in 3.x, this no longer has any effect except emitting a warning
|
|
892
|
-
*/
|
|
893
|
-
skipElement(_sourceElement) {
|
|
894
|
-
core_bentley_1.Logger.logWarning(loggerCategory, "Tried to defer/skip an element, which is no longer necessary");
|
|
895
|
-
}
|
|
896
880
|
/** Transform the specified sourceElement into ElementProps for the target iModel.
|
|
897
881
|
* @param sourceElement The Element from the source iModel to transform.
|
|
898
882
|
* @returns ElementProps for the target iModel.
|
|
@@ -937,85 +921,74 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
937
921
|
nodeAssert(this._hasElementChangedCache !== undefined, "has element changed cache should be initialized by now");
|
|
938
922
|
return this._hasElementChangedCache.has(sourceElement.id);
|
|
939
923
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
(0, core_bentley_1.assert)(false, `unreachable; entity was '${entity.constructor.name}' not an Element, Relationship, or ElementAspect`);
|
|
951
|
-
}
|
|
952
|
-
/** callback to perform when a partial element says it's ready to be completed
|
|
953
|
-
* transforms the source element with all references now valid, then updates the partial element with the results
|
|
954
|
-
*/
|
|
955
|
-
makePartialEntityCompleter(sourceEntity) {
|
|
956
|
-
return () => {
|
|
957
|
-
const targetId = this.context.findTargetEntityId(core_backend_1.EntityReferences.from(sourceEntity));
|
|
958
|
-
if (!core_backend_1.EntityReferences.isValid(targetId))
|
|
959
|
-
throw Error(`${sourceEntity.id} has not been inserted into the target yet, the completer is invalid. This is a bug.`);
|
|
960
|
-
const onEntityTransform = IModelTransformer.transformCallbackFor(this, sourceEntity);
|
|
961
|
-
const updateEntity = EntityUnifier_1.EntityUnifier.updaterFor(this.targetDb, sourceEntity);
|
|
962
|
-
const targetProps = onEntityTransform.call(this, sourceEntity);
|
|
963
|
-
if (sourceEntity instanceof core_backend_1.Relationship) {
|
|
964
|
-
targetProps.sourceId =
|
|
965
|
-
this.context.findTargetElementId(sourceEntity.sourceId);
|
|
966
|
-
targetProps.targetId =
|
|
967
|
-
this.context.findTargetElementId(sourceEntity.targetId);
|
|
924
|
+
completePartiallyCommittedElements() {
|
|
925
|
+
for (const sourceElementId of this._partiallyCommittedElementIds) {
|
|
926
|
+
const sourceElement = this.sourceDb.elements.getElement({
|
|
927
|
+
id: sourceElementId,
|
|
928
|
+
wantGeometry: this.exporter.wantGeometry,
|
|
929
|
+
wantBRepData: this.exporter.wantGeometry,
|
|
930
|
+
});
|
|
931
|
+
const targetId = this.context.findTargetElementId(sourceElementId);
|
|
932
|
+
if (core_bentley_1.Id64.isInvalid(targetId)) {
|
|
933
|
+
throw new Error(`source-target element mapping not found for element "${sourceElementId}" when completing partially committed elements. This is a bug.`);
|
|
968
934
|
}
|
|
969
|
-
|
|
970
|
-
this.
|
|
971
|
-
}
|
|
935
|
+
const targetProps = this.onTransformElement(sourceElement);
|
|
936
|
+
this.targetDb.elements.updateElement({ ...targetProps, id: targetId });
|
|
937
|
+
}
|
|
972
938
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
this._skippedEntities.has(referenceId);
|
|
985
|
-
if (alreadyProcessed)
|
|
986
|
-
continue;
|
|
987
|
-
core_bentley_1.Logger.logTrace(loggerCategory, `Deferring resolution of reference '${referenceId}' of element '${entity.id}'`);
|
|
988
|
-
const referencedExistsInSource = EntityUnifier_1.EntityUnifier.exists(this.sourceDb, {
|
|
989
|
-
entityReference: referenceId,
|
|
939
|
+
completePartiallyCommittedAspects() {
|
|
940
|
+
for (const sourceAspectId of this._partiallyCommittedAspectIds) {
|
|
941
|
+
const sourceAspect = this.sourceDb.elements.getAspect(sourceAspectId);
|
|
942
|
+
const targetAspectId = this.context.findTargetAspectId(sourceAspectId);
|
|
943
|
+
if (core_bentley_1.Id64.isInvalid(targetAspectId)) {
|
|
944
|
+
throw new Error(`source-target aspect mapping not found for aspect "${sourceAspectId}" when completing partially committed aspects. This is a bug.`);
|
|
945
|
+
}
|
|
946
|
+
const targetAspectProps = this.onTransformElementAspect(sourceAspect);
|
|
947
|
+
this.targetDb.elements.updateAspect({
|
|
948
|
+
...targetAspectProps,
|
|
949
|
+
id: targetAspectId,
|
|
990
950
|
});
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
doAllReferencesExistInTarget(entity) {
|
|
954
|
+
let allReferencesExist = true;
|
|
955
|
+
for (const referenceId of entity.getReferenceIds()) {
|
|
956
|
+
const referencedEntityId = core_backend_1.EntityReferences.toId64(referenceId);
|
|
957
|
+
if (referencedEntityId === core_common_1.IModel.repositoryModelId ||
|
|
958
|
+
referencedEntityId === core_common_1.IModel.dictionaryId ||
|
|
959
|
+
referencedEntityId === "0xe") {
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
962
|
+
if (allReferencesExist &&
|
|
963
|
+
!core_backend_1.EntityReferences.isValid(this.context.findTargetEntityId(referenceId))) {
|
|
964
|
+
// if we care about references existing then we cannot return early and must check all other references.
|
|
965
|
+
if (this._options.danglingReferencesBehavior === "ignore") {
|
|
966
|
+
return false;
|
|
1004
967
|
}
|
|
968
|
+
allReferencesExist = false;
|
|
1005
969
|
}
|
|
1006
|
-
if (
|
|
1007
|
-
|
|
1008
|
-
if (!this._partiallyCommittedEntities.has(entity))
|
|
1009
|
-
this._partiallyCommittedEntities.set(entity, thisPartialElem);
|
|
970
|
+
if (this._options.danglingReferencesBehavior === "reject") {
|
|
971
|
+
this.assertReferenceExistsInSource(referenceId, entity);
|
|
1010
972
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
973
|
+
}
|
|
974
|
+
return allReferencesExist;
|
|
975
|
+
}
|
|
976
|
+
assertReferenceExistsInSource(referenceId, entity) {
|
|
977
|
+
const referencedExistsInSource = EntityUnifier_1.EntityUnifier.exists(this.sourceDb, {
|
|
978
|
+
entityReference: referenceId,
|
|
979
|
+
});
|
|
980
|
+
if (!referencedExistsInSource) {
|
|
981
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, [
|
|
982
|
+
`Found a reference to an element "${referenceId}" that doesn't exist while looking for references of "${entity.id}".`,
|
|
983
|
+
"This must have been caused by an upstream application that changed the iModel.",
|
|
984
|
+
"You can set the IModelTransformOptions.danglingReferencesBehavior option to 'ignore' to ignore this,",
|
|
985
|
+
`and the referenceId found on "${entity.id}" will not be carried over to corresponding target element.`,
|
|
986
|
+
].join("\n"));
|
|
1014
987
|
}
|
|
1015
988
|
}
|
|
1016
989
|
/** Cause the specified Element and its child Elements (if applicable) to be exported from the source iModel and imported into the target iModel.
|
|
1017
990
|
* @param sourceElementId Identifies the Element from the source iModel to import.
|
|
1018
|
-
* @note This method is called from [[
|
|
991
|
+
* @note This method is called from [[process]], so it only needs to be called directly when processing a subset of an iModel.
|
|
1019
992
|
*/
|
|
1020
993
|
async processElement(sourceElementId) {
|
|
1021
994
|
await this.initialize();
|
|
@@ -1026,7 +999,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1026
999
|
}
|
|
1027
1000
|
/** Import child elements into the target IModelDb
|
|
1028
1001
|
* @param sourceElementId Import the child elements of this element in the source IModelDb.
|
|
1029
|
-
* @note This method is called from [[
|
|
1002
|
+
* @note This method is called from [[process]], so it only needs to be called directly when processing a subset of an iModel.
|
|
1030
1003
|
*/
|
|
1031
1004
|
async processChildElements(sourceElementId) {
|
|
1032
1005
|
await this.initialize();
|
|
@@ -1038,24 +1011,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1038
1011
|
shouldExportElement(_sourceElement) {
|
|
1039
1012
|
return true;
|
|
1040
1013
|
}
|
|
1041
|
-
onSkipElement(sourceElementId) {
|
|
1042
|
-
if (this.context.findTargetElementId(sourceElementId) !== core_bentley_1.Id64.invalid) {
|
|
1043
|
-
// element already has provenance
|
|
1044
|
-
return;
|
|
1045
|
-
}
|
|
1046
|
-
core_bentley_1.Logger.logInfo(loggerCategory, `Element '${sourceElementId}' won't be exported. Marking its references as resolved`);
|
|
1047
|
-
const elementKey = `e${sourceElementId}`;
|
|
1048
|
-
this._skippedEntities.add(elementKey);
|
|
1049
|
-
// Mark any existing pending references to the skipped element as resolved.
|
|
1050
|
-
for (const referencer of this._pendingReferences.getReferencersByEntityKey(elementKey)) {
|
|
1051
|
-
const key = PendingReferenceMap_1.PendingReference.from(referencer, elementKey);
|
|
1052
|
-
const pendingRef = this._pendingReferences.get(key);
|
|
1053
|
-
if (!pendingRef)
|
|
1054
|
-
continue;
|
|
1055
|
-
pendingRef.resolveReference(elementKey);
|
|
1056
|
-
this._pendingReferences.delete(key);
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
1014
|
/**
|
|
1060
1015
|
* If they haven't been already, import all of the required references
|
|
1061
1016
|
* @internal do not call, override or implement this, it will be removed
|
|
@@ -1122,11 +1077,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1122
1077
|
onExportElement(sourceElement) {
|
|
1123
1078
|
let targetElementId;
|
|
1124
1079
|
let targetElementProps;
|
|
1125
|
-
if (this._options.
|
|
1126
|
-
targetElementId = sourceElement.id;
|
|
1127
|
-
targetElementProps = this.onTransformElement(sourceElement);
|
|
1128
|
-
}
|
|
1129
|
-
else if (this._options.wasSourceIModelCopiedToTarget) {
|
|
1080
|
+
if (this._options.wasSourceIModelCopiedToTarget) {
|
|
1130
1081
|
targetElementId = sourceElement.id;
|
|
1131
1082
|
targetElementProps =
|
|
1132
1083
|
this.targetDb.elements.getElementProps(targetElementId);
|
|
@@ -1165,17 +1116,40 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1165
1116
|
}
|
|
1166
1117
|
if (!this.hasElementChanged(sourceElement))
|
|
1167
1118
|
return;
|
|
1168
|
-
this.
|
|
1119
|
+
if (!this.doAllReferencesExistInTarget(sourceElement)) {
|
|
1120
|
+
this._partiallyCommittedElementIds.add(sourceElement.id);
|
|
1121
|
+
}
|
|
1169
1122
|
// targetElementId will be valid (indicating update) or undefined (indicating insert)
|
|
1170
1123
|
targetElementProps.id = core_bentley_1.Id64.isValid(targetElementId)
|
|
1171
1124
|
? targetElementId
|
|
1172
1125
|
: undefined;
|
|
1126
|
+
if (this._options.preserveElementIdsForFiltering) {
|
|
1127
|
+
const isValid = core_bentley_1.Id64.isValid(targetElementId);
|
|
1128
|
+
if (isValid && targetElementId !== sourceElement.id) {
|
|
1129
|
+
// Element found with different id
|
|
1130
|
+
throw new Error(`Element id(${sourceElement.id}) cannot be preserved. Found a different mapping(${targetElementId}) from source element`);
|
|
1131
|
+
}
|
|
1132
|
+
else if (isValid && targetElementId === sourceElement.id) {
|
|
1133
|
+
// targetElementId is valid (indicating update)
|
|
1134
|
+
this.importer.markElementToUpdateDuringPreserveIds(sourceElement.id);
|
|
1135
|
+
}
|
|
1136
|
+
else if (!isValid) {
|
|
1137
|
+
const sourceInTargetElemProps = this.targetDb.elements.tryGetElementProps(sourceElement.id);
|
|
1138
|
+
// if we don't find mapping for source element in target(invalid) but another element with source id exists in target
|
|
1139
|
+
if (sourceInTargetElemProps) {
|
|
1140
|
+
// Element id is already taken by another element
|
|
1141
|
+
throw new Error(`Element id(${sourceElement.id}) cannot be preserved. An unrelated element in the target already uses id: ${sourceElement.id}`);
|
|
1142
|
+
}
|
|
1143
|
+
else {
|
|
1144
|
+
// Element id in target is available to be remapped
|
|
1145
|
+
targetElementProps.id = sourceElement.id;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1173
1149
|
if (!this._options.wasSourceIModelCopiedToTarget) {
|
|
1174
1150
|
this.importer.importElement(targetElementProps); // don't need to import if iModel was copied
|
|
1175
1151
|
}
|
|
1176
1152
|
this.context.remapElement(sourceElement.id, targetElementProps.id); // targetElementProps.id assigned by importElement
|
|
1177
|
-
// now that we've mapped this elem we can fix unmapped references to it
|
|
1178
|
-
this.resolvePendingReferences(sourceElement);
|
|
1179
1153
|
// the transformer does not currently 'split' or 'join' any elements, therefore, it does not
|
|
1180
1154
|
// insert external source aspects because federation guids are sufficient for this.
|
|
1181
1155
|
// Other transformer subclasses must insert the appropriate aspect (as provided by a TBD API)
|
|
@@ -1203,16 +1177,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1203
1177
|
this.markLastProvenance(provenance, { isRelationship: false });
|
|
1204
1178
|
}
|
|
1205
1179
|
}
|
|
1206
|
-
resolvePendingReferences(entity) {
|
|
1207
|
-
for (const referencer of this._pendingReferences.getReferencers(entity)) {
|
|
1208
|
-
const key = PendingReferenceMap_1.PendingReference.from(referencer, entity);
|
|
1209
|
-
const pendingRef = this._pendingReferences.get(key);
|
|
1210
|
-
if (!pendingRef)
|
|
1211
|
-
continue;
|
|
1212
|
-
pendingRef.resolveReference(core_backend_1.EntityReferences.from(entity));
|
|
1213
|
-
this._pendingReferences.delete(key);
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
1180
|
/** Override of [IModelExportHandler.onDeleteElement]($transformer) that is called when [IModelExporter]($transformer) detects that an Element has been deleted from the source iModel.
|
|
1217
1181
|
* This override propagates the delete to the target iModel via [IModelImporter.deleteElement]($transformer).
|
|
1218
1182
|
*/
|
|
@@ -1237,7 +1201,6 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1237
1201
|
return;
|
|
1238
1202
|
const targetModelProps = this.onTransformModel(sourceModel, targetModeledElementId);
|
|
1239
1203
|
this.importer.importModel(targetModelProps);
|
|
1240
|
-
this.resolvePendingReferences(sourceModel);
|
|
1241
1204
|
}
|
|
1242
1205
|
/** Override of [IModelExportHandler.onDeleteModel]($transformer) that is called when [IModelExporter]($transformer) detects that a [Model]($backend) has been deleted from the source iModel. */
|
|
1243
1206
|
onDeleteModel(sourceModelId) {
|
|
@@ -1311,7 +1274,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1311
1274
|
}
|
|
1312
1275
|
/** Cause the model container, contents, and sub-models to be exported from the source iModel and imported into the target iModel.
|
|
1313
1276
|
* @param sourceModeledElementId Import this [Model]($backend) from the source IModelDb.
|
|
1314
|
-
* @note This method is called from [[
|
|
1277
|
+
* @note This method is called from [[process]], so it only needs to be called directly when processing a subset of an iModel.
|
|
1315
1278
|
*/
|
|
1316
1279
|
async processModel(sourceModeledElementId) {
|
|
1317
1280
|
await this.initialize();
|
|
@@ -1321,7 +1284,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1321
1284
|
* @param sourceModelId Import the contents of this model from the source IModelDb.
|
|
1322
1285
|
* @param targetModelId Import into this model in the target IModelDb. The target model must exist prior to this call.
|
|
1323
1286
|
* @param elementClassFullName Optional classFullName of an element subclass to limit import query against the source model.
|
|
1324
|
-
* @note This method is called from [[
|
|
1287
|
+
* @note This method is called from [[process]], so it only needs to be called directly when processing a subset of an iModel.
|
|
1325
1288
|
*/
|
|
1326
1289
|
async processModelContents(sourceModelId, targetModelId, elementClassFullName = core_backend_1.Element.classFullName) {
|
|
1327
1290
|
await this.initialize();
|
|
@@ -1378,46 +1341,50 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1378
1341
|
targetModelProps.parentModel = this.context.findTargetElementId(targetModelProps.parentModel);
|
|
1379
1342
|
return targetModelProps;
|
|
1380
1343
|
}
|
|
1381
|
-
/**
|
|
1382
|
-
*
|
|
1383
|
-
*/
|
|
1384
|
-
async processDeferredElements(_numRetries = 3) { }
|
|
1385
|
-
/** called at the end of a transformation,
|
|
1344
|
+
/**
|
|
1345
|
+
* Called at the end of a transformation,
|
|
1386
1346
|
* updates the target scope element to say that transformation up through the
|
|
1387
1347
|
* source's changeset has been performed. Also stores all changesets that occurred
|
|
1388
1348
|
* during the transformation as "pending synchronization changeset indices" @see TargetScopeProvenanceJsonProps
|
|
1389
1349
|
*
|
|
1390
|
-
* You generally should not call this function yourself and use [[
|
|
1350
|
+
* You generally should not call this function yourself and use [[process]] with [[IModelTransformOptions.argsForProcessChanges]] provided instead.
|
|
1391
1351
|
* It is public for unsupported use cases of custom synchronization transforms.
|
|
1392
|
-
* @note
|
|
1393
|
-
*
|
|
1352
|
+
* @note If [[IModelTransformOptions.argsForProcessChanges]] is not defined in this transformation, this function will return early without updating the sync version,
|
|
1353
|
+
* unless the `initializeReverseSyncVersion` option is set to `true`
|
|
1354
|
+
*
|
|
1355
|
+
* The `initializeReverseSyncVersion` is added to set the reverse synchronization version during a forward synchronization.
|
|
1356
|
+
* When set to `true`, it saves the reverse sync version as the current changeset of the targetDb. This is typically used for the first transformation between a master and branch iModel.
|
|
1357
|
+
* Setting `initializeReverseSyncVersion` to `true` has the effect of making it so any changesets in the branch iModel at the time of the first transformation will be ignored during any future reverse synchronizations from the branch to the master iModel.
|
|
1358
|
+
*
|
|
1359
|
+
* Note that typically, the reverseSyncVersion is saved as the last changeset merged from the branch into master.
|
|
1360
|
+
* 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.
|
|
1394
1361
|
*/
|
|
1395
|
-
updateSynchronizationVersion({
|
|
1396
|
-
const
|
|
1397
|
-
this._sourceChangeDataState !== "has-changes"
|
|
1398
|
-
|
|
1399
|
-
if (notForcedAndHasNoChangesAndIsntProvenanceInit)
|
|
1362
|
+
updateSynchronizationVersion({ initializeReverseSyncVersion = false, } = {}) {
|
|
1363
|
+
const shouldSkipSyncVersionUpdate = !initializeReverseSyncVersion &&
|
|
1364
|
+
this._sourceChangeDataState !== "has-changes";
|
|
1365
|
+
if (shouldSkipSyncVersionUpdate)
|
|
1400
1366
|
return;
|
|
1401
1367
|
nodeAssert(this._targetScopeProvenanceProps);
|
|
1402
1368
|
const sourceVersion = `${this.sourceDb.changeset.id};${this.sourceDb.changeset.index}`;
|
|
1403
1369
|
const targetVersion = `${this.targetDb.changeset.id};${this.targetDb.changeset.index}`;
|
|
1404
|
-
if (this.
|
|
1405
|
-
this._targetScopeProvenanceProps.version = sourceVersion;
|
|
1406
|
-
this._targetScopeProvenanceProps.jsonProperties.reverseSyncVersion =
|
|
1407
|
-
targetVersion;
|
|
1408
|
-
}
|
|
1409
|
-
else if (this.isReverseSynchronization) {
|
|
1370
|
+
if (this.isReverseSynchronization) {
|
|
1410
1371
|
const oldVersion = this._targetScopeProvenanceProps.jsonProperties.reverseSyncVersion;
|
|
1411
1372
|
core_bentley_1.Logger.logInfo(loggerCategory, `updating reverse version from ${oldVersion} to ${sourceVersion}`);
|
|
1412
1373
|
this._targetScopeProvenanceProps.jsonProperties.reverseSyncVersion =
|
|
1413
1374
|
sourceVersion;
|
|
1414
1375
|
}
|
|
1415
|
-
else
|
|
1376
|
+
else {
|
|
1416
1377
|
core_bentley_1.Logger.logInfo(loggerCategory, `updating sync version from ${this._targetScopeProvenanceProps.version} to ${sourceVersion}`);
|
|
1417
1378
|
this._targetScopeProvenanceProps.version = sourceVersion;
|
|
1379
|
+
// save reverse sync version
|
|
1380
|
+
if (initializeReverseSyncVersion) {
|
|
1381
|
+
core_bentley_1.Logger.logInfo(loggerCategory, `updating reverse sync version from ${this._targetScopeProvenanceProps.jsonProperties.reverseSyncVersion} to ${targetVersion}`);
|
|
1382
|
+
this._targetScopeProvenanceProps.jsonProperties.reverseSyncVersion =
|
|
1383
|
+
targetVersion;
|
|
1384
|
+
}
|
|
1418
1385
|
}
|
|
1419
|
-
if (this.
|
|
1420
|
-
(this._startingChangesetIndices &&
|
|
1386
|
+
if (this._options.argsForProcessChanges ||
|
|
1387
|
+
(this._startingChangesetIndices && initializeReverseSyncVersion)) {
|
|
1421
1388
|
nodeAssert(this.targetDb.changeset.index !== undefined &&
|
|
1422
1389
|
this._startingChangesetIndices !== undefined, "updateSynchronizationVersion was called without change history");
|
|
1423
1390
|
const jsonProps = this._targetScopeProvenanceProps.jsonProperties;
|
|
@@ -1425,16 +1392,21 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1425
1392
|
core_bentley_1.Logger.logTrace(loggerCategory, `previous pendingSyncChanges: ${jsonProps.pendingSyncChangesetIndices}`);
|
|
1426
1393
|
const pendingSyncChangesetIndicesKey = "pendingSyncChangesetIndices";
|
|
1427
1394
|
const pendingReverseSyncChangesetIndicesKey = "pendingReverseSyncChangesetIndices";
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1395
|
+
// Determine which keys to clear and update based on the synchronization direction
|
|
1396
|
+
let syncChangesetsToClearKey;
|
|
1397
|
+
let syncChangesetsToUpdateKey;
|
|
1398
|
+
if (this.isReverseSynchronization) {
|
|
1399
|
+
syncChangesetsToClearKey = pendingReverseSyncChangesetIndicesKey;
|
|
1400
|
+
syncChangesetsToUpdateKey = pendingSyncChangesetIndicesKey;
|
|
1401
|
+
}
|
|
1402
|
+
else {
|
|
1403
|
+
syncChangesetsToClearKey = pendingSyncChangesetIndicesKey;
|
|
1404
|
+
syncChangesetsToUpdateKey = pendingReverseSyncChangesetIndicesKey;
|
|
1405
|
+
}
|
|
1406
|
+
// NOTE that as documented in [[processChanges]], this assumes that right after
|
|
1407
|
+
// transformation finalization, the work will be saved immediately, otherwise we've
|
|
1408
|
+
// just marked this changeset as a synchronization to ignore, and the user can add other
|
|
1409
|
+
// stuff to it which would break future synchronizations
|
|
1438
1410
|
for (let i = this._startingChangesetIndices.target + 1; i <= this.targetDb.changeset.index + 1; i++)
|
|
1439
1411
|
jsonProps[syncChangesetsToUpdateKey].push(i);
|
|
1440
1412
|
// Only keep the changeset indices which are greater than the source, this means they haven't been processed yet.
|
|
@@ -1454,25 +1426,14 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1454
1426
|
...this._targetScopeProvenanceProps,
|
|
1455
1427
|
jsonProperties: JSON.stringify(this._targetScopeProvenanceProps.jsonProperties),
|
|
1456
1428
|
});
|
|
1429
|
+
this.clearCachedSynchronizationVersion();
|
|
1457
1430
|
}
|
|
1458
1431
|
// FIXME<MIKE>: is this necessary when manually using low level transform APIs? (document if so)
|
|
1459
|
-
|
|
1432
|
+
finalizeTransformation() {
|
|
1460
1433
|
this.importer.finalize();
|
|
1461
|
-
this.updateSynchronizationVersion(
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
"The following elements were never fully resolved:",
|
|
1465
|
-
[...this._partiallyCommittedEntities.keys()].join(","),
|
|
1466
|
-
"This indicates that either some references were excluded from the transformation",
|
|
1467
|
-
"or the source has dangling references.",
|
|
1468
|
-
].join("\n");
|
|
1469
|
-
if (this._options.danglingReferencesBehavior === "reject")
|
|
1470
|
-
throw new Error(message);
|
|
1471
|
-
core_bentley_1.Logger.logWarning(loggerCategory, message);
|
|
1472
|
-
for (const partiallyCommittedElem of this._partiallyCommittedEntities.values()) {
|
|
1473
|
-
partiallyCommittedElem.forceComplete();
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1434
|
+
this.updateSynchronizationVersion({
|
|
1435
|
+
initializeReverseSyncVersion: this._isProvenanceInitTransform,
|
|
1436
|
+
});
|
|
1476
1437
|
// TODO: ignore if we remove change cache usage
|
|
1477
1438
|
if (!this._options.noDetachChangeCache) {
|
|
1478
1439
|
if (core_backend_1.ChangeSummaryManager.isChangeCacheAttached(this.sourceDb))
|
|
@@ -1485,35 +1446,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1485
1446
|
this.targetDb.codeValueBehavior = "trim-unicode-whitespace";
|
|
1486
1447
|
}
|
|
1487
1448
|
/* eslint-enable @itwin/no-internal */
|
|
1488
|
-
const defaultSaveTargetChanges = () => this.targetDb.saveChanges();
|
|
1489
|
-
await (options?.saveTargetChanges ?? defaultSaveTargetChanges)(this);
|
|
1490
|
-
if (this.isReverseSynchronization)
|
|
1491
|
-
this.sourceDb.saveChanges();
|
|
1492
|
-
const description = `${this._isProvenanceInitTransform
|
|
1493
|
-
? options?.provenanceInitTransformChangesetDescription ??
|
|
1494
|
-
`initialized branch provenance with master iModel: ${this.sourceDb.iModelId}`
|
|
1495
|
-
: this.isForwardSynchronization
|
|
1496
|
-
? options?.forwardSyncBranchChangesetDescription ??
|
|
1497
|
-
`Forward sync of iModel: ${this.sourceDb.iModelId}`
|
|
1498
|
-
: options?.reverseSyncMasterChangesetDescription ??
|
|
1499
|
-
`Reverse sync of iModel: ${this.sourceDb.iModelId}`}`;
|
|
1500
|
-
if (this.targetDb.isBriefcaseDb()) {
|
|
1501
|
-
// This relies on authorizationClient on iModelHost being defined, otherwise this will fail
|
|
1502
|
-
await this.targetDb.pushChanges({
|
|
1503
|
-
description,
|
|
1504
|
-
});
|
|
1505
|
-
}
|
|
1506
|
-
if (this.isReverseSynchronization && this.sourceDb.isBriefcaseDb()) {
|
|
1507
|
-
// This relies on authorizationClient on iModelHost being defined, otherwise this will fail
|
|
1508
|
-
await this.sourceDb.pushChanges({
|
|
1509
|
-
description: options?.reverseSyncBranchChangesetDescription ??
|
|
1510
|
-
`Update provenance in response to a reverse sync to iModel: ${this.targetDb.iModelId}`,
|
|
1511
|
-
});
|
|
1512
|
-
}
|
|
1513
1449
|
}
|
|
1514
1450
|
/** Imports all relationships that subclass from the specified base class.
|
|
1515
1451
|
* @param baseRelClassFullName The specified base relationship class.
|
|
1516
|
-
* @note This method is called from [[
|
|
1452
|
+
* @note This method is called from [[process]], so it only needs to be called directly when processing a subset of an iModel.
|
|
1517
1453
|
*/
|
|
1518
1454
|
async processRelationships(baseRelClassFullName) {
|
|
1519
1455
|
await this.initialize();
|
|
@@ -1586,8 +1522,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1586
1522
|
}
|
|
1587
1523
|
/** Detect Relationship deletes using ExternalSourceAspects in the target iModel and a *brute force* comparison against relationships in the source iModel.
|
|
1588
1524
|
* @deprecated in 1.x. Don't use this anymore
|
|
1589
|
-
* @see
|
|
1590
|
-
* @note This method is called from [[
|
|
1525
|
+
* @see [[process]] with [[IModelTransformOptions.argsForProcessChanges]] provided.
|
|
1526
|
+
* @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.
|
|
1591
1527
|
* @throws [[IModelError]] If the required provenance information is not available to detect deletes.
|
|
1592
1528
|
*/
|
|
1593
1529
|
async detectRelationshipDeletes() {
|
|
@@ -1654,22 +1590,25 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1654
1590
|
* This override calls [[onTransformElementAspect]] and then [IModelImporter.importElementUniqueAspect]($transformer) to update the target iModel.
|
|
1655
1591
|
*/
|
|
1656
1592
|
onExportElementUniqueAspect(sourceAspect) {
|
|
1657
|
-
const
|
|
1658
|
-
|
|
1659
|
-
|
|
1593
|
+
const targetAspectProps = this.onTransformElementAspect(sourceAspect);
|
|
1594
|
+
if (!this.doAllReferencesExistInTarget(sourceAspect)) {
|
|
1595
|
+
this._partiallyCommittedAspectIds.add(sourceAspect.id);
|
|
1596
|
+
}
|
|
1660
1597
|
const targetId = this.importer.importElementUniqueAspect(targetAspectProps);
|
|
1661
1598
|
this.context.remapElementAspect(sourceAspect.id, targetId);
|
|
1662
|
-
this.resolvePendingReferences(sourceAspect);
|
|
1663
1599
|
}
|
|
1664
1600
|
/** Override of [IModelExportHandler.onExportElementMultiAspects]($transformer) that imports ElementMultiAspects into the target iModel when they are exported from the source iModel.
|
|
1665
1601
|
* This override calls [[onTransformElementAspect]] for each ElementMultiAspect and then [IModelImporter.importElementMultiAspects]($transformer) to update the target iModel.
|
|
1666
1602
|
* @note ElementMultiAspects are handled as a group to make it easier to differentiate between insert, update, and delete.
|
|
1667
1603
|
*/
|
|
1668
1604
|
onExportElementMultiAspects(sourceAspects) {
|
|
1669
|
-
const targetElementId = this.context.findTargetElementId(sourceAspects[0].element.id);
|
|
1670
1605
|
// Transform source ElementMultiAspects into target ElementAspectProps
|
|
1671
|
-
const targetAspectPropsArray = sourceAspects.map((srcA) => this.onTransformElementAspect(srcA
|
|
1672
|
-
sourceAspects.forEach((a) =>
|
|
1606
|
+
const targetAspectPropsArray = sourceAspects.map((srcA) => this.onTransformElementAspect(srcA));
|
|
1607
|
+
sourceAspects.forEach((a) => {
|
|
1608
|
+
if (!this.doAllReferencesExistInTarget(a)) {
|
|
1609
|
+
this._partiallyCommittedAspectIds.add(a.id);
|
|
1610
|
+
}
|
|
1611
|
+
});
|
|
1673
1612
|
// const targetAspectsToImport = targetAspectPropsArray.filter((targetAspect, i) => hasEntityChanged(sourceAspects[i], targetAspect));
|
|
1674
1613
|
const targetIds = this.importer.importElementMultiAspects(targetAspectPropsArray, (a) => {
|
|
1675
1614
|
const isExternalSourceAspectFromTransformer = a instanceof core_backend_1.ExternalSourceAspect &&
|
|
@@ -1679,16 +1618,14 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1679
1618
|
});
|
|
1680
1619
|
for (let i = 0; i < targetIds.length; ++i) {
|
|
1681
1620
|
this.context.remapElementAspect(sourceAspects[i].id, targetIds[i]);
|
|
1682
|
-
this.resolvePendingReferences(sourceAspects[i]);
|
|
1683
1621
|
}
|
|
1684
1622
|
}
|
|
1685
1623
|
/** Transform the specified sourceElementAspect into ElementAspectProps for the target iModel.
|
|
1686
1624
|
* @param sourceElementAspect The ElementAspect from the source iModel to be transformed.
|
|
1687
|
-
* @param _targetElementId The ElementId of the target Element that will own the ElementAspects after transformation.
|
|
1688
1625
|
* @returns ElementAspectProps for the target iModel.
|
|
1689
1626
|
* @note A subclass can override this method to provide custom transform behavior.
|
|
1690
1627
|
*/
|
|
1691
|
-
onTransformElementAspect(sourceElementAspect
|
|
1628
|
+
onTransformElementAspect(sourceElementAspect) {
|
|
1692
1629
|
const targetElementAspectProps = this.context.cloneElementAspect(sourceElementAspect);
|
|
1693
1630
|
return targetElementAspectProps;
|
|
1694
1631
|
}
|
|
@@ -1728,6 +1665,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1728
1665
|
nodeAssert(schemaFileName.length <= systemMaxPathSegmentSize, "Schema name was still long. This is a bug.");
|
|
1729
1666
|
this._longNamedSchemasMap.set(schema.name, schemaFileName);
|
|
1730
1667
|
}
|
|
1668
|
+
/* eslint-disable-next-line deprecation/deprecation */
|
|
1731
1669
|
this.sourceDb.nativeDb.exportSchema(schema.name, this._schemaExportDir, schemaFileName);
|
|
1732
1670
|
return { schemaPath: path.join(this._schemaExportDir, schemaFileName) };
|
|
1733
1671
|
}
|
|
@@ -1768,7 +1706,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1768
1706
|
}
|
|
1769
1707
|
}
|
|
1770
1708
|
/** Cause all fonts to be exported from the source iModel and imported into the target iModel.
|
|
1771
|
-
* @note This method is called from [[
|
|
1709
|
+
* @note This method is called from [[process]], so it only needs to be called directly when processing a subset of an iModel.
|
|
1772
1710
|
*/
|
|
1773
1711
|
async processFonts() {
|
|
1774
1712
|
// we do not need to initialize for this since no entities are exported
|
|
@@ -1780,14 +1718,14 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1780
1718
|
this.context.importFont(font.id);
|
|
1781
1719
|
}
|
|
1782
1720
|
/** Cause all CodeSpecs to be exported from the source iModel and imported into the target iModel.
|
|
1783
|
-
* @note This method is called from [[
|
|
1721
|
+
* @note This method is called from [[process]], so it only needs to be called directly when processing a subset of an iModel.
|
|
1784
1722
|
*/
|
|
1785
1723
|
async processCodeSpecs() {
|
|
1786
1724
|
await this.initialize();
|
|
1787
1725
|
return this.exporter.exportCodeSpecs();
|
|
1788
1726
|
}
|
|
1789
1727
|
/** Cause a single CodeSpec to be exported from the source iModel and imported into the target iModel.
|
|
1790
|
-
* @note This method is called from [[
|
|
1728
|
+
* @note This method is called from [[process]], so it only needs to be called directly when processing a subset of an iModel.
|
|
1791
1729
|
*/
|
|
1792
1730
|
async processCodeSpec(codeSpecName) {
|
|
1793
1731
|
await this.initialize();
|
|
@@ -1811,21 +1749,23 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1811
1749
|
this.context.remapElement(sourceSubjectId, targetSubjectId);
|
|
1812
1750
|
await this.processChildElements(sourceSubjectId);
|
|
1813
1751
|
await this.processSubjectSubModels(sourceSubjectId);
|
|
1814
|
-
|
|
1752
|
+
this.completePartiallyCommittedElements();
|
|
1753
|
+
this.completePartiallyCommittedAspects();
|
|
1815
1754
|
}
|
|
1816
1755
|
/**
|
|
1817
1756
|
* Initialize prerequisites of processing, you must initialize with an [[InitOptions]] if you
|
|
1818
|
-
* are intending to process changes
|
|
1757
|
+
* are intending to process changes. Callers may wish to explicitly call initialize if they need to execute code after initialize but before [[process]] is called.
|
|
1819
1758
|
* @note Called by all `process*` functions implicitly.
|
|
1820
1759
|
* Overriders must call `super.initialize()` first
|
|
1821
1760
|
*/
|
|
1822
|
-
async initialize(
|
|
1761
|
+
async initialize() {
|
|
1823
1762
|
if (this._initialized)
|
|
1824
1763
|
return;
|
|
1825
|
-
|
|
1764
|
+
this.initScopeProvenance();
|
|
1765
|
+
await this._tryInitChangesetData(this._options.argsForProcessChanges);
|
|
1826
1766
|
await this.context.initialize();
|
|
1827
1767
|
// need exporter initialized to do remapdeletedsourceentities.
|
|
1828
|
-
await this.exporter.initialize(this.getExportInitOpts(
|
|
1768
|
+
await this.exporter.initialize(this.getExportInitOpts(this._options.argsForProcessChanges ?? {}));
|
|
1829
1769
|
// 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).
|
|
1830
1770
|
await this.processChangesets();
|
|
1831
1771
|
this._initialized = true;
|
|
@@ -1891,7 +1831,8 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1891
1831
|
const changeType = change.$meta?.op;
|
|
1892
1832
|
if (changeType === "Deleted" &&
|
|
1893
1833
|
change?.$meta?.classFullName === core_backend_1.ExternalSourceAspect.classFullName &&
|
|
1894
|
-
change.Scope.Id === this.targetScopeElementId
|
|
1834
|
+
change.Scope.Id === this.targetScopeElementId &&
|
|
1835
|
+
change.Kind === core_backend_1.ExternalSourceAspect.Kind.Element) {
|
|
1895
1836
|
elemIdToScopeEsa.set(change.Element.Id, change);
|
|
1896
1837
|
}
|
|
1897
1838
|
else if ((changeType === "Inserted" || changeType === "Updated") &&
|
|
@@ -1930,7 +1871,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
1930
1871
|
async processDeletedOp(change, mapOfDeletedElemIdToScopeEsas, isRelationship, alreadyImportedElementInserts, alreadyImportedModelInserts) {
|
|
1931
1872
|
// we need a connected iModel with changes to remap elements with deletions
|
|
1932
1873
|
const notConnectedModel = this.sourceDb.iTwinId === undefined;
|
|
1933
|
-
const noChanges = this.
|
|
1874
|
+
const noChanges = this.synchronizationVersion.index === this.sourceDb.changeset.index;
|
|
1934
1875
|
if (notConnectedModel || noChanges)
|
|
1935
1876
|
return;
|
|
1936
1877
|
/**
|
|
@@ -2027,17 +1968,18 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2027
1968
|
this._sourceChangeDataState = "unconnected";
|
|
2028
1969
|
return;
|
|
2029
1970
|
}
|
|
2030
|
-
const noChanges = this.
|
|
1971
|
+
const noChanges = this.synchronizationVersion.index === this.sourceDb.changeset.index;
|
|
2031
1972
|
if (noChanges) {
|
|
2032
1973
|
this._sourceChangeDataState = "no-changes";
|
|
2033
1974
|
this._csFileProps = [];
|
|
2034
1975
|
return;
|
|
2035
1976
|
}
|
|
1977
|
+
const startChangeset = "startChangeset" in args ? args.startChangeset : undefined;
|
|
2036
1978
|
// NOTE: that we do NOT download the changesummary for the last transformed version, we want
|
|
2037
1979
|
// to ignore those already processed changes
|
|
2038
|
-
const startChangesetIndexOrId =
|
|
2039
|
-
|
|
2040
|
-
this.
|
|
1980
|
+
const startChangesetIndexOrId = startChangeset?.index ??
|
|
1981
|
+
startChangeset?.id ??
|
|
1982
|
+
this.synchronizationVersion.index + 1;
|
|
2041
1983
|
const endChangesetId = this.sourceDb.changeset.id;
|
|
2042
1984
|
const [startChangesetIndex, endChangesetIndex] = await Promise.all([startChangesetIndexOrId, endChangesetId].map(async (indexOrId) => typeof indexOrId === "number"
|
|
2043
1985
|
? indexOrId
|
|
@@ -2046,20 +1988,20 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2046
1988
|
iModelId: this.sourceDb.iModelId,
|
|
2047
1989
|
// eslint-disable-next-line deprecation/deprecation
|
|
2048
1990
|
changeset: { id: indexOrId },
|
|
2049
|
-
accessToken: args.accessToken,
|
|
2050
1991
|
})
|
|
2051
1992
|
.then((changeset) => changeset.index)));
|
|
2052
|
-
const missingChangesets = startChangesetIndex > this.
|
|
2053
|
-
if (!this._options.
|
|
2054
|
-
|
|
2055
|
-
this.
|
|
1993
|
+
const missingChangesets = startChangesetIndex > this.synchronizationVersion.index + 1;
|
|
1994
|
+
if (!this._options.argsForProcessChanges
|
|
1995
|
+
?.ignoreMissingChangesetsInSynchronizations &&
|
|
1996
|
+
startChangesetIndex !== this.synchronizationVersion.index + 1 &&
|
|
1997
|
+
this.synchronizationVersion.index !== -1) {
|
|
2056
1998
|
throw Error(`synchronization is ${missingChangesets ? "missing changesets" : ""},` +
|
|
2057
1999
|
" startChangesetId should be" +
|
|
2058
2000
|
" exactly the first changeset *after* the previous synchronization to not miss data." +
|
|
2059
2001
|
` You specified '${startChangesetIndexOrId}' which is changeset #${startChangesetIndex}` +
|
|
2060
|
-
` but the previous synchronization for this targetScopeElement was '${this.
|
|
2061
|
-
` which is changeset #${this.
|
|
2062
|
-
` #${this.
|
|
2002
|
+
` but the previous synchronization for this targetScopeElement was '${this.synchronizationVersion.id}'` +
|
|
2003
|
+
` which is changeset #${this.synchronizationVersion.index}. The transformer expected` +
|
|
2004
|
+
` #${this.synchronizationVersion.index + 1}.`);
|
|
2063
2005
|
}
|
|
2064
2006
|
nodeAssert(this._targetScopeProvenanceProps, "_targetScopeProvenanceProps should be set by now");
|
|
2065
2007
|
const changesetsToSkip = this.isReverseSynchronization
|
|
@@ -2081,15 +2023,44 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2081
2023
|
csFileProps.push(...fileProps);
|
|
2082
2024
|
}
|
|
2083
2025
|
this._csFileProps = csFileProps;
|
|
2084
|
-
|
|
2026
|
+
/** Theres a possibility that our csFileProps length is still 0 here, since we skip cs indices found in the pendingSync and pendingReverseSync indices arrays. */
|
|
2027
|
+
this._sourceChangeDataState =
|
|
2028
|
+
this._csFileProps.length === 0 ? "no-changes" : "has-changes";
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
2031
|
+
* The behavior of process is influenced by [[IModelTransformOptions.argsForProcessChanges]] being defined or not defined during construction passed of the IModelTransformer.
|
|
2032
|
+
* @section When argsForProcessChanges are defined:
|
|
2033
|
+
*
|
|
2034
|
+
* Export changes from the source iModel and import the transformed entities into the target iModel.
|
|
2035
|
+
* Inserts, updates, and deletes are determined by inspecting the changeset(s).
|
|
2036
|
+
*
|
|
2037
|
+
* Notes:
|
|
2038
|
+
* - the transformer assumes that you saveChanges after processing changes. You should not modify the iModel after processChanges until saveChanges,
|
|
2039
|
+
* failure to do so may result in corrupted
|
|
2040
|
+
* data loss in future branch operations
|
|
2041
|
+
* - if no startChangesetId or startChangeset option is provided as part of the ProcessChangesOptions, the next unsynchronized changeset
|
|
2042
|
+
* will automatically be determined and used
|
|
2043
|
+
* - To form a range of versions to process, set `startChangesetId` for the start (inclusive) of the desired range and open the source iModel as of the end (inclusive) of the desired range.
|
|
2044
|
+
*
|
|
2045
|
+
* @section When argsForProcessChanges are undefined:
|
|
2046
|
+
*
|
|
2047
|
+
* Export everything from the source iModel and import the transformed entities into the target iModel.
|
|
2048
|
+
*
|
|
2049
|
+
* Notes:
|
|
2050
|
+
* - [[processSchemas]] is not called automatically since the target iModel may want a different collection of schemas.
|
|
2051
|
+
*
|
|
2052
|
+
*/
|
|
2053
|
+
async process() {
|
|
2054
|
+
await this.initialize();
|
|
2055
|
+
this.logSettings();
|
|
2056
|
+
return this._options.argsForProcessChanges !== undefined
|
|
2057
|
+
? this.processChanges(this._options.argsForProcessChanges)
|
|
2058
|
+
: this.processAll();
|
|
2085
2059
|
}
|
|
2086
2060
|
/** Export everything from the source iModel and import the transformed entities into the target iModel.
|
|
2087
2061
|
* @note [[processSchemas]] is not called automatically since the target iModel may want a different collection of schemas.
|
|
2088
2062
|
*/
|
|
2089
|
-
async processAll(
|
|
2090
|
-
this.logSettings();
|
|
2091
|
-
this.initScopeProvenance();
|
|
2092
|
-
await this.initialize();
|
|
2063
|
+
async processAll() {
|
|
2093
2064
|
await this.exporter.exportCodeSpecs();
|
|
2094
2065
|
await this.exporter.exportFonts();
|
|
2095
2066
|
if (this._options.skipPropagateChangesToRootElements) {
|
|
@@ -2101,9 +2072,10 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2101
2072
|
else {
|
|
2102
2073
|
await this.exporter.exportModel(core_common_1.IModel.repositoryModelId);
|
|
2103
2074
|
}
|
|
2075
|
+
this.completePartiallyCommittedElements();
|
|
2104
2076
|
await this.exporter["exportAllAspects"](); // eslint-disable-line @typescript-eslint/dot-notation
|
|
2077
|
+
this.completePartiallyCommittedAspects();
|
|
2105
2078
|
await this.exporter.exportRelationships(core_backend_1.ElementRefersToElements.classFullName);
|
|
2106
|
-
await this.processDeferredElements(); // eslint-disable-line deprecation/deprecation
|
|
2107
2079
|
if (this._options.forceExternalSourceAspectProvenance &&
|
|
2108
2080
|
this.shouldDetectDeletes()) {
|
|
2109
2081
|
// eslint-disable-next-line deprecation/deprecation
|
|
@@ -2114,7 +2086,7 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2114
2086
|
if (this._options.optimizeGeometry)
|
|
2115
2087
|
this.importer.optimizeGeometry(this._options.optimizeGeometry);
|
|
2116
2088
|
this.importer.computeProjectExtents();
|
|
2117
|
-
|
|
2089
|
+
this.finalizeTransformation();
|
|
2118
2090
|
}
|
|
2119
2091
|
markLastProvenance(sourceAspect, { isRelationship = false }) {
|
|
2120
2092
|
this._lastProvenanceEntityInfo =
|
|
@@ -2131,42 +2103,45 @@ class IModelTransformer extends IModelExporter_1.IModelExportHandler {
|
|
|
2131
2103
|
}
|
|
2132
2104
|
/** Export changes from the source iModel and import the transformed entities into the target iModel.
|
|
2133
2105
|
* Inserts, updates, and deletes are determined by inspecting the changeset(s).
|
|
2134
|
-
* @note the transformer
|
|
2106
|
+
* @note the transformer assumes that you saveChanges after processing changes. You should not
|
|
2107
|
+
* modify the iModel after processChanges until saveChanges, failure to do so may result in corrupted
|
|
2108
|
+
* data loss in future branch operations
|
|
2135
2109
|
* @note if no startChangesetId or startChangeset option is provided as part of the ProcessChangesOptions, the next unsynchronized changeset
|
|
2136
2110
|
* will automatically be determined and used
|
|
2137
2111
|
* @note To form a range of versions to process, set `startChangesetId` for the start (inclusive) of the desired range and open the source iModel as of the end (inclusive) of the desired range.
|
|
2138
2112
|
*/
|
|
2139
2113
|
async processChanges(options) {
|
|
2140
|
-
this._isSynchronization = true;
|
|
2141
|
-
this.initScopeProvenance();
|
|
2142
|
-
this.logSettings();
|
|
2143
|
-
await this.initialize(options);
|
|
2144
2114
|
// must wait for initialization of synchronization provenance data
|
|
2145
2115
|
await this.exporter.exportChanges(this.getExportInitOpts(options));
|
|
2146
|
-
|
|
2116
|
+
this.completePartiallyCommittedElements();
|
|
2117
|
+
this.completePartiallyCommittedAspects();
|
|
2147
2118
|
if (this._options.optimizeGeometry)
|
|
2148
2119
|
this.importer.optimizeGeometry(this._options.optimizeGeometry);
|
|
2149
2120
|
this.importer.computeProjectExtents();
|
|
2150
|
-
|
|
2121
|
+
this.finalizeTransformation();
|
|
2122
|
+
const defaultSaveTargetChanges = () => {
|
|
2123
|
+
this.targetDb.saveChanges();
|
|
2124
|
+
};
|
|
2125
|
+
await (options.saveTargetChanges ?? defaultSaveTargetChanges)(this);
|
|
2151
2126
|
}
|
|
2152
2127
|
/** Changeset data must be initialized in order to build correct changeOptions.
|
|
2153
2128
|
* Call [[IModelTransformer.initialize]] for initialization of synchronization provenance data
|
|
2154
2129
|
*/
|
|
2155
2130
|
getExportInitOpts(opts) {
|
|
2156
|
-
if (!this.
|
|
2131
|
+
if (!this._options.argsForProcessChanges)
|
|
2157
2132
|
return {};
|
|
2133
|
+
const startChangeset = "startChangeset" in opts ? opts.startChangeset : undefined;
|
|
2158
2134
|
return {
|
|
2159
|
-
skipPropagateChangesToRootElements: this._options.skipPropagateChangesToRootElements
|
|
2160
|
-
accessToken: opts.accessToken,
|
|
2135
|
+
skipPropagateChangesToRootElements: this._options.skipPropagateChangesToRootElements,
|
|
2161
2136
|
...(this._csFileProps
|
|
2162
2137
|
? { csFileProps: this._csFileProps }
|
|
2163
2138
|
: this._changesetRanges
|
|
2164
2139
|
? { changesetRanges: this._changesetRanges }
|
|
2165
|
-
:
|
|
2166
|
-
? { startChangeset
|
|
2140
|
+
: startChangeset
|
|
2141
|
+
? { startChangeset }
|
|
2167
2142
|
: {
|
|
2168
2143
|
startChangeset: {
|
|
2169
|
-
index: this.
|
|
2144
|
+
index: this.synchronizationVersion.index + 1,
|
|
2170
2145
|
},
|
|
2171
2146
|
}),
|
|
2172
2147
|
};
|
|
@@ -2245,8 +2220,7 @@ class TemplateModelCloner extends IModelTransformer {
|
|
|
2245
2220
|
}
|
|
2246
2221
|
/** Cloning from a template requires this override of onTransformElement. */
|
|
2247
2222
|
onTransformElement(sourceElement) {
|
|
2248
|
-
|
|
2249
|
-
const referenceIds = sourceElement.getReferenceConcreteIds();
|
|
2223
|
+
const referenceIds = sourceElement.getReferenceIds();
|
|
2250
2224
|
referenceIds.forEach((referenceId) => {
|
|
2251
2225
|
// TODO: consider going through all definition elements at once and remapping them to themselves
|
|
2252
2226
|
if (!core_backend_1.EntityReferences.isValid(this.context.findTargetEntityId(referenceId))) {
|