@itwin/core-backend 5.2.0-dev.8 → 5.3.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.
Files changed (198) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/lib/cjs/BackendHubAccess.d.ts +2 -0
  3. package/lib/cjs/BackendHubAccess.d.ts.map +1 -1
  4. package/lib/cjs/BackendHubAccess.js.map +1 -1
  5. package/lib/cjs/BackendLoggerCategory.d.ts +6 -0
  6. package/lib/cjs/BackendLoggerCategory.d.ts.map +1 -1
  7. package/lib/cjs/BackendLoggerCategory.js +6 -0
  8. package/lib/cjs/BackendLoggerCategory.js.map +1 -1
  9. package/lib/cjs/BriefcaseManager.d.ts +57 -3
  10. package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
  11. package/lib/cjs/BriefcaseManager.js +151 -42
  12. package/lib/cjs/BriefcaseManager.js.map +1 -1
  13. package/lib/cjs/CloudSqlite.d.ts +4 -0
  14. package/lib/cjs/CloudSqlite.d.ts.map +1 -1
  15. package/lib/cjs/CloudSqlite.js.map +1 -1
  16. package/lib/cjs/ECDb.d.ts +8 -0
  17. package/lib/cjs/ECDb.d.ts.map +1 -1
  18. package/lib/cjs/ECDb.js +22 -0
  19. package/lib/cjs/ECDb.js.map +1 -1
  20. package/lib/cjs/GeographicCRSServices.d.ts.map +1 -1
  21. package/lib/cjs/GeographicCRSServices.js +2 -0
  22. package/lib/cjs/GeographicCRSServices.js.map +1 -1
  23. package/lib/cjs/IModelDb.d.ts +54 -3
  24. package/lib/cjs/IModelDb.d.ts.map +1 -1
  25. package/lib/cjs/IModelDb.js +87 -9
  26. package/lib/cjs/IModelDb.js.map +1 -1
  27. package/lib/cjs/IModelHost.d.ts +11 -1
  28. package/lib/cjs/IModelHost.d.ts.map +1 -1
  29. package/lib/cjs/IModelHost.js +5 -0
  30. package/lib/cjs/IModelHost.js.map +1 -1
  31. package/lib/cjs/IModelIncrementalSchemaLocater.d.ts +1 -5
  32. package/lib/cjs/IModelIncrementalSchemaLocater.d.ts.map +1 -1
  33. package/lib/cjs/IModelIncrementalSchemaLocater.js +0 -6
  34. package/lib/cjs/IModelIncrementalSchemaLocater.js.map +1 -1
  35. package/lib/cjs/SqliteChangesetReader.d.ts +8 -0
  36. package/lib/cjs/SqliteChangesetReader.d.ts.map +1 -1
  37. package/lib/cjs/SqliteChangesetReader.js +11 -0
  38. package/lib/cjs/SqliteChangesetReader.js.map +1 -1
  39. package/lib/cjs/StashManager.d.ts +175 -0
  40. package/lib/cjs/StashManager.d.ts.map +1 -0
  41. package/lib/cjs/StashManager.js +306 -0
  42. package/lib/cjs/StashManager.js.map +1 -0
  43. package/lib/cjs/TxnManager.d.ts +226 -15
  44. package/lib/cjs/TxnManager.d.ts.map +1 -1
  45. package/lib/cjs/TxnManager.js +249 -23
  46. package/lib/cjs/TxnManager.js.map +1 -1
  47. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts +10 -1
  48. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  49. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js +15 -6
  50. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  51. package/lib/cjs/annotations/LeaderGeometry.d.ts +3 -2
  52. package/lib/cjs/annotations/LeaderGeometry.d.ts.map +1 -1
  53. package/lib/cjs/annotations/LeaderGeometry.js +5 -4
  54. package/lib/cjs/annotations/LeaderGeometry.js.map +1 -1
  55. package/lib/cjs/annotations/TextAnnotationElement.d.ts +52 -24
  56. package/lib/cjs/annotations/TextAnnotationElement.d.ts.map +1 -1
  57. package/lib/cjs/annotations/TextAnnotationElement.js +49 -59
  58. package/lib/cjs/annotations/TextAnnotationElement.js.map +1 -1
  59. package/lib/cjs/annotations/TextAnnotationGeometry.d.ts +2 -0
  60. package/lib/cjs/annotations/TextAnnotationGeometry.d.ts.map +1 -1
  61. package/lib/cjs/annotations/TextAnnotationGeometry.js +26 -19
  62. package/lib/cjs/annotations/TextAnnotationGeometry.js.map +1 -1
  63. package/lib/cjs/annotations/TextBlockGeometry.d.ts.map +1 -1
  64. package/lib/cjs/annotations/TextBlockGeometry.js +8 -0
  65. package/lib/cjs/annotations/TextBlockGeometry.js.map +1 -1
  66. package/lib/cjs/annotations/TextBlockLayout.d.ts +49 -36
  67. package/lib/cjs/annotations/TextBlockLayout.d.ts.map +1 -1
  68. package/lib/cjs/annotations/TextBlockLayout.js +204 -135
  69. package/lib/cjs/annotations/TextBlockLayout.js.map +1 -1
  70. package/lib/cjs/internal/ChannelAdmin.js +1 -1
  71. package/lib/cjs/internal/ChannelAdmin.js.map +1 -1
  72. package/lib/cjs/internal/Symbols.d.ts +1 -0
  73. package/lib/cjs/internal/Symbols.d.ts.map +1 -1
  74. package/lib/cjs/internal/Symbols.js +2 -1
  75. package/lib/cjs/internal/Symbols.js.map +1 -1
  76. package/lib/cjs/internal/annotations/fields.d.ts +2 -12
  77. package/lib/cjs/internal/annotations/fields.d.ts.map +1 -1
  78. package/lib/cjs/internal/annotations/fields.js +49 -45
  79. package/lib/cjs/internal/annotations/fields.js.map +1 -1
  80. package/lib/cjs/workspace/Workspace.d.ts +1 -1
  81. package/lib/cjs/workspace/Workspace.js.map +1 -1
  82. package/lib/esm/BackendHubAccess.d.ts +2 -0
  83. package/lib/esm/BackendHubAccess.d.ts.map +1 -1
  84. package/lib/esm/BackendHubAccess.js.map +1 -1
  85. package/lib/esm/BackendLoggerCategory.d.ts +6 -0
  86. package/lib/esm/BackendLoggerCategory.d.ts.map +1 -1
  87. package/lib/esm/BackendLoggerCategory.js +6 -0
  88. package/lib/esm/BackendLoggerCategory.js.map +1 -1
  89. package/lib/esm/BriefcaseManager.d.ts +57 -3
  90. package/lib/esm/BriefcaseManager.d.ts.map +1 -1
  91. package/lib/esm/BriefcaseManager.js +152 -43
  92. package/lib/esm/BriefcaseManager.js.map +1 -1
  93. package/lib/esm/CloudSqlite.d.ts +4 -0
  94. package/lib/esm/CloudSqlite.d.ts.map +1 -1
  95. package/lib/esm/CloudSqlite.js.map +1 -1
  96. package/lib/esm/ECDb.d.ts +8 -0
  97. package/lib/esm/ECDb.d.ts.map +1 -1
  98. package/lib/esm/ECDb.js +22 -0
  99. package/lib/esm/ECDb.js.map +1 -1
  100. package/lib/esm/GeographicCRSServices.d.ts.map +1 -1
  101. package/lib/esm/GeographicCRSServices.js +2 -0
  102. package/lib/esm/GeographicCRSServices.js.map +1 -1
  103. package/lib/esm/IModelDb.d.ts +54 -3
  104. package/lib/esm/IModelDb.d.ts.map +1 -1
  105. package/lib/esm/IModelDb.js +88 -10
  106. package/lib/esm/IModelDb.js.map +1 -1
  107. package/lib/esm/IModelHost.d.ts +11 -1
  108. package/lib/esm/IModelHost.d.ts.map +1 -1
  109. package/lib/esm/IModelHost.js +5 -0
  110. package/lib/esm/IModelHost.js.map +1 -1
  111. package/lib/esm/IModelIncrementalSchemaLocater.d.ts +1 -5
  112. package/lib/esm/IModelIncrementalSchemaLocater.d.ts.map +1 -1
  113. package/lib/esm/IModelIncrementalSchemaLocater.js +0 -6
  114. package/lib/esm/IModelIncrementalSchemaLocater.js.map +1 -1
  115. package/lib/esm/SqliteChangesetReader.d.ts +8 -0
  116. package/lib/esm/SqliteChangesetReader.d.ts.map +1 -1
  117. package/lib/esm/SqliteChangesetReader.js +11 -0
  118. package/lib/esm/SqliteChangesetReader.js.map +1 -1
  119. package/lib/esm/StashManager.d.ts +175 -0
  120. package/lib/esm/StashManager.d.ts.map +1 -0
  121. package/lib/esm/StashManager.js +301 -0
  122. package/lib/esm/StashManager.js.map +1 -0
  123. package/lib/esm/TxnManager.d.ts +226 -15
  124. package/lib/esm/TxnManager.d.ts.map +1 -1
  125. package/lib/esm/TxnManager.js +247 -21
  126. package/lib/esm/TxnManager.js.map +1 -1
  127. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts +10 -1
  128. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  129. package/lib/esm/annotations/ElementDrivesTextAnnotation.js +13 -5
  130. package/lib/esm/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  131. package/lib/esm/annotations/LeaderGeometry.d.ts +3 -2
  132. package/lib/esm/annotations/LeaderGeometry.d.ts.map +1 -1
  133. package/lib/esm/annotations/LeaderGeometry.js +5 -4
  134. package/lib/esm/annotations/LeaderGeometry.js.map +1 -1
  135. package/lib/esm/annotations/TextAnnotationElement.d.ts +52 -24
  136. package/lib/esm/annotations/TextAnnotationElement.d.ts.map +1 -1
  137. package/lib/esm/annotations/TextAnnotationElement.js +51 -61
  138. package/lib/esm/annotations/TextAnnotationElement.js.map +1 -1
  139. package/lib/esm/annotations/TextAnnotationGeometry.d.ts +2 -0
  140. package/lib/esm/annotations/TextAnnotationGeometry.d.ts.map +1 -1
  141. package/lib/esm/annotations/TextAnnotationGeometry.js +26 -19
  142. package/lib/esm/annotations/TextAnnotationGeometry.js.map +1 -1
  143. package/lib/esm/annotations/TextBlockGeometry.d.ts.map +1 -1
  144. package/lib/esm/annotations/TextBlockGeometry.js +8 -0
  145. package/lib/esm/annotations/TextBlockGeometry.js.map +1 -1
  146. package/lib/esm/annotations/TextBlockLayout.d.ts +49 -36
  147. package/lib/esm/annotations/TextBlockLayout.d.ts.map +1 -1
  148. package/lib/esm/annotations/TextBlockLayout.js +205 -136
  149. package/lib/esm/annotations/TextBlockLayout.js.map +1 -1
  150. package/lib/esm/internal/ChannelAdmin.js +1 -1
  151. package/lib/esm/internal/ChannelAdmin.js.map +1 -1
  152. package/lib/esm/internal/Symbols.d.ts +1 -0
  153. package/lib/esm/internal/Symbols.d.ts.map +1 -1
  154. package/lib/esm/internal/Symbols.js +1 -0
  155. package/lib/esm/internal/Symbols.js.map +1 -1
  156. package/lib/esm/internal/annotations/fields.d.ts +2 -12
  157. package/lib/esm/internal/annotations/fields.d.ts.map +1 -1
  158. package/lib/esm/internal/annotations/fields.js +51 -47
  159. package/lib/esm/internal/annotations/fields.js.map +1 -1
  160. package/lib/esm/test/AnnotationTestUtils.d.ts +5 -1
  161. package/lib/esm/test/AnnotationTestUtils.d.ts.map +1 -1
  162. package/lib/esm/test/AnnotationTestUtils.js +6 -1
  163. package/lib/esm/test/AnnotationTestUtils.js.map +1 -1
  164. package/lib/esm/test/annotations/Fields.test.js +163 -46
  165. package/lib/esm/test/annotations/Fields.test.js.map +1 -1
  166. package/lib/esm/test/annotations/LeaderGeometry.test.js +12 -10
  167. package/lib/esm/test/annotations/LeaderGeometry.test.js.map +1 -1
  168. package/lib/esm/test/annotations/TextAnnotation.test.js +299 -43
  169. package/lib/esm/test/annotations/TextAnnotation.test.js.map +1 -1
  170. package/lib/esm/test/annotations/TextBlock.test.js +453 -86
  171. package/lib/esm/test/annotations/TextBlock.test.js.map +1 -1
  172. package/lib/esm/test/assets/IncrementalSchemaLocater/configs/simple.config.d.ts +46 -0
  173. package/lib/esm/test/assets/IncrementalSchemaLocater/configs/simple.config.d.ts.map +1 -1
  174. package/lib/esm/test/assets/IncrementalSchemaLocater/configs/simple.config.js +20 -2
  175. package/lib/esm/test/assets/IncrementalSchemaLocater/configs/simple.config.js.map +1 -1
  176. package/lib/esm/test/ecdb/ECDb.test.js +71 -1
  177. package/lib/esm/test/ecdb/ECDb.test.js.map +1 -1
  178. package/lib/esm/test/hubaccess/Rebase.test.d.ts +2 -0
  179. package/lib/esm/test/hubaccess/Rebase.test.d.ts.map +1 -0
  180. package/lib/esm/test/hubaccess/Rebase.test.js +640 -0
  181. package/lib/esm/test/hubaccess/Rebase.test.js.map +1 -0
  182. package/lib/esm/test/incrementalSchemaLocater/ECSqlQueries.test.js +20 -20
  183. package/lib/esm/test/incrementalSchemaLocater/ECSqlQueries.test.js.map +1 -1
  184. package/lib/esm/test/incrementalSchemaLocater/IncrementalLoading.test.js +3 -3
  185. package/lib/esm/test/incrementalSchemaLocater/IncrementalLoading.test.js.map +1 -1
  186. package/lib/esm/test/incrementalSchemaLocater/utils/TestSqlSchemaLocater.d.ts +16 -1
  187. package/lib/esm/test/incrementalSchemaLocater/utils/TestSqlSchemaLocater.d.ts.map +1 -1
  188. package/lib/esm/test/incrementalSchemaLocater/utils/TestSqlSchemaLocater.js +47 -0
  189. package/lib/esm/test/incrementalSchemaLocater/utils/TestSqlSchemaLocater.js.map +1 -1
  190. package/lib/esm/test/standalone/ChangeMerge.test.js +15 -19
  191. package/lib/esm/test/standalone/ChangeMerge.test.js.map +1 -1
  192. package/lib/esm/test/standalone/ChangesetReader.test.js +131 -1
  193. package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
  194. package/lib/esm/test/standalone/MergeConflict.test.js +3 -3
  195. package/lib/esm/test/standalone/MergeConflict.test.js.map +1 -1
  196. package/lib/esm/workspace/Workspace.d.ts +1 -1
  197. package/lib/esm/workspace/Workspace.js.map +1 -1
  198. package/package.json +13 -13
