@itwin/core-backend 5.6.0-dev.7 → 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.
Files changed (60) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/lib/cjs/IModelDb.d.ts +32 -3
  3. package/lib/cjs/IModelDb.d.ts.map +1 -1
  4. package/lib/cjs/IModelDb.js +42 -4
  5. package/lib/cjs/IModelDb.js.map +1 -1
  6. package/lib/cjs/SQLiteDb.d.ts.map +1 -1
  7. package/lib/cjs/SQLiteDb.js +0 -1
  8. package/lib/cjs/SQLiteDb.js.map +1 -1
  9. package/lib/cjs/SqliteChangesetReader.d.ts +5 -0
  10. package/lib/cjs/SqliteChangesetReader.d.ts.map +1 -1
  11. package/lib/cjs/SqliteChangesetReader.js +7 -0
  12. package/lib/cjs/SqliteChangesetReader.js.map +1 -1
  13. package/lib/cjs/annotations/TextAnnotationElement.d.ts +1 -1
  14. package/lib/cjs/annotations/TextAnnotationElement.d.ts.map +1 -1
  15. package/lib/cjs/annotations/TextAnnotationElement.js +14 -1
  16. package/lib/cjs/annotations/TextAnnotationElement.js.map +1 -1
  17. package/lib/cjs/annotations/TextAnnotationGeometry.d.ts +14 -0
  18. package/lib/cjs/annotations/TextAnnotationGeometry.d.ts.map +1 -1
  19. package/lib/cjs/annotations/TextAnnotationGeometry.js +29 -10
  20. package/lib/cjs/annotations/TextAnnotationGeometry.js.map +1 -1
  21. package/lib/cjs/annotations/TextBlockLayout.js +6 -6
  22. package/lib/cjs/annotations/TextBlockLayout.js.map +1 -1
  23. package/lib/cjs/rpc-impl/RpcBriefcaseUtility.d.ts.map +1 -1
  24. package/lib/cjs/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
  25. package/lib/esm/IModelDb.d.ts +32 -3
  26. package/lib/esm/IModelDb.d.ts.map +1 -1
  27. package/lib/esm/IModelDb.js +42 -4
  28. package/lib/esm/IModelDb.js.map +1 -1
  29. package/lib/esm/SQLiteDb.d.ts.map +1 -1
  30. package/lib/esm/SQLiteDb.js +0 -1
  31. package/lib/esm/SQLiteDb.js.map +1 -1
  32. package/lib/esm/SqliteChangesetReader.d.ts +5 -0
  33. package/lib/esm/SqliteChangesetReader.d.ts.map +1 -1
  34. package/lib/esm/SqliteChangesetReader.js +7 -0
  35. package/lib/esm/SqliteChangesetReader.js.map +1 -1
  36. package/lib/esm/annotations/TextAnnotationElement.d.ts +1 -1
  37. package/lib/esm/annotations/TextAnnotationElement.d.ts.map +1 -1
  38. package/lib/esm/annotations/TextAnnotationElement.js +14 -1
  39. package/lib/esm/annotations/TextAnnotationElement.js.map +1 -1
  40. package/lib/esm/annotations/TextAnnotationGeometry.d.ts +14 -0
  41. package/lib/esm/annotations/TextAnnotationGeometry.d.ts.map +1 -1
  42. package/lib/esm/annotations/TextAnnotationGeometry.js +29 -10
  43. package/lib/esm/annotations/TextAnnotationGeometry.js.map +1 -1
  44. package/lib/esm/annotations/TextBlockLayout.js +6 -6
  45. package/lib/esm/annotations/TextBlockLayout.js.map +1 -1
  46. package/lib/esm/rpc-impl/RpcBriefcaseUtility.d.ts.map +1 -1
  47. package/lib/esm/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
  48. package/lib/esm/test/annotations/LeaderGeometry.test.js +2 -3
  49. package/lib/esm/test/annotations/LeaderGeometry.test.js.map +1 -1
  50. package/lib/esm/test/annotations/TextAnnotation.test.js +39 -4
  51. package/lib/esm/test/annotations/TextAnnotation.test.js.map +1 -1
  52. package/lib/esm/test/annotations/TextBlock.test.js +49 -42
  53. package/lib/esm/test/annotations/TextBlock.test.js.map +1 -1
  54. package/lib/esm/test/hubaccess/Rebase.test.js +345 -10
  55. package/lib/esm/test/hubaccess/Rebase.test.js.map +1 -1
  56. package/lib/esm/test/imodel/SchemaXmlImport.test.js +87 -44
  57. package/lib/esm/test/imodel/SchemaXmlImport.test.js.map +1 -1
  58. package/lib/esm/test/schema/SchemaImportCallbacks.test.js +4 -4
  59. package/lib/esm/test/schema/SchemaImportCallbacks.test.js.map +1 -1
  60. 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({ ...baseProps, prop1: `${this._data++}` });
119
+ id = b.elements.insertElement(baseProps);
80
120
  });
81
121
  return id;
82
122
  }
83
- return b.elements.insertElement({ ...baseProps, prop1: `${this._data++}` });
123
+ baseProps.prop1 = `${this._data++}`;
124
+ return b.elements.insertElement(baseProps);
84
125
  }
85
- async insertElement2(b, args) {
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("2");
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("1");
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.insertElement2(b1, { parent: { id: parentId, relClassName: "TestDomain:A1OwnsA1" } });
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.insertElement2(b2, { parent: { id: childId, relClassName: "TestDomain:A1OwnsA1" }, markAsIndirect: true });
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