@itwin/core-backend 5.6.0-dev.8 → 5.7.0-dev.1
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/IModelDb.d.ts +32 -3
- 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/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/IModelDb.d.ts +32 -3
- 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/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 +345 -10
- 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/package.json +15 -15
|
@@ -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++}` });
|
|
@@ -669,11 +724,11 @@ describe("rebase changes & stashing api", function () {
|
|
|
669
724
|
await b2.pullChanges();
|
|
670
725
|
chai.expect(b2.changeset.index).to.equals(4);
|
|
671
726
|
const elBefore = b2.elements.tryGetElementProps(e1);
|
|
672
|
-
chai.expect(elBefore.prop1).to.equals("
|
|
727
|
+
chai.expect(elBefore.prop1).to.equals("3");
|
|
673
728
|
// restore stash should succeed as now it can obtain lock
|
|
674
729
|
await StashManager.restore({ db: b2, stash: b2Stash1 });
|
|
675
730
|
const elAfter = b2.elements.tryGetElementProps(e1);
|
|
676
|
-
chai.expect(elAfter.prop1).to.equals("
|
|
731
|
+
chai.expect(elAfter.prop1).to.equals("2");
|
|
677
732
|
await b2.pushChanges({ description: `${e1} updated` });
|
|
678
733
|
});
|
|
679
734
|
it("schema change should not be stashed", async () => {
|
|
@@ -686,6 +741,10 @@ describe("rebase changes & stashing api", function () {
|
|
|
686
741
|
<ECProperty propertyName="prop1" typeName="string" />
|
|
687
742
|
<ECProperty propertyName="prop2" typeName="string" />
|
|
688
743
|
</ECEntityClass>
|
|
744
|
+
<ECEntityClass typeName="A1Recipe2d">
|
|
745
|
+
<BaseClass>bis:TemplateRecipe2d</BaseClass>
|
|
746
|
+
<ECProperty propertyName="prop1" typeName="string" />
|
|
747
|
+
</ECEntityClass>
|
|
689
748
|
<ECRelationshipClass typeName="A1OwnsA1" modifier="None" strength="embedding">
|
|
690
749
|
<BaseClass>bis:ElementOwnsChildElements</BaseClass>
|
|
691
750
|
<Source multiplicity="(0..1)" roleLabel="owns" polymorphic="true">
|
|
@@ -783,7 +842,7 @@ describe("rebase changes & stashing api", function () {
|
|
|
783
842
|
const b1 = await testIModel.openBriefcase();
|
|
784
843
|
const b2 = await testIModel.openBriefcase();
|
|
785
844
|
const parentId = await testIModel.insertElement(b1);
|
|
786
|
-
const childId = await testIModel.
|
|
845
|
+
const childId = await testIModel.insertElementEx(b1, { parent: { id: parentId, relClassName: "TestDomain:A1OwnsA1" } });
|
|
787
846
|
b1.saveChanges("insert parent and child");
|
|
788
847
|
await b1.pushChanges({ description: `inserted parent ${parentId} and child ${childId}` });
|
|
789
848
|
await b2.pullChanges();
|
|
@@ -791,7 +850,7 @@ describe("rebase changes & stashing api", function () {
|
|
|
791
850
|
await testIModel.deleteElement(b1, childId);
|
|
792
851
|
b1.saveChanges("delete child");
|
|
793
852
|
// no exclusive lock required on child1
|
|
794
|
-
const grandChildId = await testIModel.
|
|
853
|
+
const grandChildId = await testIModel.insertElementEx(b2, { parent: { id: childId, relClassName: "TestDomain:A1OwnsA1" }, markAsIndirect: true });
|
|
795
854
|
b2.saveChanges("delete child and insert grandchild");
|
|
796
855
|
await b1.pushChanges({ description: `deleted child ${childId}` });
|
|
797
856
|
// should fail to pull and rebase changes.
|
|
@@ -987,6 +1046,185 @@ describe("rebase changes & stashing api", function () {
|
|
|
987
1046
|
chai.expect(events.rebaseHandler.shouldReinstate.map((txn) => txn.id)).to.deep.equal(["0x100000000", "0x100000001", "0x100000002", "0x100000003"]);
|
|
988
1047
|
chai.expect(events.rebaseHandler.recompute.map((txn) => txn.id)).to.deep.equal(["0x100000000", "0x100000001", "0x100000002", "0x100000003"]);
|
|
989
1048
|
});
|
|
1049
|
+
it("onModelGeometryChanged() not fired during rebase/pullMerge with no local change", async () => {
|
|
1050
|
+
const b1 = await testIModel.openBriefcase();
|
|
1051
|
+
const b2 = await testIModel.openBriefcase();
|
|
1052
|
+
const pushChangeFromB2 = async () => {
|
|
1053
|
+
await b2.pullChanges();
|
|
1054
|
+
await testIModel.insertElement(b2);
|
|
1055
|
+
b2.saveChanges();
|
|
1056
|
+
await b2.pushChanges({ description: "insert element on b2" });
|
|
1057
|
+
};
|
|
1058
|
+
const events = {
|
|
1059
|
+
modelGeometryChanged: [],
|
|
1060
|
+
};
|
|
1061
|
+
const getGeometryGuidFromB1 = (modelId) => {
|
|
1062
|
+
const modelProps = b1.models.tryGetModelProps(modelId);
|
|
1063
|
+
return modelProps?.geometryGuid;
|
|
1064
|
+
};
|
|
1065
|
+
const clearEvents = () => {
|
|
1066
|
+
events.modelGeometryChanged = [];
|
|
1067
|
+
};
|
|
1068
|
+
b1.txns.onModelGeometryChanged.addListener((changes) => {
|
|
1069
|
+
events.modelGeometryChanged.push(changes);
|
|
1070
|
+
});
|
|
1071
|
+
clearEvents();
|
|
1072
|
+
b1.txns.rebaser.setCustomHandler({
|
|
1073
|
+
shouldReinstate: (_txn) => {
|
|
1074
|
+
return true;
|
|
1075
|
+
},
|
|
1076
|
+
recompute: async (_txn) => {
|
|
1077
|
+
},
|
|
1078
|
+
});
|
|
1079
|
+
await pushChangeFromB2();
|
|
1080
|
+
clearEvents();
|
|
1081
|
+
const geomGuidBeforePull = getGeometryGuidFromB1("0x20000000001");
|
|
1082
|
+
chai.expect(geomGuidBeforePull).is.undefined;
|
|
1083
|
+
await b1.pushChanges({ description: "push changes on b1" });
|
|
1084
|
+
const geomGuidAfterPull = getGeometryGuidFromB1("0x20000000001");
|
|
1085
|
+
chai.expect(geomGuidAfterPull).is.undefined;
|
|
1086
|
+
chai.expect(events.modelGeometryChanged.length).to.equal(0);
|
|
1087
|
+
});
|
|
1088
|
+
it("onModelGeometryChanged() fired during rebase with geometric local change", async () => {
|
|
1089
|
+
const b1 = await testIModel.openBriefcase();
|
|
1090
|
+
const b2 = await testIModel.openBriefcase();
|
|
1091
|
+
const pushChangeFromB2 = async () => {
|
|
1092
|
+
await b2.pullChanges();
|
|
1093
|
+
await testIModel.insertElement(b2);
|
|
1094
|
+
b2.saveChanges();
|
|
1095
|
+
await b2.pushChanges({ description: "insert element on b2" });
|
|
1096
|
+
};
|
|
1097
|
+
const events = {
|
|
1098
|
+
modelGeometryChanged: [],
|
|
1099
|
+
onGeometryChanged: [],
|
|
1100
|
+
};
|
|
1101
|
+
const getGeometryGuidFromB1 = (modelId) => {
|
|
1102
|
+
const modelProps = b1.models.tryGetModelProps(modelId);
|
|
1103
|
+
return modelProps?.geometryGuid;
|
|
1104
|
+
};
|
|
1105
|
+
const clearEvents = () => {
|
|
1106
|
+
events.modelGeometryChanged = [];
|
|
1107
|
+
events.onGeometryChanged = [];
|
|
1108
|
+
};
|
|
1109
|
+
b1.txns.onModelGeometryChanged.addListener((changes) => {
|
|
1110
|
+
events.modelGeometryChanged.push(changes);
|
|
1111
|
+
});
|
|
1112
|
+
b1.txns.onGeometryChanged.addListener((changes) => {
|
|
1113
|
+
events.onGeometryChanged.push(changes);
|
|
1114
|
+
});
|
|
1115
|
+
clearEvents();
|
|
1116
|
+
const e1 = await testIModel.insertElement(b1);
|
|
1117
|
+
const e2 = await testIModel.insertElement(b1, true);
|
|
1118
|
+
chai.expect(e1).to.exist;
|
|
1119
|
+
chai.expect(e2).to.exist;
|
|
1120
|
+
b1.saveChanges(`insert element ${e1} and ${e2}`);
|
|
1121
|
+
chai.expect(events.modelGeometryChanged.length).to.equal(1);
|
|
1122
|
+
chai.expect(events.modelGeometryChanged[0].length).to.equal(1);
|
|
1123
|
+
chai.expect(events.modelGeometryChanged[0][0].id).to.equal("0x20000000001");
|
|
1124
|
+
chai.assert(Guid.isGuid(events.modelGeometryChanged[0][0].guid));
|
|
1125
|
+
b1.txns.rebaser.setCustomHandler({
|
|
1126
|
+
shouldReinstate: (_txn) => {
|
|
1127
|
+
return true;
|
|
1128
|
+
},
|
|
1129
|
+
recompute: async (_txn) => {
|
|
1130
|
+
await testIModel.updateElement(b1, e1);
|
|
1131
|
+
await testIModel.updateElement(b1, e2);
|
|
1132
|
+
},
|
|
1133
|
+
});
|
|
1134
|
+
await pushChangeFromB2();
|
|
1135
|
+
clearEvents();
|
|
1136
|
+
const geomGuidBeforePull = getGeometryGuidFromB1("0x20000000001");
|
|
1137
|
+
await b1.pushChanges({ description: "push changes on b1" });
|
|
1138
|
+
const geomGuidAfterPull = getGeometryGuidFromB1("0x20000000001");
|
|
1139
|
+
chai.expect(geomGuidBeforePull).to.not.equal(geomGuidAfterPull);
|
|
1140
|
+
chai.expect(events.modelGeometryChanged.length).to.equal(4);
|
|
1141
|
+
});
|
|
1142
|
+
it("onModelGeometryChanged() fired during rebase with non-geometric local change", async () => {
|
|
1143
|
+
const b1 = await testIModel.openBriefcase();
|
|
1144
|
+
const b2 = await testIModel.openBriefcase();
|
|
1145
|
+
const pushChangeFromB2 = async () => {
|
|
1146
|
+
await b2.pullChanges();
|
|
1147
|
+
await testIModel.insertElement(b2);
|
|
1148
|
+
b2.saveChanges();
|
|
1149
|
+
await b2.pushChanges({ description: "insert element on b2" });
|
|
1150
|
+
};
|
|
1151
|
+
const events = {
|
|
1152
|
+
modelGeometryChanged: [],
|
|
1153
|
+
};
|
|
1154
|
+
const getGeometryGuidFromB1 = (modelId) => {
|
|
1155
|
+
const modelProps = b1.models.tryGetModelProps(modelId);
|
|
1156
|
+
return modelProps?.geometryGuid;
|
|
1157
|
+
};
|
|
1158
|
+
const clearEvents = () => {
|
|
1159
|
+
events.modelGeometryChanged = [];
|
|
1160
|
+
};
|
|
1161
|
+
b1.txns.onModelGeometryChanged.addListener((changes) => {
|
|
1162
|
+
events.modelGeometryChanged.push(changes);
|
|
1163
|
+
});
|
|
1164
|
+
clearEvents();
|
|
1165
|
+
b1.txns.rebaser.setCustomHandler({
|
|
1166
|
+
shouldReinstate: (_txn) => {
|
|
1167
|
+
return true;
|
|
1168
|
+
},
|
|
1169
|
+
recompute: async (_txn) => {
|
|
1170
|
+
},
|
|
1171
|
+
});
|
|
1172
|
+
await pushChangeFromB2();
|
|
1173
|
+
await testIModel.insertRecipe2d(b1);
|
|
1174
|
+
b1.saveChanges();
|
|
1175
|
+
clearEvents();
|
|
1176
|
+
const geomGuidBeforePull = getGeometryGuidFromB1("0x20000000001");
|
|
1177
|
+
chai.expect(geomGuidBeforePull).is.undefined;
|
|
1178
|
+
await b1.pushChanges({ description: "push changes on b1" });
|
|
1179
|
+
const geomGuidAfterPull = getGeometryGuidFromB1("0x20000000001");
|
|
1180
|
+
chai.expect(geomGuidAfterPull).to.exist;
|
|
1181
|
+
chai.expect(events.modelGeometryChanged.length).to.equal(1);
|
|
1182
|
+
});
|
|
1183
|
+
it("onModelGeometryChanged() fired during rebase with geometric local change", async () => {
|
|
1184
|
+
const b1 = await testIModel.openBriefcase();
|
|
1185
|
+
const b2 = await testIModel.openBriefcase();
|
|
1186
|
+
const pushChangeFromB2 = async () => {
|
|
1187
|
+
await b2.pullChanges();
|
|
1188
|
+
await testIModel.insertRecipe2d(b2);
|
|
1189
|
+
b2.saveChanges();
|
|
1190
|
+
await b2.pushChanges({ description: "insert element on b2" });
|
|
1191
|
+
};
|
|
1192
|
+
const events = {
|
|
1193
|
+
modelGeometryChanged: [],
|
|
1194
|
+
onGeometryChanged: [],
|
|
1195
|
+
};
|
|
1196
|
+
const getGeometryGuidFromB1 = (modelId) => {
|
|
1197
|
+
const modelProps = b1.models.tryGetModelProps(modelId);
|
|
1198
|
+
return modelProps?.geometryGuid;
|
|
1199
|
+
};
|
|
1200
|
+
const clearEvents = () => {
|
|
1201
|
+
events.modelGeometryChanged = [];
|
|
1202
|
+
events.onGeometryChanged = [];
|
|
1203
|
+
};
|
|
1204
|
+
b1.txns.onModelGeometryChanged.addListener((changes) => {
|
|
1205
|
+
events.modelGeometryChanged.push(changes);
|
|
1206
|
+
});
|
|
1207
|
+
b1.txns.onGeometryChanged.addListener((changes) => {
|
|
1208
|
+
events.onGeometryChanged.push(changes);
|
|
1209
|
+
});
|
|
1210
|
+
clearEvents();
|
|
1211
|
+
b1.txns.rebaser.setCustomHandler({
|
|
1212
|
+
shouldReinstate: (_txn) => {
|
|
1213
|
+
return true;
|
|
1214
|
+
},
|
|
1215
|
+
recompute: async (_txn) => {
|
|
1216
|
+
await testIModel.insertElement(b1);
|
|
1217
|
+
},
|
|
1218
|
+
});
|
|
1219
|
+
await pushChangeFromB2();
|
|
1220
|
+
clearEvents();
|
|
1221
|
+
const geomGuidBeforePull = getGeometryGuidFromB1("0x20000000001");
|
|
1222
|
+
chai.expect(geomGuidBeforePull).is.undefined;
|
|
1223
|
+
await b1.pushChanges({ description: "push changes on b1" });
|
|
1224
|
+
const geomGuidAfterPull = getGeometryGuidFromB1("0x20000000001");
|
|
1225
|
+
chai.expect(geomGuidAfterPull).is.undefined;
|
|
1226
|
+
chai.expect(events.modelGeometryChanged.length).to.equal(0);
|
|
1227
|
+
});
|
|
990
1228
|
it("rebase multi txn", async () => {
|
|
991
1229
|
const b1 = await testIModel.openBriefcase();
|
|
992
1230
|
const b2 = await testIModel.openBriefcase();
|
|
@@ -1349,5 +1587,102 @@ describe("rebase changes & stashing api", function () {
|
|
|
1349
1587
|
chai.expect(b1.elements.tryGetElementProps(e4)).to.exist;
|
|
1350
1588
|
chai.expect(b1.elements.tryGetElementProps(e7)).to.exist;
|
|
1351
1589
|
});
|
|
1590
|
+
it("changeset DDL error are ignored and ec_* tables are used to reconstruct the sqlite tables", async () => {
|
|
1591
|
+
const b1 = await testIModel.openBriefcase();
|
|
1592
|
+
const b2 = await testIModel.openBriefcase();
|
|
1593
|
+
const iModelId = testIModel.iModelId;
|
|
1594
|
+
const targetDir = path.join(KnownTestLocations.outputDir, iModelId, "changesets");
|
|
1595
|
+
let ver = 0;
|
|
1596
|
+
let props = 0;
|
|
1597
|
+
const tblGeom2d = "bis_GeometricElement2d";
|
|
1598
|
+
const geom2dBaseColumnList = [
|
|
1599
|
+
"ElementId",
|
|
1600
|
+
"ECClassId",
|
|
1601
|
+
"CategoryId",
|
|
1602
|
+
"Origin_X",
|
|
1603
|
+
"Origin_Y",
|
|
1604
|
+
"Rotation",
|
|
1605
|
+
"BBoxLow_X",
|
|
1606
|
+
"BBoxLow_Y",
|
|
1607
|
+
"BBoxHigh_X",
|
|
1608
|
+
"BBoxHigh_Y",
|
|
1609
|
+
"GeometryStream",
|
|
1610
|
+
"TypeDefinitionId",
|
|
1611
|
+
"TypeDefinitionRelECClassId",
|
|
1612
|
+
"js1",
|
|
1613
|
+
"js2",
|
|
1614
|
+
];
|
|
1615
|
+
const generateSchema = (noOfNewPropsToAdd) => {
|
|
1616
|
+
props += noOfNewPropsToAdd;
|
|
1617
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1618
|
+
<ECSchema schemaName="TestDomain1" alias="ts1" version="01.00.${ver++}" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
|
|
1619
|
+
<ECSchemaReference name="BisCore" version="01.00.00" alias="bis"/>
|
|
1620
|
+
<ECEntityClass typeName="test">
|
|
1621
|
+
<BaseClass>bis:GraphicalElement2d</BaseClass>
|
|
1622
|
+
<ECProperty propertyName="prop1" typeName="string" />
|
|
1623
|
+
${Array.from({ length: props - 1 }, (_, i) => `<ECProperty propertyName="prop${i + 2}" typeName="string" />`).join("\n ")}
|
|
1624
|
+
</ECEntityClass>
|
|
1625
|
+
</ECSchema>`;
|
|
1626
|
+
};
|
|
1627
|
+
const getColumnNames = (b, tableName) => {
|
|
1628
|
+
return b.withSqliteStatement(`PRAGMA table_info(${tableName})`, (stmt) => {
|
|
1629
|
+
const columnNames = [];
|
|
1630
|
+
while (stmt.step() === DbResult.BE_SQLITE_ROW) {
|
|
1631
|
+
columnNames.push(stmt.getValue(1).getString());
|
|
1632
|
+
}
|
|
1633
|
+
return columnNames;
|
|
1634
|
+
});
|
|
1635
|
+
};
|
|
1636
|
+
const withLatestChangeset = async (cb) => {
|
|
1637
|
+
const csInfo = await HubMock.getLatestChangeset({ iModelId });
|
|
1638
|
+
const info = await HubMock.downloadChangeset({
|
|
1639
|
+
iModelId,
|
|
1640
|
+
changeset: { id: csInfo.id },
|
|
1641
|
+
targetDir,
|
|
1642
|
+
});
|
|
1643
|
+
const reader = SqliteChangesetReader.openFile({ db: b1, fileName: info.pathname });
|
|
1644
|
+
try {
|
|
1645
|
+
await cb(reader);
|
|
1646
|
+
}
|
|
1647
|
+
finally {
|
|
1648
|
+
reader.close();
|
|
1649
|
+
}
|
|
1650
|
+
};
|
|
1651
|
+
// Verify initial columns
|
|
1652
|
+
chai.expect(getColumnNames(b1, tblGeom2d)).deep.equals(geom2dBaseColumnList);
|
|
1653
|
+
chai.expect(getColumnNames(b2, tblGeom2d)).deep.equals(geom2dBaseColumnList);
|
|
1654
|
+
// Import schema that add 5 new properties that should add 3 new shared columns
|
|
1655
|
+
await b1.importSchemaStrings([generateSchema(5)]);
|
|
1656
|
+
await b1.pushChanges({ description: `imported schema version 1.0.${ver - 1}` });
|
|
1657
|
+
// Verify columns after schema import
|
|
1658
|
+
chai.expect(getColumnNames(b1, tblGeom2d)).deep.equals([...geom2dBaseColumnList, "js3", "js4", "js5"]);
|
|
1659
|
+
//verify changeset has schema changes
|
|
1660
|
+
await withLatestChangeset(async (reader) => {
|
|
1661
|
+
const schemaChanges = reader.getDdlChanges()?.split(";");
|
|
1662
|
+
chai.expect(schemaChanges).to.include("ALTER TABLE [bis_GeometricElement2d] ADD COLUMN [js3] BLOB");
|
|
1663
|
+
chai.expect(schemaChanges).to.include("ALTER TABLE [bis_GeometricElement2d] ADD COLUMN [js4] BLOB");
|
|
1664
|
+
chai.expect(schemaChanges).to.include("ALTER TABLE [bis_GeometricElement2d] ADD COLUMN [js5] BLOB");
|
|
1665
|
+
});
|
|
1666
|
+
await b2.pullChanges();
|
|
1667
|
+
chai.expect(getColumnNames(b2, "bis_GeometricElement2d")).deep.equals([...geom2dBaseColumnList, "js3", "js4", "js5"]);
|
|
1668
|
+
// Import schema that add 5 new properties that should add 3 new shared columns
|
|
1669
|
+
await b1.importSchemaStrings([generateSchema(1)]);
|
|
1670
|
+
await b1.pushChanges({ description: `imported schema version 1.0.${ver - 1}` });
|
|
1671
|
+
// Verify columns after schema import
|
|
1672
|
+
chai.expect(getColumnNames(b1, tblGeom2d)).deep.equals([...geom2dBaseColumnList, "js3", "js4", "js5", "js6"]);
|
|
1673
|
+
//verify changeset has schema changes
|
|
1674
|
+
await withLatestChangeset(async (reader) => {
|
|
1675
|
+
const schemaChanges = reader.getDdlChanges()?.split(";");
|
|
1676
|
+
chai.expect(schemaChanges).to.include("ALTER TABLE [bis_GeometricElement2d] ADD COLUMN [js6] BLOB");
|
|
1677
|
+
});
|
|
1678
|
+
// delete the table so DDL apply should fail
|
|
1679
|
+
b2[_nativeDb].executeSql(`DROP TABLE ${tblGeom2d}`);
|
|
1680
|
+
// this would fail before this PR but should succeed as DDL error are ignored and table reconstruction is attempted using ec_* tables
|
|
1681
|
+
await b2.pullChanges();
|
|
1682
|
+
// Verify columns after schema import
|
|
1683
|
+
chai.expect(getColumnNames(b2, tblGeom2d)).deep.equals([...geom2dBaseColumnList, "js3", "js4", "js5", "js6"]);
|
|
1684
|
+
b1.close();
|
|
1685
|
+
b2.close();
|
|
1686
|
+
});
|
|
1352
1687
|
});
|
|
1353
1688
|
//# sourceMappingURL=Rebase.test.js.map
|