@@ -0,0 +1,640 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
+ * See LICENSE.md in the project root for license terms and full copyright notice.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ import * as chai from "chai";
6
+ import * as chaiAsPromised from "chai-as-promised";
7
+ import { HubWrappers, IModelTestUtils, KnownTestLocations } from "..";
8
+ import { BriefcaseManager, ChannelControl, DrawingCategory, IModelHost, SqliteChangesetReader } from "../../core-backend";
9
+ import { HubMock } from "../../internal/HubMock";
10
+ import { Code, IModel, SubCategoryAppearance } from "@itwin/core-common";
11
+ import { Guid } from "@itwin/core-bentley";
12
+ import { StashManager } from "../../StashManager";
13
+ chai.use(chaiAsPromised);
14
+ class TestIModel {
15
+ iModelId = "";
16
+ drawingModelId = "";
17
+ drawingCategoryId = "";
18
+ briefcases = [];
19
+ _data = 0;
20
+ constructor() { }
21
+ async startup() {
22
+ HubMock.startup("TestIModel", KnownTestLocations.outputDir);
23
+ this.iModelId = await HubMock.createNewIModel({ iTwinId: HubMock.iTwinId, iModelName: "Test", description: "TestSubject" });
24
+ const b1 = await HubWrappers.downloadAndOpenBriefcase({ iTwinId: HubMock.iTwinId, iModelId: this.iModelId });
25
+ b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);
26
+ b1.saveChanges();
27
+ const schema1 = `<?xml version="1.0" encoding="UTF-8"?>
28
+ <ECSchema schemaName="TestDomain" alias="ts" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
29
+ <ECSchemaReference name="BisCore" version="01.00.00" alias="bis"/>
30
+ <ECEntityClass typeName="a1">
31
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
32
+ <ECProperty propertyName="prop1" typeName="string" />
33
+ </ECEntityClass>
34
+ <ECRelationshipClass typeName="A1OwnsA1" modifier="None" strength="embedding">
35
+ <BaseClass>bis:ElementOwnsChildElements</BaseClass>
36
+ <Source multiplicity="(0..1)" roleLabel="owns" polymorphic="true">
37
+ <Class class="a1"/>
38
+ </Source>
39
+ <Target multiplicity="(0..*)" roleLabel="is owned by" polymorphic="false">
40
+ <Class class="a1"/>
41
+ </Target>
42
+ </ECRelationshipClass>
43
+ </ECSchema>`;
44
+ await b1.importSchemaStrings([schema1]);
45
+ chai.expect(b1.txns.hasPendingTxns).to.be.true;
46
+ await b1.pushChanges({ description: "schema1" });
47
+ const codeProps = Code.createEmpty();
48
+ codeProps.value = "DrawingModel";
49
+ await b1.locks.acquireLocks({ shared: IModel.dictionaryId });
50
+ this.drawingModelId = IModelTestUtils.createAndInsertDrawingPartitionAndModel(b1, codeProps, true)[1];
51
+ let drawingCategoryId = DrawingCategory.queryCategoryIdByName(b1, IModel.dictionaryId, "MyDrawingCategory");
52
+ if (undefined === drawingCategoryId)
53
+ drawingCategoryId = DrawingCategory.insert(b1, IModel.dictionaryId, "MyDrawingCategory", new SubCategoryAppearance());
54
+ this.drawingCategoryId = drawingCategoryId;
55
+ b1.saveChanges();
56
+ await b1.pushChanges({ description: "drawing category" });
57
+ b1.close();
58
+ }
59
+ async openBriefcase() {
60
+ const b = await HubWrappers.downloadAndOpenBriefcase({ iTwinId: HubMock.iTwinId, iModelId: this.iModelId });
61
+ b.channels.addAllowedChannel(ChannelControl.sharedChannelName);
62
+ b.saveChanges();
63
+ this.briefcases.push(b);
64
+ return b;
65
+ }
66
+ async insertElement(b, markAsIndirect) {
67
+ await b.locks.acquireLocks({ shared: [this.drawingModelId] });
68
+ const baseProps = {
69
+ classFullName: "TestDomain:a1",
70
+ model: this.drawingModelId,
71
+ category: this.drawingCategoryId,
72
+ code: Code.createEmpty(),
73
+ };
74
+ let id = "";
75
+ if (markAsIndirect) {
76
+ b.txns.withIndirectTxnMode(() => {
77
+ id = b.elements.insertElement({ ...baseProps, prop1: `${this._data++}` });
78
+ });
79
+ return id;
80
+ }
81
+ return b.elements.insertElement({ ...baseProps, prop1: `${this._data++}` });
82
+ }
83
+ async insertElement2(b, args) {
84
+ await b.locks.acquireLocks({ shared: [this.drawingModelId] });
85
+ const props = {
86
+ classFullName: "TestDomain:a1",
87
+ model: this.drawingModelId,
88
+ category: this.drawingCategoryId,
89
+ code: Code.createEmpty(),
90
+ parent: args?.parent,
91
+ prop1: args?.prop1 ?? `${this._data++}`
92
+ };
93
+ let id = "";
94
+ if (args?.markAsIndirect) {
95
+ b.txns.withIndirectTxnMode(() => {
96
+ id = b.elements.insertElement(props);
97
+ });
98
+ return id;
99
+ }
100
+ return b.elements.insertElement(props);
101
+ }
102
+ async updateElement(b, id, markAsIndirect) {
103
+ await b.locks.acquireLocks({ shared: [this.drawingModelId], exclusive: [id] });
104
+ const elProps = b.elements.getElementProps(id);
105
+ if (markAsIndirect) {
106
+ b.txns.withIndirectTxnMode(() => {
107
+ b.elements.updateElement({ ...elProps, prop1: `${this._data++}` });
108
+ });
109
+ }
110
+ else {
111
+ b.elements.updateElement({ ...elProps, prop1: `${this._data++}` });
112
+ }
113
+ }
114
+ async deleteElement(b, id, markAsIndirect) {
115
+ await b.locks.acquireLocks({ shared: [this.drawingModelId], exclusive: [id] });
116
+ if (markAsIndirect) {
117
+ b.txns.withIndirectTxnMode(() => {
118
+ b.elements.deleteElement(id);
119
+ });
120
+ }
121
+ else {
122
+ b.elements.deleteElement(id);
123
+ }
124
+ }
125
+ async shutdown() {
126
+ this.briefcases.forEach(b => b.close());
127
+ HubMock.shutdown();
128
+ }
129
+ }
130
+ describe("rebase changes & stashing api", function () {
131
+ let testIModel;
132
+ before(async () => {
133
+ if (!IModelHost.isValid)
134
+ await IModelHost.startup();
135
+ });
136
+ this.beforeEach(async () => {
137
+ testIModel = new TestIModel();
138
+ await testIModel.startup();
139
+ });
140
+ this.afterEach(async () => {
141
+ await testIModel.shutdown();
142
+ });
143
+ it("save changes args", async () => {
144
+ const b1 = await testIModel.openBriefcase();
145
+ await testIModel.insertElement(b1);
146
+ b1.saveChanges({
147
+ source: "test",
148
+ description: "test description",
149
+ appData: {
150
+ test: "test",
151
+ foo: [1, 2, 3],
152
+ bar: { baz: "qux" }
153
+ }
154
+ });
155
+ let lastTxn = b1.txns.getLastSavedTxnProps();
156
+ chai.assert.isDefined(lastTxn);
157
+ if (lastTxn) {
158
+ chai.expect(lastTxn.props.source).to.be.equals("test");
159
+ chai.expect(lastTxn.props.description).to.be.equals("test description");
160
+ chai.expect(lastTxn.props.appData).to.not.be.undefined;
161
+ chai.expect(lastTxn.props.appData?.test).to.be.eq("test");
162
+ chai.expect(lastTxn.props.appData?.foo).to.be.deep.eq([1, 2, 3]);
163
+ chai.expect(lastTxn.props.appData?.bar).to.be.deep.eq({ baz: "qux" });
164
+ chai.expect(lastTxn.nextId).to.be.undefined;
165
+ chai.expect(lastTxn.prevId).to.be.undefined;
166
+ chai.expect(lastTxn.type).to.be.eq("Data");
167
+ chai.expect(lastTxn.id).to.be.eq('0x100000000');
168
+ chai.expect(lastTxn.reversed).to.be.false;
169
+ chai.expect(lastTxn.grouped).to.be.false;
170
+ }
171
+ await testIModel.insertElement(b1);
172
+ b1.saveChanges({
173
+ source: "test2",
174
+ description: "test description 2",
175
+ appData: {
176
+ test: "test 2",
177
+ foo: [11, 12, 13],
178
+ bar: { baz: "qux2" }
179
+ }
180
+ });
181
+ lastTxn = b1.txns.getLastSavedTxnProps();
182
+ chai.assert.isDefined(lastTxn);
183
+ if (lastTxn) {
184
+ chai.expect(lastTxn.props.source).to.be.equals("test2");
185
+ chai.expect(lastTxn.props.description).to.be.equals("test description 2");
186
+ chai.expect(lastTxn.props.appData).to.not.be.undefined;
187
+ chai.expect(lastTxn.props.appData?.test).to.be.eq("test 2");
188
+ chai.expect(lastTxn.props.appData?.foo).to.be.deep.eq([11, 12, 13]);
189
+ chai.expect(lastTxn.props.appData?.bar).to.be.deep.eq({ baz: "qux2" });
190
+ chai.expect(lastTxn.nextId).to.be.undefined;
191
+ chai.expect(lastTxn.prevId).to.be.equal('0x100000000');
192
+ chai.expect(lastTxn.type).to.be.eq("Data");
193
+ chai.expect(lastTxn.id).to.be.eq('0x100000001');
194
+ chai.expect(lastTxn.reversed).to.be.false;
195
+ chai.expect(lastTxn.grouped).to.be.false;
196
+ }
197
+ await testIModel.insertElement(b1);
198
+ b1.saveChanges("new element");
199
+ lastTxn = b1.txns.getLastSavedTxnProps();
200
+ chai.assert.isDefined(lastTxn);
201
+ if (lastTxn) {
202
+ chai.expect(lastTxn.props.source).is.undefined;
203
+ chai.expect(lastTxn.props.description).to.be.equals("new element");
204
+ chai.expect(lastTxn.props.appData).to.be.undefined;
205
+ chai.expect(lastTxn.nextId).to.be.undefined;
206
+ chai.expect(lastTxn.prevId).to.be.equal('0x100000001');
207
+ chai.expect(lastTxn.type).to.be.eq("Data");
208
+ chai.expect(lastTxn.id).to.be.eq('0x100000002');
209
+ chai.expect(lastTxn.reversed).to.be.false;
210
+ chai.expect(lastTxn.grouped).to.be.false;
211
+ }
212
+ await b1.pushChanges({ description: "new element" });
213
+ chai.expect(b1.txns.isUndoPossible).is.false;
214
+ chai.expect(b1.txns.isRedoPossible).is.false;
215
+ lastTxn = b1.txns.getLastSavedTxnProps();
216
+ chai.assert.isUndefined(lastTxn);
217
+ });
218
+ it("direct / indirect", async () => {
219
+ const b1 = await testIModel.openBriefcase();
220
+ const directElId = await testIModel.insertElement(b1);
221
+ const indirectElId = await testIModel.insertElement(b1, true);
222
+ chai.expect(directElId).to.not.be.undefined;
223
+ chai.expect(indirectElId).to.not.be.undefined;
224
+ b1.saveChanges({ description: "insert element 1 direct and 1 indirect" });
225
+ const txn = b1.txns.getLastSavedTxnProps();
226
+ chai.assert.isDefined(txn);
227
+ if (txn) {
228
+ let checkCount = 0;
229
+ const reader = SqliteChangesetReader.openTxn({ txnId: txn?.id, db: b1 });
230
+ while (reader.step()) {
231
+ if (reader.primaryKeyValues.length === 0)
232
+ continue;
233
+ if (reader.tableName !== "bis_Element")
234
+ continue;
235
+ const iid = reader.primaryKeyValues[0];
236
+ if (iid === directElId) {
237
+ chai.expect(reader.isIndirect).to.be.false;
238
+ }
239
+ if (iid === indirectElId) {
240
+ chai.expect(reader.isIndirect).to.be.true;
241
+ }
242
+ checkCount++;
243
+ }
244
+ chai.expect(checkCount).to.be.equals(2);
245
+ }
246
+ await b1.pushChanges({ description: "insert element 1 direct and 1 indirect" });
247
+ chai.expect(b1.txns.isUndoPossible).is.false;
248
+ chai.expect(b1.txns.isRedoPossible).is.false;
249
+ const lastTxn = b1.txns.getLastSavedTxnProps();
250
+ chai.assert.isUndefined(lastTxn);
251
+ });
252
+ it("rebase handler", async () => {
253
+ const b1 = await testIModel.openBriefcase();
254
+ const b2 = await testIModel.openBriefcase();
255
+ const e1 = await testIModel.insertElement(b1);
256
+ const e2 = await testIModel.insertElement(b1, true);
257
+ b1.saveChanges();
258
+ await b1.pushChanges({ description: "insert element 1 direct and 1 indirect" });
259
+ await b2.pullChanges();
260
+ await testIModel.updateElement(b1, e1);
261
+ await testIModel.updateElement(b1, e2, true);
262
+ b1.saveChanges();
263
+ await b1.pushChanges({ description: "update element 1 direct and 1 indirect" });
264
+ await testIModel.insertElement(b2);
265
+ await testIModel.insertElement(b2, true);
266
+ b2.saveChanges("first change");
267
+ await testIModel.insertElement(b2);
268
+ await testIModel.insertElement(b2, true);
269
+ b2.saveChanges("second change");
270
+ await testIModel.insertElement(b2);
271
+ await testIModel.insertElement(b2, true);
272
+ b2.saveChanges("third change");
273
+ b2.txns.rebaser.setCustomHandler({
274
+ shouldReinstate: (_txn) => {
275
+ return true;
276
+ },
277
+ recompute: async (_txn) => {
278
+ await testIModel.insertElement(b2);
279
+ await testIModel.insertElement(b2, true);
280
+ },
281
+ });
282
+ await b1.pullChanges();
283
+ });
284
+ it("stash & drop", async () => {
285
+ const b1 = await testIModel.openBriefcase();
286
+ const e1 = await testIModel.insertElement(b1);
287
+ b1.saveChanges();
288
+ await b1.pushChanges({ description: "insert element 1 direct and 1 indirect" });
289
+ const e2 = await testIModel.insertElement(b1);
290
+ b1.saveChanges();
291
+ await b1.pushChanges({ description: "insert element 1 direct and 1 indirect" });
292
+ await testIModel.insertElement(b1);
293
+ b1.saveChanges(`first`);
294
+ await testIModel.updateElement(b1, e1);
295
+ b1.saveChanges(`second`);
296
+ await testIModel.deleteElement(b1, e2);
297
+ b1.saveChanges(`third`);
298
+ await testIModel.insertElement(b1);
299
+ b1.saveChanges(`fourth`);
300
+ const stash1 = await StashManager.stash({ db: b1, description: "stash test 1" });
301
+ chai.expect(stash1).to.exist;
302
+ chai.assert(Guid.isGuid(stash1.id));
303
+ chai.expect(stash1.description).to.equals("stash test 1");
304
+ chai.expect(stash1.briefcaseId).equals(b1.briefcaseId);
305
+ chai.expect(stash1.iModelId).to.equals(b1.iModelId);
306
+ chai.expect(stash1.timestamp).to.exist;
307
+ chai.expect(stash1.description).to.exist;
308
+ chai.expect(stash1.hash).length(64);
309
+ chai.expect(stash1.parentChangeset).to.exist;
310
+ chai.expect(stash1.idSequences.element).to.equals("0x30000000004");
311
+ chai.expect(stash1.idSequences.instance).to.equals("0x30000000000");
312
+ chai.expect(stash1.acquiredLocks).equals(4);
313
+ chai.expect(stash1.txns).to.exist;
314
+ chai.expect(stash1.txns).to.have.lengthOf(4);
315
+ chai.expect(stash1.txns[0].props.description).to.equal("first");
316
+ chai.expect(stash1.txns[1].props.description).to.equal("second");
317
+ chai.expect(stash1.txns[2].props.description).to.equal("third");
318
+ chai.expect(stash1.txns[3].props.description).to.equal("fourth");
319
+ chai.expect(stash1.txns[0].id).to.equals("0x100000000");
320
+ chai.expect(stash1.txns[1].id).to.equals("0x100000001");
321
+ chai.expect(stash1.txns[2].id).to.equals("0x100000002");
322
+ chai.expect(stash1.txns[3].id).to.equals("0x100000003");
323
+ await testIModel.insertElement(b1);
324
+ b1.saveChanges(`fifth`);
325
+ await testIModel.updateElement(b1, e1);
326
+ b1.saveChanges(`sixth`);
327
+ await testIModel.insertElement(b1);
328
+ b1.saveChanges(`seventh`);
329
+ const stash2 = await StashManager.stash({ db: b1, description: "stash test 2" });
330
+ chai.expect(stash2).to.exist;
331
+ chai.expect(stash2.description).to.equals("stash test 2");
332
+ chai.expect(stash2.hash).length(64);
333
+ chai.expect(stash2.parentChangeset).to.exist;
334
+ chai.expect(stash2.idSequences.element).to.equals("0x30000000006");
335
+ chai.expect(stash2.idSequences.instance).to.equals("0x30000000000");
336
+ chai.expect(stash2.acquiredLocks).equals(4);
337
+ chai.expect(stash2.txns).to.exist;
338
+ chai.expect(stash2.txns).to.have.lengthOf(7);
339
+ chai.expect(stash2.txns[0].props.description).to.equal("first");
340
+ chai.expect(stash2.txns[1].props.description).to.equal("second");
341
+ chai.expect(stash2.txns[2].props.description).to.equal("third");
342
+ chai.expect(stash2.txns[3].props.description).to.equal("fourth");
343
+ chai.expect(stash2.txns[4].props.description).to.equal("fifth");
344
+ chai.expect(stash2.txns[5].props.description).to.equal("sixth");
345
+ chai.expect(stash2.txns[6].props.description).to.equal("seventh");
346
+ chai.expect(stash2.txns[0].id).to.equals("0x100000000");
347
+ chai.expect(stash2.txns[1].id).to.equals("0x100000001");
348
+ chai.expect(stash2.txns[2].id).to.equals("0x100000002");
349
+ chai.expect(stash2.txns[3].id).to.equals("0x100000003");
350
+ chai.expect(stash2.txns[4].id).to.equals("0x100000004");
351
+ chai.expect(stash2.txns[5].id).to.equals("0x100000005");
352
+ chai.expect(stash2.txns[6].id).to.equals("0x100000006");
353
+ const stashes = StashManager.getStashes(b1);
354
+ chai.expect(stashes).to.have.lengthOf(2);
355
+ chai.expect(stashes[0].description).to.equals("stash test 2");
356
+ chai.expect(stashes[1].description).to.equals("stash test 1");
357
+ chai.expect(stashes[0]).to.deep.equal(stash2);
358
+ chai.expect(stashes[1]).to.deep.equal(stash1);
359
+ StashManager.dropAllStashes(b1);
360
+ chai.expect(StashManager.getStashes(b1)).to.have.lengthOf(0);
361
+ });
362
+ it("should restore mutually exclusive stashes", async () => {
363
+ const b1 = await testIModel.openBriefcase();
364
+ // stash 1
365
+ const e1 = await testIModel.insertElement(b1);
366
+ chai.expect(e1).to.exist;
367
+ b1.saveChanges("first");
368
+ const stash1 = await StashManager.stash({ db: b1, description: "stash test 1", discardLocalChanges: true, retainLocks: true });
369
+ chai.expect(stash1).to.exist;
370
+ chai.expect(b1.elements.tryGetElement(e1)).to.undefined;
371
+ chai.expect(b1.txns.isUndoPossible).to.be.false;
372
+ chai.expect(b1.txns.isRedoPossible).to.be.false;
373
+ // stash 2
374
+ const e2 = await testIModel.insertElement(b1);
375
+ chai.expect(e2).to.exist;
376
+ b1.saveChanges("second");
377
+ const stash2 = await StashManager.stash({ db: b1, description: "stash test 2", discardLocalChanges: true, retainLocks: true });
378
+ chai.expect(stash2).to.exist;
379
+ chai.expect(b1.elements.tryGetElement(e1)).to.undefined;
380
+ chai.expect(b1.elements.tryGetElement(e2)).to.undefined;
381
+ chai.expect(b1.txns.isUndoPossible).to.be.false;
382
+ chai.expect(b1.txns.isRedoPossible).to.be.false;
383
+ // stash 3
384
+ const e3 = await testIModel.insertElement(b1);
385
+ chai.expect(e3).to.exist;
386
+ b1.saveChanges("third");
387
+ const stash3 = await StashManager.stash({ db: b1, description: "stash test 3", discardLocalChanges: true, retainLocks: true });
388
+ chai.expect(stash3).to.exist;
389
+ chai.expect(b1.elements.tryGetElement(e1)).to.undefined;
390
+ chai.expect(b1.elements.tryGetElement(e2)).to.undefined;
391
+ chai.expect(b1.elements.tryGetElement(e3)).to.undefined;
392
+ chai.expect(b1.txns.isUndoPossible).to.be.false;
393
+ chai.expect(b1.txns.isRedoPossible).to.be.false;
394
+ chai.expect(e1).not.equals(e2);
395
+ chai.expect(e1).not.equals(e3);
396
+ chai.expect(e2).not.equals(e3);
397
+ const stashes = StashManager.getStashes(b1);
398
+ chai.expect(stashes).to.have.lengthOf(3);
399
+ chai.expect(stashes[0].description).to.equals("stash test 3");
400
+ chai.expect(stashes[1].description).to.equals("stash test 2");
401
+ chai.expect(stashes[2].description).to.equals("stash test 1");
402
+ // restore stash 1
403
+ await StashManager.restore({ db: b1, stash: stash1 });
404
+ chai.expect(b1.elements.tryGetElement(e1)).to.exist;
405
+ chai.expect(b1.elements.tryGetElement(e2)).to.undefined;
406
+ chai.expect(b1.elements.tryGetElement(e3)).to.undefined;
407
+ // restore stash 2
408
+ await StashManager.restore({ db: b1, stash: stash2 });
409
+ chai.expect(b1.elements.tryGetElement(e1)).to.undefined;
410
+ chai.expect(b1.elements.tryGetElement(e2)).to.exist;
411
+ chai.expect(b1.elements.tryGetElement(e3)).to.undefined;
412
+ // restore stash 3
413
+ await StashManager.restore({ db: b1, stash: stash3 });
414
+ chai.expect(b1.elements.tryGetElement(e1)).to.undefined;
415
+ chai.expect(b1.elements.tryGetElement(e2)).to.undefined;
416
+ chai.expect(b1.elements.tryGetElement(e3)).to.exist;
417
+ });
418
+ it("should restore stash in any order", async () => {
419
+ const b1 = await testIModel.openBriefcase();
420
+ // stash 1
421
+ const e1 = await testIModel.insertElement(b1);
422
+ chai.expect(e1).to.exist;
423
+ b1.saveChanges("first");
424
+ // do not discard local changes
425
+ const stash1 = await StashManager.stash({ db: b1, description: "stash test 1" });
426
+ chai.expect(stash1).to.exist;
427
+ chai.expect(b1.elements.tryGetElement(e1)).to.exist;
428
+ chai.expect(b1.txns.isUndoPossible).to.be.true;
429
+ chai.expect(b1.txns.isRedoPossible).to.be.false;
430
+ // stash 2
431
+ const e2 = await testIModel.insertElement(b1);
432
+ chai.expect(e2).to.exist;
433
+ b1.saveChanges("second");
434
+ // do not discard local changes
435
+ const stash2 = await StashManager.stash({ db: b1, description: "stash test 2" });
436
+ chai.expect(stash2).to.exist;
437
+ chai.expect(b1.elements.tryGetElement(e1)).to.exist;
438
+ chai.expect(b1.elements.tryGetElement(e2)).to.exist;
439
+ chai.expect(b1.txns.isUndoPossible).to.be.true;
440
+ chai.expect(b1.txns.isRedoPossible).to.be.false;
441
+ // stash 3
442
+ const e3 = await testIModel.insertElement(b1);
443
+ chai.expect(e3).to.exist;
444
+ b1.saveChanges("third");
445
+ // do not discard local changes
446
+ const stash3 = await StashManager.stash({ db: b1, description: "stash test 3" });
447
+ chai.expect(stash3).to.exist;
448
+ chai.expect(b1.elements.tryGetElement(e1)).to.exist;
449
+ chai.expect(b1.elements.tryGetElement(e2)).to.exist;
450
+ chai.expect(b1.elements.tryGetElement(e3)).to.exist;
451
+ chai.expect(b1.txns.isUndoPossible).to.be.true;
452
+ chai.expect(b1.txns.isRedoPossible).to.be.false;
453
+ const stashes = StashManager.getStashes(b1);
454
+ chai.expect(stashes).to.have.lengthOf(3);
455
+ chai.expect(stashes[0].description).to.equals("stash test 3");
456
+ chai.expect(stashes[1].description).to.equals("stash test 2");
457
+ chai.expect(stashes[2].description).to.equals("stash test 1");
458
+ await b1.discardChanges({ retainLocks: true });
459
+ chai.expect(b1.elements.tryGetElement(e1)).to.undefined;
460
+ chai.expect(b1.elements.tryGetElement(e2)).to.undefined;
461
+ chai.expect(b1.elements.tryGetElement(e3)).to.undefined;
462
+ chai.expect(b1.txns.isUndoPossible).to.be.false;
463
+ chai.expect(b1.txns.isRedoPossible).to.be.false;
464
+ // restore stash 1
465
+ await StashManager.restore({ db: b1, stash: stash1 });
466
+ chai.expect(b1.elements.tryGetElement(e1)).to.exist;
467
+ chai.expect(b1.elements.tryGetElement(e2)).to.undefined;
468
+ chai.expect(b1.elements.tryGetElement(e3)).to.undefined;
469
+ // restore stash 2
470
+ await StashManager.restore({ db: b1, stash: stash2 });
471
+ chai.expect(b1.elements.tryGetElement(e1)).to.exist;
472
+ chai.expect(b1.elements.tryGetElement(e2)).to.exist;
473
+ chai.expect(b1.elements.tryGetElement(e3)).to.undefined;
474
+ // restore stash 3
475
+ await StashManager.restore({ db: b1, stash: stash3 });
476
+ chai.expect(b1.elements.tryGetElement(e1)).to.exist;
477
+ chai.expect(b1.elements.tryGetElement(e2)).to.exist;
478
+ chai.expect(b1.elements.tryGetElement(e3)).to.exist;
479
+ });
480
+ it("should restore stash when briefcase has advanced to latest changeset", async () => {
481
+ const b1 = await testIModel.openBriefcase();
482
+ const b2 = await testIModel.openBriefcase();
483
+ chai.expect(b1.changeset.index).to.equals(2);
484
+ chai.expect(b2.changeset.index).to.equals(2);
485
+ const e1 = await testIModel.insertElement(b1);
486
+ chai.expect(e1).to.exist;
487
+ b1.saveChanges();
488
+ await b1.pushChanges({ description: `${e1} inserted` });
489
+ chai.expect(b1.changeset.index).to.equals(3);
490
+ const e2 = await testIModel.insertElement(b2);
491
+ chai.expect(e2).to.exist;
492
+ b2.saveChanges();
493
+ chai.expect(b2.elements.tryGetElement(e1)).to.undefined;
494
+ chai.expect(b2.elements.tryGetElement(e2)).to.exist;
495
+ const b2Stash1 = await StashManager.stash({ db: b2, description: "stash test 1", discardLocalChanges: true });
496
+ chai.expect(b2Stash1.parentChangeset.index).to.equals(2);
497
+ chai.expect(b2.elements.tryGetElement(e1)).to.undefined;
498
+ chai.expect(b2.elements.tryGetElement(e2)).to.undefined;
499
+ await b2.pullChanges();
500
+ chai.expect(b2.changeset.index).to.equals(3);
501
+ chai.expect(b2.elements.tryGetElement(e1)).to.exist;
502
+ chai.expect(b2.elements.tryGetElement(e2)).to.undefined;
503
+ // stash restore should downgrade briefcase to older changeset as specified in stash
504
+ await StashManager.restore({ db: b2, stash: b2Stash1 });
505
+ chai.expect(b2.changeset.index).to.equals(2);
506
+ chai.expect(b2.elements.tryGetElement(e1)).to.undefined;
507
+ chai.expect(b2.elements.tryGetElement(e2)).to.exist;
508
+ await b2.pullChanges();
509
+ chai.expect(b2.changeset.index).to.equals(3);
510
+ chai.expect(b2.elements.tryGetElement(e1)).to.exist;
511
+ chai.expect(b2.elements.tryGetElement(e2)).to.exist;
512
+ await b2.pushChanges({ description: "test" });
513
+ chai.expect(b2.changeset.index).to.equals(4);
514
+ });
515
+ it("restore stash that has element changed by another briefcase", async () => {
516
+ const b1 = await testIModel.openBriefcase();
517
+ const b2 = await testIModel.openBriefcase();
518
+ chai.expect(b1.changeset.index).to.equals(2);
519
+ chai.expect(b2.changeset.index).to.equals(2);
520
+ const e1 = await testIModel.insertElement(b1);
521
+ chai.expect(e1).to.exist;
522
+ b1.saveChanges();
523
+ await b1.pushChanges({ description: `${e1} inserted` });
524
+ chai.expect(b1.changeset.index).to.equals(3);
525
+ await b2.pullChanges();
526
+ chai.expect(b2.changeset.index).to.equals(3);
527
+ await testIModel.updateElement(b2, e1);
528
+ b2.saveChanges();
529
+ chai.expect(b2.locks.holdsExclusiveLock(e1)).to.be.true;
530
+ const b2Stash1 = await StashManager.stash({ db: b2, description: "stash test 1", discardLocalChanges: true });
531
+ chai.expect(b2Stash1.parentChangeset.index).to.equals(3);
532
+ chai.expect(b2.locks.holdsExclusiveLock(e1)).to.be.false;
533
+ // stash release lock so b2 should have released lock and b1 should be able to update.
534
+ await testIModel.updateElement(b1, e1);
535
+ b1.saveChanges();
536
+ // restore stash should fail because of lock not obtained on e1
537
+ await chai.expect(StashManager.restore({ db: b2, stash: b2Stash1 })).to.be.rejectedWith("exclusive lock is already held");
538
+ // push b1 changes to release lock
539
+ await b1.pushChanges({ description: `${e1} inserted` });
540
+ // restore stash should fail because pull is required to obtain lock
541
+ await chai.expect(StashManager.restore({ db: b2, stash: b2Stash1 })).to.be.rejectedWith("pull is required to obtain lock");
542
+ await b2.pullChanges();
543
+ chai.expect(b2.changeset.index).to.equals(4);
544
+ const elBefore = b2.elements.tryGetElementProps(e1);
545
+ chai.expect(elBefore.prop1).to.equals("2");
546
+ // restore stash should succeed as now it can obtain lock
547
+ await StashManager.restore({ db: b2, stash: b2Stash1 });
548
+ const elAfter = b2.elements.tryGetElementProps(e1);
549
+ chai.expect(elAfter.prop1).to.equals("1");
550
+ await b2.pushChanges({ description: `${e1} updated` });
551
+ });
552
+ it("schema change should not be stashed", async () => {
553
+ const b1 = await testIModel.openBriefcase();
554
+ const schema1 = `<?xml version="1.0" encoding="UTF-8"?>
555
+ <ECSchema schemaName="TestDomain" alias="ts" version="01.00.01" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
556
+ <ECSchemaReference name="BisCore" version="01.00.00" alias="bis"/>
557
+ <ECEntityClass typeName="a1">
558
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
559
+ <ECProperty propertyName="prop1" typeName="string" />
560
+ <ECProperty propertyName="prop2" typeName="string" />
561
+ </ECEntityClass>
562
+ <ECRelationshipClass typeName="A1OwnsA1" modifier="None" strength="embedding">
563
+ <BaseClass>bis:ElementOwnsChildElements</BaseClass>
564
+ <Source multiplicity="(0..1)" roleLabel="owns" polymorphic="true">
565
+ <Class class="a1"/>
566
+ </Source>
567
+ <Target multiplicity="(0..*)" roleLabel="is owned by" polymorphic="false">
568
+ <Class class="a1"/>
569
+ </Target>
570
+ </ECRelationshipClass>
571
+ </ECSchema>`;
572
+ await b1.importSchemaStrings([schema1]);
573
+ b1.saveChanges();
574
+ await chai.expect(StashManager.stash({ db: b1, description: "stash test 1" })).to.not.rejectedWith("Bad Arg: Pending schema changeset stashing is not currently supported");
575
+ });
576
+ it("abort rebase", async () => {
577
+ const b1 = await testIModel.openBriefcase();
578
+ const b2 = await testIModel.openBriefcase();
579
+ const e1 = await testIModel.insertElement(b1);
580
+ b1.saveChanges();
581
+ await b1.pushChanges({ description: `${e1} inserted` });
582
+ const e2 = await testIModel.insertElement(b2);
583
+ chai.expect(e2).to.exist;
584
+ let e3 = "";
585
+ b2.saveChanges();
586
+ b2.txns.rebaser.setCustomHandler({
587
+ shouldReinstate: (_txnProps) => {
588
+ return true;
589
+ },
590
+ recompute: async (_txnProps) => {
591
+ chai.expect(BriefcaseManager.containsRestorePoint(b2, BriefcaseManager.PULL_MERGE_RESTORE_POINT_NAME)).is.true;
592
+ e3 = await testIModel.insertElement(b2);
593
+ throw new Error("Rebase failed");
594
+ },
595
+ });
596
+ chai.expect(b2.elements.tryGetElementProps(e1)).to.undefined;
597
+ chai.expect(b2.elements.tryGetElementProps(e2)).to.exist;
598
+ chai.expect(b2.elements.tryGetElementProps(e3)).to.undefined;
599
+ chai.expect(b2.changeset.index).to.equals(2);
600
+ await chai.expect(b2.pullChanges()).to.be.rejectedWith("Rebase failed");
601
+ chai.expect(b2.changeset.index).to.equals(3);
602
+ chai.expect(e3).to.exist;
603
+ chai.expect(b2.elements.tryGetElementProps(e1)).to.exist; // came from incoming changeset
604
+ chai.expect(b2.elements.tryGetElementProps(e2)).to.undefined; // was local change and reversed during rebase.
605
+ chai.expect(b2.elements.tryGetElementProps(e3)).to.undefined; // was insert by reCompute() but due to exception the rebase attempt was abandoned.
606
+ chai.expect(BriefcaseManager.containsRestorePoint(b2, BriefcaseManager.PULL_MERGE_RESTORE_POINT_NAME)).is.true;
607
+ chai.expect(b2.txns.rebaser.canAbort()).is.true;
608
+ await b2.txns.rebaser.abort();
609
+ chai.expect(b2.changeset.index).to.equals(2);
610
+ chai.expect(b2.elements.tryGetElementProps(e1)).to.undefined; // reset briefcase should move tip back to where it was before pull
611
+ chai.expect(b2.elements.tryGetElementProps(e2)).to.exist; // abort should put back e2 which was only change at the time of pull
612
+ chai.expect(b2.elements.tryGetElementProps(e3)).to.undefined; // add by rebase so should not exist either
613
+ chai.expect(BriefcaseManager.containsRestorePoint(b2, BriefcaseManager.PULL_MERGE_RESTORE_POINT_NAME)).is.false;
614
+ });
615
+ it("getStash() should throw exception", async () => {
616
+ const b1 = await testIModel.openBriefcase();
617
+ chai.expect(() => StashManager.getStash({ db: b1, stash: "invalid_stash" })).to.throw("Invalid stash");
618
+ chai.expect(StashManager.tryGetStash({ db: b1, stash: "invalid_stash" })).to.be.undefined;
619
+ });
620
+ it("edge case: a indirect update can cause FK violation", async () => {
621
+ const b1 = await testIModel.openBriefcase();
622
+ const b2 = await testIModel.openBriefcase();
623
+ const parentId = await testIModel.insertElement(b1);
624
+ const childId = await testIModel.insertElement2(b1, { parent: { id: parentId, relClassName: "TestDomain:A1OwnsA1" } });
625
+ b1.saveChanges("insert parent and child");
626
+ await b1.pushChanges({ description: `inserted parent ${parentId} and child ${childId}` });
627
+ await b2.pullChanges();
628
+ // b1 delete childId while b1 create a child of childId as indirect change
629
+ await testIModel.deleteElement(b1, childId);
630
+ b1.saveChanges("delete child");
631
+ // no exclusive lock required on child1
632
+ const grandChildId = await testIModel.insertElement2(b2, { parent: { id: childId, relClassName: "TestDomain:A1OwnsA1" }, markAsIndirect: true });
633
+ b2.saveChanges("delete child and insert grandchild");
634
+ await b1.pushChanges({ description: `deleted child ${childId}` });
635
+ // should fail to pull and rebase changes.
636
+ await chai.expect(b2.pushChanges({ description: `deleted child ${childId} and inserted grandchild ${grandChildId}` }))
637
+ .to.be.rejectedWith("Foreign key conflicts in ChangeSet. Aborting rebase.");
638
+ });
639
+ });
640
+ //# sourceMappingURL=Rebase.test.js.map