@itwin/core-backend 5.6.0-dev.8 → 5.7.0-dev.2
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 +11 -1
- package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
- package/lib/cjs/BriefcaseManager.js +37 -14
- package/lib/cjs/BriefcaseManager.js.map +1 -1
- package/lib/cjs/IModelDb.d.ts +33 -24
- package/lib/cjs/IModelDb.d.ts.map +1 -1
- package/lib/cjs/IModelDb.js +42 -4
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/cjs/SQLiteDb.d.ts.map +1 -1
- package/lib/cjs/SQLiteDb.js +0 -1
- package/lib/cjs/SQLiteDb.js.map +1 -1
- package/lib/cjs/SqliteChangesetReader.d.ts +5 -0
- package/lib/cjs/SqliteChangesetReader.d.ts.map +1 -1
- package/lib/cjs/SqliteChangesetReader.js +7 -0
- package/lib/cjs/SqliteChangesetReader.js.map +1 -1
- package/lib/cjs/StashManager.d.ts +1 -2
- package/lib/cjs/StashManager.d.ts.map +1 -1
- package/lib/cjs/StashManager.js.map +1 -1
- package/lib/cjs/TxnManager.d.ts +74 -52
- package/lib/cjs/TxnManager.d.ts.map +1 -1
- package/lib/cjs/TxnManager.js +114 -26
- package/lib/cjs/TxnManager.js.map +1 -1
- package/lib/cjs/annotations/TextAnnotationElement.d.ts +1 -1
- package/lib/cjs/annotations/TextAnnotationElement.d.ts.map +1 -1
- package/lib/cjs/annotations/TextAnnotationElement.js +14 -1
- package/lib/cjs/annotations/TextAnnotationElement.js.map +1 -1
- package/lib/cjs/annotations/TextAnnotationGeometry.d.ts +14 -0
- package/lib/cjs/annotations/TextAnnotationGeometry.d.ts.map +1 -1
- package/lib/cjs/annotations/TextAnnotationGeometry.js +29 -10
- package/lib/cjs/annotations/TextAnnotationGeometry.js.map +1 -1
- package/lib/cjs/annotations/TextBlockLayout.js +6 -6
- package/lib/cjs/annotations/TextBlockLayout.js.map +1 -1
- package/lib/cjs/rpc-impl/RpcBriefcaseUtility.d.ts.map +1 -1
- package/lib/cjs/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
- package/lib/esm/BriefcaseManager.d.ts.map +1 -1
- package/lib/esm/BriefcaseManager.js +37 -14
- package/lib/esm/BriefcaseManager.js.map +1 -1
- package/lib/esm/IModelDb.d.ts +33 -24
- package/lib/esm/IModelDb.d.ts.map +1 -1
- package/lib/esm/IModelDb.js +42 -4
- package/lib/esm/IModelDb.js.map +1 -1
- package/lib/esm/SQLiteDb.d.ts.map +1 -1
- package/lib/esm/SQLiteDb.js +0 -1
- package/lib/esm/SQLiteDb.js.map +1 -1
- package/lib/esm/SqliteChangesetReader.d.ts +5 -0
- package/lib/esm/SqliteChangesetReader.d.ts.map +1 -1
- package/lib/esm/SqliteChangesetReader.js +7 -0
- package/lib/esm/SqliteChangesetReader.js.map +1 -1
- package/lib/esm/StashManager.d.ts +1 -2
- package/lib/esm/StashManager.d.ts.map +1 -1
- package/lib/esm/StashManager.js.map +1 -1
- package/lib/esm/TxnManager.d.ts +74 -52
- package/lib/esm/TxnManager.d.ts.map +1 -1
- package/lib/esm/TxnManager.js +114 -26
- package/lib/esm/TxnManager.js.map +1 -1
- package/lib/esm/annotations/TextAnnotationElement.d.ts +1 -1
- package/lib/esm/annotations/TextAnnotationElement.d.ts.map +1 -1
- package/lib/esm/annotations/TextAnnotationElement.js +14 -1
- package/lib/esm/annotations/TextAnnotationElement.js.map +1 -1
- package/lib/esm/annotations/TextAnnotationGeometry.d.ts +14 -0
- package/lib/esm/annotations/TextAnnotationGeometry.d.ts.map +1 -1
- package/lib/esm/annotations/TextAnnotationGeometry.js +29 -10
- package/lib/esm/annotations/TextAnnotationGeometry.js.map +1 -1
- package/lib/esm/annotations/TextBlockLayout.js +6 -6
- package/lib/esm/annotations/TextBlockLayout.js.map +1 -1
- package/lib/esm/rpc-impl/RpcBriefcaseUtility.d.ts.map +1 -1
- package/lib/esm/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
- package/lib/esm/test/annotations/LeaderGeometry.test.js +2 -3
- package/lib/esm/test/annotations/LeaderGeometry.test.js.map +1 -1
- package/lib/esm/test/annotations/TextAnnotation.test.js +39 -4
- package/lib/esm/test/annotations/TextAnnotation.test.js.map +1 -1
- package/lib/esm/test/annotations/TextBlock.test.js +49 -42
- package/lib/esm/test/annotations/TextBlock.test.js.map +1 -1
- package/lib/esm/test/hubaccess/Rebase.test.js +546 -22
- package/lib/esm/test/hubaccess/Rebase.test.js.map +1 -1
- package/lib/esm/test/imodel/SchemaXmlImport.test.js +87 -44
- package/lib/esm/test/imodel/SchemaXmlImport.test.js.map +1 -1
- package/lib/esm/test/schema/SchemaImportCallbacks.test.js +4 -4
- package/lib/esm/test/schema/SchemaImportCallbacks.test.js.map +1 -1
- package/lib/esm/test/standalone/ChangeMerge.test.js +9 -9
- package/lib/esm/test/standalone/ChangeMerge.test.js.map +1 -1
- package/package.json +14 -14
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
import { DbResult, Guid } from "@itwin/core-bentley";
|
|
6
|
-
import { Code, IModel, QueryBinder, SubCategoryAppearance } from "@itwin/core-common";
|
|
6
|
+
import { Code, GeometryStreamBuilder, IModel, QueryBinder, SubCategoryAppearance } from "@itwin/core-common";
|
|
7
7
|
import * as chai from "chai";
|
|
8
8
|
import * as chaiAsPromised from "chai-as-promised";
|
|
9
9
|
import { HubWrappers, IModelTestUtils, KnownTestLocations } from "..";
|
|
10
|
-
import { BriefcaseManager, ChangesetECAdaptor, ChannelControl, DrawingCategory, ElementGroupsMembers, IModelHost, SqliteChangesetReader } from "../../core-backend";
|
|
10
|
+
import { _nativeDb, BriefcaseManager, ChangesetECAdaptor, ChannelControl, DrawingCategory, ElementGroupsMembers, IModelHost, SqliteChangesetReader } from "../../core-backend";
|
|
11
11
|
import { HubMock } from "../../internal/HubMock";
|
|
12
12
|
import { StashManager } from "../../StashManager";
|
|
13
13
|
import { existsSync, unlinkSync, writeFileSync } from "fs";
|
|
14
14
|
import * as path from "path";
|
|
15
|
+
import { LineSegment3d, Point3d } from "@itwin/core-geometry";
|
|
15
16
|
chai.use(chaiAsPromised);
|
|
16
17
|
class TestIModel {
|
|
17
18
|
iModelId = "";
|
|
@@ -33,6 +34,10 @@ class TestIModel {
|
|
|
33
34
|
<BaseClass>bis:GraphicalElement2d</BaseClass>
|
|
34
35
|
<ECProperty propertyName="prop1" typeName="string" />
|
|
35
36
|
</ECEntityClass>
|
|
37
|
+
<ECEntityClass typeName="A1Recipe2d">
|
|
38
|
+
<BaseClass>bis:TemplateRecipe2d</BaseClass>
|
|
39
|
+
<ECProperty propertyName="prop1" typeName="string" />
|
|
40
|
+
</ECEntityClass>
|
|
36
41
|
<ECRelationshipClass typeName="A1OwnsA1" modifier="None" strength="embedding">
|
|
37
42
|
<BaseClass>bis:ElementOwnsChildElements</BaseClass>
|
|
38
43
|
<Source multiplicity="(0..1)" roleLabel="owns" polymorphic="true">
|
|
@@ -65,31 +70,73 @@ class TestIModel {
|
|
|
65
70
|
this.briefcases.push(b);
|
|
66
71
|
return b;
|
|
67
72
|
}
|
|
73
|
+
async insertRecipe2d(b, markAsIndirect) {
|
|
74
|
+
await b.locks.acquireLocks({ shared: [IModel.dictionaryId] });
|
|
75
|
+
const baseProps = {
|
|
76
|
+
classFullName: "TestDomain:A1Recipe2d",
|
|
77
|
+
model: IModel.dictionaryId,
|
|
78
|
+
code: Code.createEmpty(),
|
|
79
|
+
};
|
|
80
|
+
let id = "";
|
|
81
|
+
if (markAsIndirect) {
|
|
82
|
+
b.txns.withIndirectTxnMode(() => {
|
|
83
|
+
id = b.elements.insertElement({ ...baseProps, prop1: `${this._data++}` });
|
|
84
|
+
});
|
|
85
|
+
return id;
|
|
86
|
+
}
|
|
87
|
+
return b.elements.insertElement({ ...baseProps, prop1: `${this._data++}` });
|
|
88
|
+
}
|
|
89
|
+
async updateRecipe2d(b, id, markAsIndirect) {
|
|
90
|
+
await b.locks.acquireLocks({ shared: [IModel.dictionaryId], exclusive: [id] });
|
|
91
|
+
const elProps = b.elements.getElementProps(id);
|
|
92
|
+
if (markAsIndirect) {
|
|
93
|
+
b.txns.withIndirectTxnMode(() => {
|
|
94
|
+
b.elements.updateElement({ ...elProps, prop1: `${this._data++}` });
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
b.elements.updateElement({ ...elProps, prop1: `${this._data++}` });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
68
101
|
async insertElement(b, markAsIndirect) {
|
|
69
102
|
await b.locks.acquireLocks({ shared: [this.drawingModelId] });
|
|
103
|
+
const builder = new GeometryStreamBuilder();
|
|
104
|
+
const p1 = Point3d.createZero();
|
|
105
|
+
const p2 = Point3d.createFrom({ x: Math.random() * 10.0 + 5.0, y: 0.0, z: 0.0 });
|
|
106
|
+
const circle = LineSegment3d.create(p1, p2);
|
|
107
|
+
builder.appendGeometry(circle);
|
|
70
108
|
const baseProps = {
|
|
71
109
|
classFullName: "TestDomain:a1",
|
|
72
110
|
model: this.drawingModelId,
|
|
73
111
|
category: this.drawingCategoryId,
|
|
74
112
|
code: Code.createEmpty(),
|
|
113
|
+
geom: builder.geometryStream,
|
|
114
|
+
prop1: `${this._data++}`,
|
|
75
115
|
};
|
|
76
116
|
let id = "";
|
|
77
117
|
if (markAsIndirect) {
|
|
78
118
|
b.txns.withIndirectTxnMode(() => {
|
|
79
|
-
id = b.elements.insertElement(
|
|
119
|
+
id = b.elements.insertElement(baseProps);
|
|
80
120
|
});
|
|
81
121
|
return id;
|
|
82
122
|
}
|
|
83
|
-
|
|
123
|
+
baseProps.prop1 = `${this._data++}`;
|
|
124
|
+
return b.elements.insertElement(baseProps);
|
|
84
125
|
}
|
|
85
|
-
async
|
|
126
|
+
async insertElementEx(b, args) {
|
|
86
127
|
await b.locks.acquireLocks({ shared: [this.drawingModelId] });
|
|
128
|
+
const builder = new GeometryStreamBuilder();
|
|
129
|
+
const p1 = Point3d.createZero();
|
|
130
|
+
const p2 = Point3d.createFrom({ x: Math.random() * 10.0 + 5.0, y: 0.0, z: 0.0 });
|
|
131
|
+
const circle = LineSegment3d.create(p1, p2);
|
|
132
|
+
builder.appendGeometry(circle);
|
|
87
133
|
const props = {
|
|
88
134
|
classFullName: "TestDomain:a1",
|
|
89
135
|
model: this.drawingModelId,
|
|
90
136
|
category: this.drawingCategoryId,
|
|
91
137
|
code: Code.createEmpty(),
|
|
92
138
|
parent: args?.parent,
|
|
139
|
+
geom: builder.geometryStream,
|
|
93
140
|
prop1: args?.prop1 ?? `${this._data++}`
|
|
94
141
|
};
|
|
95
142
|
let id = "";
|
|
@@ -101,9 +148,17 @@ class TestIModel {
|
|
|
101
148
|
}
|
|
102
149
|
return b.elements.insertElement(props);
|
|
103
150
|
}
|
|
104
|
-
async updateElement(b, id, markAsIndirect) {
|
|
151
|
+
async updateElement(b, id, markAsIndirect, updateGeom) {
|
|
105
152
|
await b.locks.acquireLocks({ shared: [this.drawingModelId], exclusive: [id] });
|
|
106
153
|
const elProps = b.elements.getElementProps(id);
|
|
154
|
+
if (updateGeom) {
|
|
155
|
+
const builder = new GeometryStreamBuilder();
|
|
156
|
+
const p1 = Point3d.createZero();
|
|
157
|
+
const p2 = Point3d.createFrom({ x: Math.random() * 10.0 + 10.0, y: 0.0, z: 0.0 });
|
|
158
|
+
const circle = LineSegment3d.create(p1, p2);
|
|
159
|
+
builder.appendGeometry(circle);
|
|
160
|
+
elProps.geom = builder.geometryStream;
|
|
161
|
+
}
|
|
107
162
|
if (markAsIndirect) {
|
|
108
163
|
b.txns.withIndirectTxnMode(() => {
|
|
109
164
|
b.elements.updateElement({ ...elProps, prop1: `${this._data++}` });
|
|
@@ -129,6 +184,18 @@ class TestIModel {
|
|
|
129
184
|
HubMock.shutdown();
|
|
130
185
|
}
|
|
131
186
|
}
|
|
187
|
+
const removePropertyRecursive = (obj, prop) => {
|
|
188
|
+
if (obj && typeof obj === "object") {
|
|
189
|
+
Object.keys(obj).forEach((key) => {
|
|
190
|
+
if (key === prop) {
|
|
191
|
+
delete obj[key];
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
removePropertyRecursive(obj[key], prop);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
};
|
|
132
199
|
describe("rebase changes & stashing api", function () {
|
|
133
200
|
let testIModel;
|
|
134
201
|
before(async () => {
|
|
@@ -428,7 +495,7 @@ describe("rebase changes & stashing api", function () {
|
|
|
428
495
|
b1.saveChanges();
|
|
429
496
|
await chai.expect(b1.txns.withIndirectTxnModeAsync(async () => {
|
|
430
497
|
await b1.pushChanges({ description: "test" });
|
|
431
|
-
})).to.be.rejectedWith("Cannot
|
|
498
|
+
})).to.be.rejectedWith("Cannot pull and apply changeset while in an indirect change scope");
|
|
432
499
|
await b1.pushChanges({ description: "test" });
|
|
433
500
|
});
|
|
434
501
|
it("should fail to saveFileProperty/deleteFileProperty in indirect scope", async () => {
|
|
@@ -669,11 +736,11 @@ describe("rebase changes & stashing api", function () {
|
|
|
669
736
|
await b2.pullChanges();
|
|
670
737
|
chai.expect(b2.changeset.index).to.equals(4);
|
|
671
738
|
const elBefore = b2.elements.tryGetElementProps(e1);
|
|
672
|
-
chai.expect(elBefore.prop1).to.equals("
|
|
739
|
+
chai.expect(elBefore.prop1).to.equals("3");
|
|
673
740
|
// restore stash should succeed as now it can obtain lock
|
|
674
741
|
await StashManager.restore({ db: b2, stash: b2Stash1 });
|
|
675
742
|
const elAfter = b2.elements.tryGetElementProps(e1);
|
|
676
|
-
chai.expect(elAfter.prop1).to.equals("
|
|
743
|
+
chai.expect(elAfter.prop1).to.equals("2");
|
|
677
744
|
await b2.pushChanges({ description: `${e1} updated` });
|
|
678
745
|
});
|
|
679
746
|
it("schema change should not be stashed", async () => {
|
|
@@ -686,6 +753,10 @@ describe("rebase changes & stashing api", function () {
|
|
|
686
753
|
<ECProperty propertyName="prop1" typeName="string" />
|
|
687
754
|
<ECProperty propertyName="prop2" typeName="string" />
|
|
688
755
|
</ECEntityClass>
|
|
756
|
+
<ECEntityClass typeName="A1Recipe2d">
|
|
757
|
+
<BaseClass>bis:TemplateRecipe2d</BaseClass>
|
|
758
|
+
<ECProperty propertyName="prop1" typeName="string" />
|
|
759
|
+
</ECEntityClass>
|
|
689
760
|
<ECRelationshipClass typeName="A1OwnsA1" modifier="None" strength="embedding">
|
|
690
761
|
<BaseClass>bis:ElementOwnsChildElements</BaseClass>
|
|
691
762
|
<Source multiplicity="(0..1)" roleLabel="owns" polymorphic="true">
|
|
@@ -783,7 +854,7 @@ describe("rebase changes & stashing api", function () {
|
|
|
783
854
|
const b1 = await testIModel.openBriefcase();
|
|
784
855
|
const b2 = await testIModel.openBriefcase();
|
|
785
856
|
const parentId = await testIModel.insertElement(b1);
|
|
786
|
-
const childId = await testIModel.
|
|
857
|
+
const childId = await testIModel.insertElementEx(b1, { parent: { id: parentId, relClassName: "TestDomain:A1OwnsA1" } });
|
|
787
858
|
b1.saveChanges("insert parent and child");
|
|
788
859
|
await b1.pushChanges({ description: `inserted parent ${parentId} and child ${childId}` });
|
|
789
860
|
await b2.pullChanges();
|
|
@@ -791,7 +862,7 @@ describe("rebase changes & stashing api", function () {
|
|
|
791
862
|
await testIModel.deleteElement(b1, childId);
|
|
792
863
|
b1.saveChanges("delete child");
|
|
793
864
|
// no exclusive lock required on child1
|
|
794
|
-
const grandChildId = await testIModel.
|
|
865
|
+
const grandChildId = await testIModel.insertElementEx(b2, { parent: { id: childId, relClassName: "TestDomain:A1OwnsA1" }, markAsIndirect: true });
|
|
795
866
|
b2.saveChanges("delete child and insert grandchild");
|
|
796
867
|
await b1.pushChanges({ description: `deleted child ${childId}` });
|
|
797
868
|
// should fail to pull and rebase changes.
|
|
@@ -919,7 +990,7 @@ describe("rebase changes & stashing api", function () {
|
|
|
919
990
|
onRebase: {
|
|
920
991
|
beginCount: 0,
|
|
921
992
|
endCount: 0,
|
|
922
|
-
|
|
993
|
+
beginTxns: [],
|
|
923
994
|
},
|
|
924
995
|
onRebaseTxn: {
|
|
925
996
|
beginTxns: [],
|
|
@@ -928,44 +999,174 @@ describe("rebase changes & stashing api", function () {
|
|
|
928
999
|
rebaseHandler: {
|
|
929
1000
|
shouldReinstate: [],
|
|
930
1001
|
recompute: [],
|
|
931
|
-
}
|
|
1002
|
+
},
|
|
1003
|
+
pullMerge: {
|
|
1004
|
+
beginCount: 0,
|
|
1005
|
+
endCount: 0,
|
|
1006
|
+
beginChangeset: [],
|
|
1007
|
+
endChangeset: [],
|
|
1008
|
+
},
|
|
1009
|
+
applyIncomingChanges: {
|
|
1010
|
+
beginCount: 0,
|
|
1011
|
+
endCount: 0,
|
|
1012
|
+
beginChangesets: [],
|
|
1013
|
+
endChangesets: [],
|
|
1014
|
+
},
|
|
1015
|
+
reverseLocalChanges: {
|
|
1016
|
+
beginCount: 0,
|
|
1017
|
+
endCount: 0,
|
|
1018
|
+
txns: [],
|
|
1019
|
+
},
|
|
1020
|
+
downloadChangesets: {
|
|
1021
|
+
beginCount: 0,
|
|
1022
|
+
endCount: 0,
|
|
1023
|
+
},
|
|
932
1024
|
};
|
|
933
1025
|
const resetEvent = () => {
|
|
934
1026
|
events.onRebase.beginCount = 0;
|
|
935
1027
|
events.onRebase.endCount = 0;
|
|
936
|
-
events.onRebase.
|
|
1028
|
+
events.onRebase.beginTxns = [];
|
|
937
1029
|
events.onRebaseTxn.beginTxns = [];
|
|
938
1030
|
events.onRebaseTxn.endTxns = [];
|
|
939
1031
|
events.rebaseHandler.shouldReinstate = [];
|
|
940
1032
|
events.rebaseHandler.recompute = [];
|
|
1033
|
+
events.pullMerge.beginCount = 0;
|
|
1034
|
+
events.pullMerge.endCount = 0;
|
|
1035
|
+
events.pullMerge.beginChangeset = [];
|
|
1036
|
+
events.pullMerge.endChangeset = [];
|
|
1037
|
+
events.applyIncomingChanges.beginCount = 0;
|
|
1038
|
+
events.applyIncomingChanges.endCount = 0;
|
|
1039
|
+
events.applyIncomingChanges.beginChangesets = [];
|
|
1040
|
+
events.applyIncomingChanges.endChangesets = [];
|
|
1041
|
+
events.reverseLocalChanges.beginCount = 0;
|
|
1042
|
+
events.reverseLocalChanges.endCount = 0;
|
|
1043
|
+
events.reverseLocalChanges.txns = [];
|
|
1044
|
+
events.downloadChangesets.beginCount = 0;
|
|
1045
|
+
events.downloadChangesets.endCount = 0;
|
|
941
1046
|
};
|
|
942
|
-
|
|
1047
|
+
// onPullMergeXXXX
|
|
1048
|
+
b2.txns.rebaser.onPullMergeBegin.addListener((changeset) => {
|
|
1049
|
+
events.pullMerge.beginCount++;
|
|
1050
|
+
events.pullMerge.beginChangeset.push(changeset);
|
|
1051
|
+
});
|
|
1052
|
+
b2.txns.rebaser.onPullMergeEnd.addListener((changeset) => {
|
|
1053
|
+
events.pullMerge.endCount++;
|
|
1054
|
+
events.pullMerge.endChangeset.push(changeset);
|
|
1055
|
+
});
|
|
1056
|
+
// onApplyIncomingChangesXXXX
|
|
1057
|
+
b2.txns.rebaser.onApplyIncomingChangesBegin.addListener((changesets) => {
|
|
1058
|
+
events.applyIncomingChanges.beginCount++;
|
|
1059
|
+
events.applyIncomingChanges.beginChangesets.push(...changesets);
|
|
1060
|
+
});
|
|
1061
|
+
b2.txns.rebaser.onApplyIncomingChangesEnd.addListener((changesets) => {
|
|
1062
|
+
events.applyIncomingChanges.endCount++;
|
|
1063
|
+
events.applyIncomingChanges.endChangesets.push(...changesets);
|
|
1064
|
+
});
|
|
1065
|
+
// onReverseLocalChangesXXXX
|
|
1066
|
+
b2.txns.rebaser.onReverseLocalChangesBegin.addListener(() => {
|
|
1067
|
+
events.reverseLocalChanges.beginCount++;
|
|
1068
|
+
});
|
|
1069
|
+
b2.txns.rebaser.onReverseLocalChangesEnd.addListener((txns) => {
|
|
1070
|
+
events.reverseLocalChanges.endCount++;
|
|
1071
|
+
removePropertyRecursive(txns, "timestamp"); // it changes on each run, so remove it for comparison
|
|
1072
|
+
events.reverseLocalChanges.txns.push(...txns);
|
|
1073
|
+
});
|
|
1074
|
+
// onDownloadChangesetsXXXX
|
|
1075
|
+
b2.txns.rebaser.onDownloadChangesetsBegin.addListener(() => {
|
|
1076
|
+
events.downloadChangesets.beginCount++;
|
|
1077
|
+
});
|
|
1078
|
+
b2.txns.rebaser.onDownloadChangesetsEnd.addListener(() => {
|
|
1079
|
+
events.downloadChangesets.endCount++;
|
|
1080
|
+
});
|
|
1081
|
+
// onRebaseXXXX
|
|
1082
|
+
b2.txns.rebaser.onRebaseBegin.addListener((txns) => {
|
|
943
1083
|
events.onRebase.beginCount++;
|
|
944
|
-
|
|
1084
|
+
removePropertyRecursive(txns, "timestamp"); // it changes on each run, so remove it for comparison
|
|
1085
|
+
events.onRebase.beginTxns.push(...txns);
|
|
945
1086
|
});
|
|
946
|
-
b2.txns.onRebaseEnd.addListener(() => {
|
|
1087
|
+
b2.txns.rebaser.onRebaseEnd.addListener(() => {
|
|
947
1088
|
events.onRebase.endCount++;
|
|
948
1089
|
});
|
|
949
|
-
|
|
1090
|
+
// onRebaseTxnXXXX
|
|
1091
|
+
b2.txns.rebaser.onRebaseTxnBegin.addListener((txn) => {
|
|
1092
|
+
removePropertyRecursive(txn, "timestamp"); // it changes on each run, so remove it for comparison
|
|
950
1093
|
events.onRebaseTxn.beginTxns.push(txn);
|
|
951
1094
|
});
|
|
952
|
-
b2.txns.onRebaseTxnEnd.addListener((txn) => {
|
|
1095
|
+
b2.txns.rebaser.onRebaseTxnEnd.addListener((txn) => {
|
|
1096
|
+
removePropertyRecursive(txn, "timestamp"); // it changes on each run, so remove it for comparison
|
|
953
1097
|
events.onRebaseTxn.endTxns.push(txn);
|
|
954
1098
|
});
|
|
955
1099
|
b2.txns.rebaser.setCustomHandler({
|
|
956
1100
|
shouldReinstate: (_txn) => {
|
|
1101
|
+
// shouldReinstate
|
|
1102
|
+
removePropertyRecursive(_txn, "timestamp"); // it changes on each run, so remove it for comparison
|
|
957
1103
|
events.rebaseHandler.shouldReinstate.push(_txn);
|
|
958
1104
|
return true;
|
|
959
1105
|
},
|
|
960
1106
|
recompute: async (_txn) => {
|
|
1107
|
+
// recompute
|
|
1108
|
+
removePropertyRecursive(_txn, "timestamp"); // it changes on each run, so remove it for comparison
|
|
961
1109
|
events.rebaseHandler.recompute.push(_txn);
|
|
962
1110
|
},
|
|
963
1111
|
});
|
|
964
1112
|
resetEvent();
|
|
965
1113
|
await b2.pullChanges();
|
|
1114
|
+
// pullMerge events
|
|
1115
|
+
chai.expect(events.pullMerge.beginCount).to.equal(1);
|
|
1116
|
+
chai.expect(events.pullMerge.endCount).to.equal(1);
|
|
1117
|
+
chai.expect((events.pullMerge.beginChangeset[0].index)).to.equal(3);
|
|
1118
|
+
chai.expect((events.pullMerge.endChangeset[0].index)).to.equal(4);
|
|
1119
|
+
// applyIncomingChanges events
|
|
1120
|
+
chai.expect(events.applyIncomingChanges.beginCount).to.equal(1);
|
|
1121
|
+
chai.expect(events.applyIncomingChanges.endCount).to.equal(1);
|
|
1122
|
+
chai.expect(events.applyIncomingChanges.beginChangesets.map((cs) => cs.index)).to.deep.equal([4]);
|
|
1123
|
+
chai.expect(events.applyIncomingChanges.endChangesets.map((cs) => cs.index)).to.deep.equal([4]);
|
|
1124
|
+
// downloadChangesets events
|
|
1125
|
+
chai.expect(events.downloadChangesets.beginCount).to.equal(1);
|
|
1126
|
+
chai.expect(events.downloadChangesets.endCount).to.equal(1);
|
|
1127
|
+
// reverseLocalChanges events
|
|
1128
|
+
chai.expect(events.reverseLocalChanges.beginCount).to.equal(1);
|
|
1129
|
+
chai.expect(events.reverseLocalChanges.endCount).to.equal(1);
|
|
1130
|
+
chai.expect(events.reverseLocalChanges.txns.map((txn) => txn.id)).to.deep.equal(["0x100000000", "0x100000001", "0x100000002"]);
|
|
1131
|
+
// rebase events
|
|
966
1132
|
chai.expect(events.onRebase.beginCount).to.equal(1);
|
|
967
1133
|
chai.expect(events.onRebase.endCount).to.equal(1);
|
|
968
|
-
chai.expect(events.onRebase.
|
|
1134
|
+
chai.expect(events.onRebase.beginTxns).to.deep.equal([
|
|
1135
|
+
{
|
|
1136
|
+
grouped: false,
|
|
1137
|
+
id: "0x100000000",
|
|
1138
|
+
nextId: "0x100000001",
|
|
1139
|
+
props: {
|
|
1140
|
+
description: "first change"
|
|
1141
|
+
},
|
|
1142
|
+
reversed: true,
|
|
1143
|
+
sessionId: 1,
|
|
1144
|
+
type: "Data"
|
|
1145
|
+
},
|
|
1146
|
+
{
|
|
1147
|
+
grouped: false,
|
|
1148
|
+
id: "0x100000001",
|
|
1149
|
+
nextId: "0x100000002",
|
|
1150
|
+
prevId: "0x100000000",
|
|
1151
|
+
props: {
|
|
1152
|
+
description: "second change",
|
|
1153
|
+
},
|
|
1154
|
+
reversed: true,
|
|
1155
|
+
sessionId: 1,
|
|
1156
|
+
type: "Data"
|
|
1157
|
+
},
|
|
1158
|
+
{
|
|
1159
|
+
grouped: false,
|
|
1160
|
+
id: "0x100000002",
|
|
1161
|
+
prevId: "0x100000001",
|
|
1162
|
+
props: {
|
|
1163
|
+
description: "third change"
|
|
1164
|
+
},
|
|
1165
|
+
reversed: true,
|
|
1166
|
+
sessionId: 1,
|
|
1167
|
+
type: "Data"
|
|
1168
|
+
}
|
|
1169
|
+
]);
|
|
969
1170
|
chai.expect(events.onRebaseTxn.beginTxns.map((txn) => txn.id)).to.deep.equal(["0x100000000", "0x100000001", "0x100000002"]);
|
|
970
1171
|
chai.expect(events.onRebaseTxn.endTxns.map((txn) => txn.id)).to.deep.equal(["0x100000000", "0x100000001", "0x100000002"]);
|
|
971
1172
|
chai.expect(events.rebaseHandler.shouldReinstate.map((txn) => txn.id)).to.deep.equal(["0x100000000", "0x100000001", "0x100000002"]);
|
|
@@ -976,17 +1177,243 @@ describe("rebase changes & stashing api", function () {
|
|
|
976
1177
|
await b1.pushChanges({ description: "update element 1 direct and 1 indirect" });
|
|
977
1178
|
await testIModel.insertElement(b2);
|
|
978
1179
|
await testIModel.insertElement(b2, true);
|
|
979
|
-
b2.saveChanges("
|
|
1180
|
+
b2.saveChanges("fourth change");
|
|
980
1181
|
resetEvent();
|
|
981
1182
|
await b2.pullChanges();
|
|
982
1183
|
chai.expect(events.onRebase.beginCount).to.equal(1);
|
|
983
1184
|
chai.expect(events.onRebase.endCount).to.equal(1);
|
|
984
|
-
chai.expect(events.onRebase.
|
|
1185
|
+
chai.expect(events.onRebase.beginTxns).to.deep.equal([
|
|
1186
|
+
{
|
|
1187
|
+
grouped: false,
|
|
1188
|
+
id: "0x100000000",
|
|
1189
|
+
nextId: "0x100000001",
|
|
1190
|
+
props: {
|
|
1191
|
+
description: "first change"
|
|
1192
|
+
},
|
|
1193
|
+
reversed: true,
|
|
1194
|
+
sessionId: 1,
|
|
1195
|
+
type: "Data"
|
|
1196
|
+
},
|
|
1197
|
+
{
|
|
1198
|
+
grouped: false,
|
|
1199
|
+
id: "0x100000001",
|
|
1200
|
+
nextId: "0x100000002",
|
|
1201
|
+
prevId: "0x100000000",
|
|
1202
|
+
props: {
|
|
1203
|
+
description: "second change"
|
|
1204
|
+
},
|
|
1205
|
+
reversed: true,
|
|
1206
|
+
sessionId: 1,
|
|
1207
|
+
type: "Data"
|
|
1208
|
+
},
|
|
1209
|
+
{
|
|
1210
|
+
grouped: false,
|
|
1211
|
+
id: "0x100000002",
|
|
1212
|
+
nextId: "0x100000003",
|
|
1213
|
+
prevId: "0x100000001",
|
|
1214
|
+
props: {
|
|
1215
|
+
description: "third change"
|
|
1216
|
+
},
|
|
1217
|
+
reversed: true,
|
|
1218
|
+
sessionId: 1,
|
|
1219
|
+
type: "Data"
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
grouped: false,
|
|
1223
|
+
id: "0x100000003",
|
|
1224
|
+
prevId: "0x100000002",
|
|
1225
|
+
props: {
|
|
1226
|
+
description: "fourth change"
|
|
1227
|
+
},
|
|
1228
|
+
reversed: true,
|
|
1229
|
+
sessionId: 1,
|
|
1230
|
+
type: "Data"
|
|
1231
|
+
}
|
|
1232
|
+
]);
|
|
985
1233
|
chai.expect(events.onRebaseTxn.beginTxns.map((txn) => txn.id)).to.deep.equal(["0x100000000", "0x100000001", "0x100000002", "0x100000003"]);
|
|
986
1234
|
chai.expect(events.onRebaseTxn.endTxns.map((txn) => txn.id)).to.deep.equal(["0x100000000", "0x100000001", "0x100000002", "0x100000003"]);
|
|
987
1235
|
chai.expect(events.rebaseHandler.shouldReinstate.map((txn) => txn.id)).to.deep.equal(["0x100000000", "0x100000001", "0x100000002", "0x100000003"]);
|
|
988
1236
|
chai.expect(events.rebaseHandler.recompute.map((txn) => txn.id)).to.deep.equal(["0x100000000", "0x100000001", "0x100000002", "0x100000003"]);
|
|
989
1237
|
});
|
|
1238
|
+
it("onModelGeometryChanged() not fired during rebase/pullMerge with no local change", async () => {
|
|
1239
|
+
const b1 = await testIModel.openBriefcase();
|
|
1240
|
+
const b2 = await testIModel.openBriefcase();
|
|
1241
|
+
const pushChangeFromB2 = async () => {
|
|
1242
|
+
await b2.pullChanges();
|
|
1243
|
+
await testIModel.insertElement(b2);
|
|
1244
|
+
b2.saveChanges();
|
|
1245
|
+
await b2.pushChanges({ description: "insert element on b2" });
|
|
1246
|
+
};
|
|
1247
|
+
const events = {
|
|
1248
|
+
modelGeometryChanged: [],
|
|
1249
|
+
};
|
|
1250
|
+
const getGeometryGuidFromB1 = (modelId) => {
|
|
1251
|
+
const modelProps = b1.models.tryGetModelProps(modelId);
|
|
1252
|
+
return modelProps?.geometryGuid;
|
|
1253
|
+
};
|
|
1254
|
+
const clearEvents = () => {
|
|
1255
|
+
events.modelGeometryChanged = [];
|
|
1256
|
+
};
|
|
1257
|
+
b1.txns.onModelGeometryChanged.addListener((changes) => {
|
|
1258
|
+
events.modelGeometryChanged.push(changes);
|
|
1259
|
+
});
|
|
1260
|
+
clearEvents();
|
|
1261
|
+
b1.txns.rebaser.setCustomHandler({
|
|
1262
|
+
shouldReinstate: (_txn) => {
|
|
1263
|
+
return true;
|
|
1264
|
+
},
|
|
1265
|
+
recompute: async (_txn) => {
|
|
1266
|
+
},
|
|
1267
|
+
});
|
|
1268
|
+
await pushChangeFromB2();
|
|
1269
|
+
clearEvents();
|
|
1270
|
+
const geomGuidBeforePull = getGeometryGuidFromB1("0x20000000001");
|
|
1271
|
+
chai.expect(geomGuidBeforePull).is.undefined;
|
|
1272
|
+
await b1.pushChanges({ description: "push changes on b1" });
|
|
1273
|
+
const geomGuidAfterPull = getGeometryGuidFromB1("0x20000000001");
|
|
1274
|
+
chai.expect(geomGuidAfterPull).is.undefined;
|
|
1275
|
+
chai.expect(events.modelGeometryChanged.length).to.equal(0);
|
|
1276
|
+
});
|
|
1277
|
+
it("onModelGeometryChanged() fired during rebase with geometric local change", async () => {
|
|
1278
|
+
const b1 = await testIModel.openBriefcase();
|
|
1279
|
+
const b2 = await testIModel.openBriefcase();
|
|
1280
|
+
const pushChangeFromB2 = async () => {
|
|
1281
|
+
await b2.pullChanges();
|
|
1282
|
+
await testIModel.insertElement(b2);
|
|
1283
|
+
b2.saveChanges();
|
|
1284
|
+
await b2.pushChanges({ description: "insert element on b2" });
|
|
1285
|
+
};
|
|
1286
|
+
const events = {
|
|
1287
|
+
modelGeometryChanged: [],
|
|
1288
|
+
onGeometryChanged: [],
|
|
1289
|
+
};
|
|
1290
|
+
const getGeometryGuidFromB1 = (modelId) => {
|
|
1291
|
+
const modelProps = b1.models.tryGetModelProps(modelId);
|
|
1292
|
+
return modelProps?.geometryGuid;
|
|
1293
|
+
};
|
|
1294
|
+
const clearEvents = () => {
|
|
1295
|
+
events.modelGeometryChanged = [];
|
|
1296
|
+
events.onGeometryChanged = [];
|
|
1297
|
+
};
|
|
1298
|
+
b1.txns.onModelGeometryChanged.addListener((changes) => {
|
|
1299
|
+
events.modelGeometryChanged.push(changes);
|
|
1300
|
+
});
|
|
1301
|
+
b1.txns.onGeometryChanged.addListener((changes) => {
|
|
1302
|
+
events.onGeometryChanged.push(changes);
|
|
1303
|
+
});
|
|
1304
|
+
clearEvents();
|
|
1305
|
+
const e1 = await testIModel.insertElement(b1);
|
|
1306
|
+
const e2 = await testIModel.insertElement(b1, true);
|
|
1307
|
+
chai.expect(e1).to.exist;
|
|
1308
|
+
chai.expect(e2).to.exist;
|
|
1309
|
+
b1.saveChanges(`insert element ${e1} and ${e2}`);
|
|
1310
|
+
chai.expect(events.modelGeometryChanged.length).to.equal(1);
|
|
1311
|
+
chai.expect(events.modelGeometryChanged[0].length).to.equal(1);
|
|
1312
|
+
chai.expect(events.modelGeometryChanged[0][0].id).to.equal("0x20000000001");
|
|
1313
|
+
chai.assert(Guid.isGuid(events.modelGeometryChanged[0][0].guid));
|
|
1314
|
+
b1.txns.rebaser.setCustomHandler({
|
|
1315
|
+
shouldReinstate: (_txn) => {
|
|
1316
|
+
return true;
|
|
1317
|
+
},
|
|
1318
|
+
recompute: async (_txn) => {
|
|
1319
|
+
await testIModel.updateElement(b1, e1);
|
|
1320
|
+
await testIModel.updateElement(b1, e2);
|
|
1321
|
+
},
|
|
1322
|
+
});
|
|
1323
|
+
await pushChangeFromB2();
|
|
1324
|
+
clearEvents();
|
|
1325
|
+
const geomGuidBeforePull = getGeometryGuidFromB1("0x20000000001");
|
|
1326
|
+
await b1.pushChanges({ description: "push changes on b1" });
|
|
1327
|
+
const geomGuidAfterPull = getGeometryGuidFromB1("0x20000000001");
|
|
1328
|
+
chai.expect(geomGuidBeforePull).to.not.equal(geomGuidAfterPull);
|
|
1329
|
+
chai.expect(events.modelGeometryChanged.length).to.equal(4);
|
|
1330
|
+
});
|
|
1331
|
+
it("onModelGeometryChanged() fired during rebase with non-geometric local change", async () => {
|
|
1332
|
+
const b1 = await testIModel.openBriefcase();
|
|
1333
|
+
const b2 = await testIModel.openBriefcase();
|
|
1334
|
+
const pushChangeFromB2 = async () => {
|
|
1335
|
+
await b2.pullChanges();
|
|
1336
|
+
await testIModel.insertElement(b2);
|
|
1337
|
+
b2.saveChanges();
|
|
1338
|
+
await b2.pushChanges({ description: "insert element on b2" });
|
|
1339
|
+
};
|
|
1340
|
+
const events = {
|
|
1341
|
+
modelGeometryChanged: [],
|
|
1342
|
+
};
|
|
1343
|
+
const getGeometryGuidFromB1 = (modelId) => {
|
|
1344
|
+
const modelProps = b1.models.tryGetModelProps(modelId);
|
|
1345
|
+
return modelProps?.geometryGuid;
|
|
1346
|
+
};
|
|
1347
|
+
const clearEvents = () => {
|
|
1348
|
+
events.modelGeometryChanged = [];
|
|
1349
|
+
};
|
|
1350
|
+
b1.txns.onModelGeometryChanged.addListener((changes) => {
|
|
1351
|
+
events.modelGeometryChanged.push(changes);
|
|
1352
|
+
});
|
|
1353
|
+
clearEvents();
|
|
1354
|
+
b1.txns.rebaser.setCustomHandler({
|
|
1355
|
+
shouldReinstate: (_txn) => {
|
|
1356
|
+
return true;
|
|
1357
|
+
},
|
|
1358
|
+
recompute: async (_txn) => {
|
|
1359
|
+
},
|
|
1360
|
+
});
|
|
1361
|
+
await pushChangeFromB2();
|
|
1362
|
+
await testIModel.insertRecipe2d(b1);
|
|
1363
|
+
b1.saveChanges();
|
|
1364
|
+
clearEvents();
|
|
1365
|
+
const geomGuidBeforePull = getGeometryGuidFromB1("0x20000000001");
|
|
1366
|
+
chai.expect(geomGuidBeforePull).is.undefined;
|
|
1367
|
+
await b1.pushChanges({ description: "push changes on b1" });
|
|
1368
|
+
const geomGuidAfterPull = getGeometryGuidFromB1("0x20000000001");
|
|
1369
|
+
chai.expect(geomGuidAfterPull).to.exist;
|
|
1370
|
+
chai.expect(events.modelGeometryChanged.length).to.equal(1);
|
|
1371
|
+
});
|
|
1372
|
+
it("onModelGeometryChanged() fired during rebase with geometric local change", async () => {
|
|
1373
|
+
const b1 = await testIModel.openBriefcase();
|
|
1374
|
+
const b2 = await testIModel.openBriefcase();
|
|
1375
|
+
const pushChangeFromB2 = async () => {
|
|
1376
|
+
await b2.pullChanges();
|
|
1377
|
+
await testIModel.insertRecipe2d(b2);
|
|
1378
|
+
b2.saveChanges();
|
|
1379
|
+
await b2.pushChanges({ description: "insert element on b2" });
|
|
1380
|
+
};
|
|
1381
|
+
const events = {
|
|
1382
|
+
modelGeometryChanged: [],
|
|
1383
|
+
onGeometryChanged: [],
|
|
1384
|
+
};
|
|
1385
|
+
const getGeometryGuidFromB1 = (modelId) => {
|
|
1386
|
+
const modelProps = b1.models.tryGetModelProps(modelId);
|
|
1387
|
+
return modelProps?.geometryGuid;
|
|
1388
|
+
};
|
|
1389
|
+
const clearEvents = () => {
|
|
1390
|
+
events.modelGeometryChanged = [];
|
|
1391
|
+
events.onGeometryChanged = [];
|
|
1392
|
+
};
|
|
1393
|
+
b1.txns.onModelGeometryChanged.addListener((changes) => {
|
|
1394
|
+
events.modelGeometryChanged.push(changes);
|
|
1395
|
+
});
|
|
1396
|
+
b1.txns.onGeometryChanged.addListener((changes) => {
|
|
1397
|
+
events.onGeometryChanged.push(changes);
|
|
1398
|
+
});
|
|
1399
|
+
clearEvents();
|
|
1400
|
+
b1.txns.rebaser.setCustomHandler({
|
|
1401
|
+
shouldReinstate: (_txn) => {
|
|
1402
|
+
return true;
|
|
1403
|
+
},
|
|
1404
|
+
recompute: async (_txn) => {
|
|
1405
|
+
await testIModel.insertElement(b1);
|
|
1406
|
+
},
|
|
1407
|
+
});
|
|
1408
|
+
await pushChangeFromB2();
|
|
1409
|
+
clearEvents();
|
|
1410
|
+
const geomGuidBeforePull = getGeometryGuidFromB1("0x20000000001");
|
|
1411
|
+
chai.expect(geomGuidBeforePull).is.undefined;
|
|
1412
|
+
await b1.pushChanges({ description: "push changes on b1" });
|
|
1413
|
+
const geomGuidAfterPull = getGeometryGuidFromB1("0x20000000001");
|
|
1414
|
+
chai.expect(geomGuidAfterPull).is.undefined;
|
|
1415
|
+
chai.expect(events.modelGeometryChanged.length).to.equal(0);
|
|
1416
|
+
});
|
|
990
1417
|
it("rebase multi txn", async () => {
|
|
991
1418
|
const b1 = await testIModel.openBriefcase();
|
|
992
1419
|
const b2 = await testIModel.openBriefcase();
|
|
@@ -1349,5 +1776,102 @@ describe("rebase changes & stashing api", function () {
|
|
|
1349
1776
|
chai.expect(b1.elements.tryGetElementProps(e4)).to.exist;
|
|
1350
1777
|
chai.expect(b1.elements.tryGetElementProps(e7)).to.exist;
|
|
1351
1778
|
});
|
|
1779
|
+
it("changeset DDL error are ignored and ec_* tables are used to reconstruct the sqlite tables", async () => {
|
|
1780
|
+
const b1 = await testIModel.openBriefcase();
|
|
1781
|
+
const b2 = await testIModel.openBriefcase();
|
|
1782
|
+
const iModelId = testIModel.iModelId;
|
|
1783
|
+
const targetDir = path.join(KnownTestLocations.outputDir, iModelId, "changesets");
|
|
1784
|
+
let ver = 0;
|
|
1785
|
+
let props = 0;
|
|
1786
|
+
const tblGeom2d = "bis_GeometricElement2d";
|
|
1787
|
+
const geom2dBaseColumnList = [
|
|
1788
|
+
"ElementId",
|
|
1789
|
+
"ECClassId",
|
|
1790
|
+
"CategoryId",
|
|
1791
|
+
"Origin_X",
|
|
1792
|
+
"Origin_Y",
|
|
1793
|
+
"Rotation",
|
|
1794
|
+
"BBoxLow_X",
|
|
1795
|
+
"BBoxLow_Y",
|
|
1796
|
+
"BBoxHigh_X",
|
|
1797
|
+
"BBoxHigh_Y",
|
|
1798
|
+
"GeometryStream",
|
|
1799
|
+
"TypeDefinitionId",
|
|
1800
|
+
"TypeDefinitionRelECClassId",
|
|
1801
|
+
"js1",
|
|
1802
|
+
"js2",
|
|
1803
|
+
];
|
|
1804
|
+
const generateSchema = (noOfNewPropsToAdd) => {
|
|
1805
|
+
props += noOfNewPropsToAdd;
|
|
1806
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1807
|
+
<ECSchema schemaName="TestDomain1" alias="ts1" version="01.00.${ver++}" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
|
|
1808
|
+
<ECSchemaReference name="BisCore" version="01.00.00" alias="bis"/>
|
|
1809
|
+
<ECEntityClass typeName="test">
|
|
1810
|
+
<BaseClass>bis:GraphicalElement2d</BaseClass>
|
|
1811
|
+
<ECProperty propertyName="prop1" typeName="string" />
|
|
1812
|
+
${Array.from({ length: props - 1 }, (_, i) => `<ECProperty propertyName="prop${i + 2}" typeName="string" />`).join("\n ")}
|
|
1813
|
+
</ECEntityClass>
|
|
1814
|
+
</ECSchema>`;
|
|
1815
|
+
};
|
|
1816
|
+
const getColumnNames = (b, tableName) => {
|
|
1817
|
+
return b.withSqliteStatement(`PRAGMA table_info(${tableName})`, (stmt) => {
|
|
1818
|
+
const columnNames = [];
|
|
1819
|
+
while (stmt.step() === DbResult.BE_SQLITE_ROW) {
|
|
1820
|
+
columnNames.push(stmt.getValue(1).getString());
|
|
1821
|
+
}
|
|
1822
|
+
return columnNames;
|
|
1823
|
+
});
|
|
1824
|
+
};
|
|
1825
|
+
const withLatestChangeset = async (cb) => {
|
|
1826
|
+
const csInfo = await HubMock.getLatestChangeset({ iModelId });
|
|
1827
|
+
const info = await HubMock.downloadChangeset({
|
|
1828
|
+
iModelId,
|
|
1829
|
+
changeset: { id: csInfo.id },
|
|
1830
|
+
targetDir,
|
|
1831
|
+
});
|
|
1832
|
+
const reader = SqliteChangesetReader.openFile({ db: b1, fileName: info.pathname });
|
|
1833
|
+
try {
|
|
1834
|
+
await cb(reader);
|
|
1835
|
+
}
|
|
1836
|
+
finally {
|
|
1837
|
+
reader.close();
|
|
1838
|
+
}
|
|
1839
|
+
};
|
|
1840
|
+
// Verify initial columns
|
|
1841
|
+
chai.expect(getColumnNames(b1, tblGeom2d)).deep.equals(geom2dBaseColumnList);
|
|
1842
|
+
chai.expect(getColumnNames(b2, tblGeom2d)).deep.equals(geom2dBaseColumnList);
|
|
1843
|
+
// Import schema that add 5 new properties that should add 3 new shared columns
|
|
1844
|
+
await b1.importSchemaStrings([generateSchema(5)]);
|
|
1845
|
+
await b1.pushChanges({ description: `imported schema version 1.0.${ver - 1}` });
|
|
1846
|
+
// Verify columns after schema import
|
|
1847
|
+
chai.expect(getColumnNames(b1, tblGeom2d)).deep.equals([...geom2dBaseColumnList, "js3", "js4", "js5"]);
|
|
1848
|
+
//verify changeset has schema changes
|
|
1849
|
+
await withLatestChangeset(async (reader) => {
|
|
1850
|
+
const schemaChanges = reader.getDdlChanges()?.split(";");
|
|
1851
|
+
chai.expect(schemaChanges).to.include("ALTER TABLE [bis_GeometricElement2d] ADD COLUMN [js3] BLOB");
|
|
1852
|
+
chai.expect(schemaChanges).to.include("ALTER TABLE [bis_GeometricElement2d] ADD COLUMN [js4] BLOB");
|
|
1853
|
+
chai.expect(schemaChanges).to.include("ALTER TABLE [bis_GeometricElement2d] ADD COLUMN [js5] BLOB");
|
|
1854
|
+
});
|
|
1855
|
+
await b2.pullChanges();
|
|
1856
|
+
chai.expect(getColumnNames(b2, "bis_GeometricElement2d")).deep.equals([...geom2dBaseColumnList, "js3", "js4", "js5"]);
|
|
1857
|
+
// Import schema that add 5 new properties that should add 3 new shared columns
|
|
1858
|
+
await b1.importSchemaStrings([generateSchema(1)]);
|
|
1859
|
+
await b1.pushChanges({ description: `imported schema version 1.0.${ver - 1}` });
|
|
1860
|
+
// Verify columns after schema import
|
|
1861
|
+
chai.expect(getColumnNames(b1, tblGeom2d)).deep.equals([...geom2dBaseColumnList, "js3", "js4", "js5", "js6"]);
|
|
1862
|
+
//verify changeset has schema changes
|
|
1863
|
+
await withLatestChangeset(async (reader) => {
|
|
1864
|
+
const schemaChanges = reader.getDdlChanges()?.split(";");
|
|
1865
|
+
chai.expect(schemaChanges).to.include("ALTER TABLE [bis_GeometricElement2d] ADD COLUMN [js6] BLOB");
|
|
1866
|
+
});
|
|
1867
|
+
// delete the table so DDL apply should fail
|
|
1868
|
+
b2[_nativeDb].executeSql(`DROP TABLE ${tblGeom2d}`);
|
|
1869
|
+
// this would fail before this PR but should succeed as DDL error are ignored and table reconstruction is attempted using ec_* tables
|
|
1870
|
+
await b2.pullChanges();
|
|
1871
|
+
// Verify columns after schema import
|
|
1872
|
+
chai.expect(getColumnNames(b2, tblGeom2d)).deep.equals([...geom2dBaseColumnList, "js3", "js4", "js5", "js6"]);
|
|
1873
|
+
b1.close();
|
|
1874
|
+
b2.close();
|
|
1875
|
+
});
|
|
1352
1876
|
});
|
|
1353
1877
|
//# sourceMappingURL=Rebase.test.js.map
|