@itwin/core-backend 5.15.0-dev.7 → 5.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/CHANGELOG.md +46 -1
  2. package/lib/cjs/BlobContainerService.d.ts +2 -0
  3. package/lib/cjs/BlobContainerService.d.ts.map +1 -1
  4. package/lib/cjs/BlobContainerService.js.map +1 -1
  5. package/lib/cjs/BriefcaseManager.d.ts +2 -1
  6. package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
  7. package/lib/cjs/BriefcaseManager.js +39 -9
  8. package/lib/cjs/BriefcaseManager.js.map +1 -1
  9. package/lib/cjs/ChangeUnifier.d.ts +82 -0
  10. package/lib/cjs/ChangeUnifier.d.ts.map +1 -0
  11. package/lib/cjs/ChangeUnifier.js +163 -0
  12. package/lib/cjs/ChangeUnifier.js.map +1 -0
  13. package/lib/cjs/ChangesetReader.d.ts +21 -2
  14. package/lib/cjs/ChangesetReader.d.ts.map +1 -1
  15. package/lib/cjs/ChangesetReader.js +37 -1
  16. package/lib/cjs/ChangesetReader.js.map +1 -1
  17. package/lib/cjs/CloudSqlite.d.ts +15 -0
  18. package/lib/cjs/CloudSqlite.d.ts.map +1 -1
  19. package/lib/cjs/CloudSqlite.js.map +1 -1
  20. package/lib/cjs/ECDb.d.ts +1 -0
  21. package/lib/cjs/ECDb.d.ts.map +1 -1
  22. package/lib/cjs/ECDb.js +4 -1
  23. package/lib/cjs/ECDb.js.map +1 -1
  24. package/lib/cjs/EditTxn.d.ts +61 -19
  25. package/lib/cjs/EditTxn.d.ts.map +1 -1
  26. package/lib/cjs/EditTxn.js +84 -29
  27. package/lib/cjs/EditTxn.js.map +1 -1
  28. package/lib/cjs/GeoCoordConfig.d.ts +1 -0
  29. package/lib/cjs/GeoCoordConfig.d.ts.map +1 -1
  30. package/lib/cjs/GeoCoordConfig.js +12 -8
  31. package/lib/cjs/GeoCoordConfig.js.map +1 -1
  32. package/lib/cjs/IModelDb.d.ts +22 -9
  33. package/lib/cjs/IModelDb.d.ts.map +1 -1
  34. package/lib/cjs/IModelDb.js +54 -12
  35. package/lib/cjs/IModelDb.js.map +1 -1
  36. package/lib/cjs/NativeHost.d.ts.map +1 -1
  37. package/lib/cjs/NativeHost.js +5 -1
  38. package/lib/cjs/NativeHost.js.map +1 -1
  39. package/lib/cjs/Relationship.d.ts +28 -0
  40. package/lib/cjs/Relationship.d.ts.map +1 -1
  41. package/lib/cjs/Relationship.js +78 -0
  42. package/lib/cjs/Relationship.js.map +1 -1
  43. package/lib/cjs/SchemaSync.d.ts.map +1 -1
  44. package/lib/cjs/SchemaSync.js +1 -0
  45. package/lib/cjs/SchemaSync.js.map +1 -1
  46. package/lib/cjs/TxnManager.d.ts +126 -89
  47. package/lib/cjs/TxnManager.d.ts.map +1 -1
  48. package/lib/cjs/TxnManager.js +114 -76
  49. package/lib/cjs/TxnManager.js.map +1 -1
  50. package/lib/cjs/ViewStateHydrator.d.ts.map +1 -1
  51. package/lib/cjs/ViewStateHydrator.js.map +1 -1
  52. package/lib/cjs/ViewStore.d.ts.map +1 -1
  53. package/lib/cjs/ViewStore.js +62 -19
  54. package/lib/cjs/ViewStore.js.map +1 -1
  55. package/lib/cjs/annotations/LeaderGeometry.d.ts.map +1 -1
  56. package/lib/cjs/annotations/LeaderGeometry.js +3 -0
  57. package/lib/cjs/annotations/LeaderGeometry.js.map +1 -1
  58. package/lib/cjs/core-backend.d.ts +1 -0
  59. package/lib/cjs/core-backend.d.ts.map +1 -1
  60. package/lib/cjs/core-backend.js +1 -0
  61. package/lib/cjs/core-backend.js.map +1 -1
  62. package/lib/cjs/internal/BriefcaseUtils.d.ts +19 -0
  63. package/lib/cjs/internal/BriefcaseUtils.d.ts.map +1 -0
  64. package/lib/cjs/internal/BriefcaseUtils.js +65 -0
  65. package/lib/cjs/internal/BriefcaseUtils.js.map +1 -0
  66. package/lib/cjs/internal/QueryMetadataCache.d.ts +44 -0
  67. package/lib/cjs/internal/QueryMetadataCache.d.ts.map +1 -0
  68. package/lib/cjs/internal/QueryMetadataCache.js +107 -0
  69. package/lib/cjs/internal/QueryMetadataCache.js.map +1 -0
  70. package/lib/cjs/internal/Symbols.d.ts +9 -0
  71. package/lib/cjs/internal/Symbols.d.ts.map +1 -1
  72. package/lib/cjs/internal/Symbols.js +10 -1
  73. package/lib/cjs/internal/Symbols.js.map +1 -1
  74. package/lib/cjs/internal/workspace/WorkspaceImpl.d.ts.map +1 -1
  75. package/lib/cjs/internal/workspace/WorkspaceImpl.js +20 -6
  76. package/lib/cjs/internal/workspace/WorkspaceImpl.js.map +1 -1
  77. package/lib/cjs/rpc-impl/IModelReadRpcImpl.d.ts +6 -6
  78. package/lib/cjs/rpc-impl/IModelReadRpcImpl.d.ts.map +1 -1
  79. package/lib/cjs/rpc-impl/IModelReadRpcImpl.js.map +1 -1
  80. package/lib/cjs/workspace/SettingsEditor.d.ts +4 -3
  81. package/lib/cjs/workspace/SettingsEditor.d.ts.map +1 -1
  82. package/lib/cjs/workspace/SettingsEditor.js +7 -5
  83. package/lib/cjs/workspace/SettingsEditor.js.map +1 -1
  84. package/lib/esm/BlobContainerService.d.ts +2 -0
  85. package/lib/esm/BlobContainerService.d.ts.map +1 -1
  86. package/lib/esm/BlobContainerService.js.map +1 -1
  87. package/lib/esm/BriefcaseManager.d.ts +2 -1
  88. package/lib/esm/BriefcaseManager.d.ts.map +1 -1
  89. package/lib/esm/BriefcaseManager.js +40 -10
  90. package/lib/esm/BriefcaseManager.js.map +1 -1
  91. package/lib/esm/ChangeUnifier.d.ts +82 -0
  92. package/lib/esm/ChangeUnifier.d.ts.map +1 -0
  93. package/lib/esm/ChangeUnifier.js +159 -0
  94. package/lib/esm/ChangeUnifier.js.map +1 -0
  95. package/lib/esm/ChangesetReader.d.ts +21 -2
  96. package/lib/esm/ChangesetReader.d.ts.map +1 -1
  97. package/lib/esm/ChangesetReader.js +38 -2
  98. package/lib/esm/ChangesetReader.js.map +1 -1
  99. package/lib/esm/CloudSqlite.d.ts +15 -0
  100. package/lib/esm/CloudSqlite.d.ts.map +1 -1
  101. package/lib/esm/CloudSqlite.js.map +1 -1
  102. package/lib/esm/ECDb.d.ts +1 -0
  103. package/lib/esm/ECDb.d.ts.map +1 -1
  104. package/lib/esm/ECDb.js +4 -1
  105. package/lib/esm/ECDb.js.map +1 -1
  106. package/lib/esm/EditTxn.d.ts +61 -19
  107. package/lib/esm/EditTxn.d.ts.map +1 -1
  108. package/lib/esm/EditTxn.js +85 -30
  109. package/lib/esm/EditTxn.js.map +1 -1
  110. package/lib/esm/GeoCoordConfig.d.ts +1 -0
  111. package/lib/esm/GeoCoordConfig.d.ts.map +1 -1
  112. package/lib/esm/GeoCoordConfig.js +12 -8
  113. package/lib/esm/GeoCoordConfig.js.map +1 -1
  114. package/lib/esm/IModelDb.d.ts +22 -9
  115. package/lib/esm/IModelDb.d.ts.map +1 -1
  116. package/lib/esm/IModelDb.js +56 -14
  117. package/lib/esm/IModelDb.js.map +1 -1
  118. package/lib/esm/NativeHost.d.ts.map +1 -1
  119. package/lib/esm/NativeHost.js +5 -1
  120. package/lib/esm/NativeHost.js.map +1 -1
  121. package/lib/esm/Relationship.d.ts +28 -0
  122. package/lib/esm/Relationship.d.ts.map +1 -1
  123. package/lib/esm/Relationship.js +78 -0
  124. package/lib/esm/Relationship.js.map +1 -1
  125. package/lib/esm/SchemaSync.d.ts.map +1 -1
  126. package/lib/esm/SchemaSync.js +1 -0
  127. package/lib/esm/SchemaSync.js.map +1 -1
  128. package/lib/esm/TxnManager.d.ts +126 -89
  129. package/lib/esm/TxnManager.d.ts.map +1 -1
  130. package/lib/esm/TxnManager.js +116 -78
  131. package/lib/esm/TxnManager.js.map +1 -1
  132. package/lib/esm/ViewStateHydrator.d.ts.map +1 -1
  133. package/lib/esm/ViewStateHydrator.js.map +1 -1
  134. package/lib/esm/ViewStore.d.ts.map +1 -1
  135. package/lib/esm/ViewStore.js +62 -19
  136. package/lib/esm/ViewStore.js.map +1 -1
  137. package/lib/esm/annotations/LeaderGeometry.d.ts.map +1 -1
  138. package/lib/esm/annotations/LeaderGeometry.js +3 -0
  139. package/lib/esm/annotations/LeaderGeometry.js.map +1 -1
  140. package/lib/esm/core-backend.d.ts +1 -0
  141. package/lib/esm/core-backend.d.ts.map +1 -1
  142. package/lib/esm/core-backend.js +1 -0
  143. package/lib/esm/core-backend.js.map +1 -1
  144. package/lib/esm/internal/BriefcaseUtils.d.ts +19 -0
  145. package/lib/esm/internal/BriefcaseUtils.d.ts.map +1 -0
  146. package/lib/esm/internal/BriefcaseUtils.js +61 -0
  147. package/lib/esm/internal/BriefcaseUtils.js.map +1 -0
  148. package/lib/esm/internal/QueryMetadataCache.d.ts +44 -0
  149. package/lib/esm/internal/QueryMetadataCache.d.ts.map +1 -0
  150. package/lib/esm/internal/QueryMetadataCache.js +103 -0
  151. package/lib/esm/internal/QueryMetadataCache.js.map +1 -0
  152. package/lib/esm/internal/Symbols.d.ts +9 -0
  153. package/lib/esm/internal/Symbols.d.ts.map +1 -1
  154. package/lib/esm/internal/Symbols.js +9 -0
  155. package/lib/esm/internal/Symbols.js.map +1 -1
  156. package/lib/esm/internal/workspace/WorkspaceImpl.d.ts.map +1 -1
  157. package/lib/esm/internal/workspace/WorkspaceImpl.js +20 -6
  158. package/lib/esm/internal/workspace/WorkspaceImpl.js.map +1 -1
  159. package/lib/esm/rpc-impl/IModelReadRpcImpl.d.ts +6 -6
  160. package/lib/esm/rpc-impl/IModelReadRpcImpl.d.ts.map +1 -1
  161. package/lib/esm/rpc-impl/IModelReadRpcImpl.js.map +1 -1
  162. package/lib/esm/test/annotations/LeaderGeometry.test.js +15 -1
  163. package/lib/esm/test/annotations/LeaderGeometry.test.js.map +1 -1
  164. package/lib/esm/test/ecdb/ConcurrentQuery.test.js +144 -30
  165. package/lib/esm/test/ecdb/ConcurrentQuery.test.js.map +1 -1
  166. package/lib/esm/test/ecdb/QueryMetadataCache.test.d.ts +2 -0
  167. package/lib/esm/test/ecdb/QueryMetadataCache.test.d.ts.map +1 -0
  168. package/lib/esm/test/ecdb/QueryMetadataCache.test.js +630 -0
  169. package/lib/esm/test/ecdb/QueryMetadataCache.test.js.map +1 -0
  170. package/lib/esm/test/element/DeleteDefinitionElements.test.js +403 -249
  171. package/lib/esm/test/element/DeleteDefinitionElements.test.js.map +1 -1
  172. package/lib/esm/test/element/RelationshipBatchLookup.test.d.ts +2 -0
  173. package/lib/esm/test/element/RelationshipBatchLookup.test.d.ts.map +1 -0
  174. package/lib/esm/test/element/RelationshipBatchLookup.test.js +202 -0
  175. package/lib/esm/test/element/RelationshipBatchLookup.test.js.map +1 -0
  176. package/lib/esm/test/hubaccess/BriefcaseManager.test.js +187 -1
  177. package/lib/esm/test/hubaccess/BriefcaseManager.test.js.map +1 -1
  178. package/lib/esm/test/hubaccess/SchemaImportOrphanColumns.test.d.ts +2 -0
  179. package/lib/esm/test/hubaccess/SchemaImportOrphanColumns.test.d.ts.map +1 -0
  180. package/lib/esm/test/hubaccess/SchemaImportOrphanColumns.test.js +142 -0
  181. package/lib/esm/test/hubaccess/SchemaImportOrphanColumns.test.js.map +1 -0
  182. package/lib/esm/test/imodel/IModelGeolocation.test.js +8 -6
  183. package/lib/esm/test/imodel/IModelGeolocation.test.js.map +1 -1
  184. package/lib/esm/test/imodel/IModelProps.test.d.ts +2 -0
  185. package/lib/esm/test/imodel/IModelProps.test.d.ts.map +1 -0
  186. package/lib/esm/test/imodel/IModelProps.test.js +411 -0
  187. package/lib/esm/test/imodel/IModelProps.test.js.map +1 -0
  188. package/lib/esm/test/schema/SchemaViewPropertyOverrides.test.d.ts +2 -0
  189. package/lib/esm/test/schema/SchemaViewPropertyOverrides.test.d.ts.map +1 -0
  190. package/lib/esm/test/schema/SchemaViewPropertyOverrides.test.js +240 -0
  191. package/lib/esm/test/schema/SchemaViewPropertyOverrides.test.js.map +1 -0
  192. package/lib/esm/test/schema/SchemaViewValidation.test.js +2 -8
  193. package/lib/esm/test/schema/SchemaViewValidation.test.js.map +1 -1
  194. package/lib/esm/test/standalone/BriefcaseManagerDelete.test.d.ts +2 -0
  195. package/lib/esm/test/standalone/BriefcaseManagerDelete.test.d.ts.map +1 -0
  196. package/lib/esm/test/standalone/BriefcaseManagerDelete.test.js +159 -0
  197. package/lib/esm/test/standalone/BriefcaseManagerDelete.test.js.map +1 -0
  198. package/lib/esm/test/standalone/ChangeUnifier.test.d.ts +2 -0
  199. package/lib/esm/test/standalone/ChangeUnifier.test.d.ts.map +1 -0
  200. package/lib/esm/test/standalone/ChangeUnifier.test.js +732 -0
  201. package/lib/esm/test/standalone/ChangeUnifier.test.js.map +1 -0
  202. package/lib/esm/test/standalone/DeleteElements.test.js +962 -83
  203. package/lib/esm/test/standalone/DeleteElements.test.js.map +1 -1
  204. package/lib/esm/test/standalone/EditTxn.test.js +72 -1
  205. package/lib/esm/test/standalone/EditTxn.test.js.map +1 -1
  206. package/lib/esm/test/standalone/GeoCoordConfig.test.d.ts +2 -0
  207. package/lib/esm/test/standalone/GeoCoordConfig.test.d.ts.map +1 -0
  208. package/lib/esm/test/standalone/GeoCoordConfig.test.js +158 -0
  209. package/lib/esm/test/standalone/GeoCoordConfig.test.js.map +1 -0
  210. package/lib/esm/test/standalone/ViewStoreDb.test.js +53 -0
  211. package/lib/esm/test/standalone/ViewStoreDb.test.js.map +1 -1
  212. package/lib/esm/test/workspace/SettingsContainers.test.d.ts +2 -0
  213. package/lib/esm/test/workspace/SettingsContainers.test.d.ts.map +1 -0
  214. package/lib/esm/test/workspace/SettingsContainers.test.js +95 -0
  215. package/lib/esm/test/workspace/SettingsContainers.test.js.map +1 -0
  216. package/lib/esm/workspace/SettingsEditor.d.ts +4 -3
  217. package/lib/esm/workspace/SettingsEditor.d.ts.map +1 -1
  218. package/lib/esm/workspace/SettingsEditor.js +7 -5
  219. package/lib/esm/workspace/SettingsEditor.js.map +1 -1
  220. package/package.json +18 -18
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=RelationshipBatchLookup.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelationshipBatchLookup.test.d.ts","sourceRoot":"","sources":["../../../../src/test/element/RelationshipBatchLookup.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,202 @@
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 { expect } from "chai";
6
+ import * as sinon from "sinon";
7
+ import { Guid, Id64 } from "@itwin/core-bentley";
8
+ import { Code, IModel } from "@itwin/core-common";
9
+ import { withEditTxn } from "../../EditTxn";
10
+ import { RepositoryLink } from "../../Element";
11
+ import { SnapshotDb } from "../../IModelDb";
12
+ import { ElementDrivesElement, ElementGroupsMembers, ElementRefersToElements } from "../../Relationship";
13
+ import { IModelTestUtils } from "../IModelTestUtils";
14
+ describe("Relationships batched lookup", () => {
15
+ const testSchema = `<?xml version="1.0" encoding="UTF-8"?>
16
+ <ECSchema schemaName="BatchLookupTest" alias="blt" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
17
+ <ECSchemaReference name="BisCore" version="01.00.00" alias="bis"/>
18
+ <ECStructClass typeName="TestStruct">
19
+ <ECProperty propertyName="label" typeName="string"/>
20
+ <ECProperty propertyName="origin" typeName="point2d"/>
21
+ </ECStructClass>
22
+ <ECRelationshipClass typeName="TestRefersToElements" strength="referencing" modifier="Sealed">
23
+ <BaseClass>bis:ElementRefersToElements</BaseClass>
24
+ <Source multiplicity="(0..*)" roleLabel="refers to" polymorphic="true">
25
+ <Class class="bis:Element"/>
26
+ </Source>
27
+ <Target multiplicity="(0..*)" roleLabel="is referenced by" polymorphic="true">
28
+ <Class class="bis:Element"/>
29
+ </Target>
30
+ <ECProperty propertyName="Location" typeName="point3d"/>
31
+ <ECStructProperty propertyName="Info" typeName="TestStruct"/>
32
+ <ECArrayProperty propertyName="Points" typeName="point3d" minOccurs="0" maxOccurs="unbounded"/>
33
+ </ECRelationshipClass>
34
+ </ECSchema>`;
35
+ const testRelClass = "BatchLookupTest:TestRefersToElements";
36
+ let imodel;
37
+ let elementIds;
38
+ let relIds;
39
+ before(async () => {
40
+ imodel = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile("Relationship", "BatchLookup.bim"), { rootSubject: { name: "BatchLookup" } });
41
+ await imodel.importSchemaStrings([testSchema]);
42
+ withEditTxn(imodel, (txn) => {
43
+ elementIds = [];
44
+ for (let i = 0; i < 6; ++i) {
45
+ const props = {
46
+ classFullName: RepositoryLink.classFullName,
47
+ model: IModel.repositoryModelId,
48
+ code: Code.createEmpty(),
49
+ repositoryGuid: Guid.createValue(),
50
+ };
51
+ elementIds.push(txn.insertElement(props));
52
+ }
53
+ // Chain e0->e1->e2->e3->e4 with memberPriority equal to the source index.
54
+ relIds = [];
55
+ for (let i = 0; i < 4; ++i)
56
+ relIds.push(txn.insertRelationship(ElementGroupsMembers.create(imodel, elementIds[i], elementIds[i + 1], i).toJSON()));
57
+ });
58
+ });
59
+ after(() => imodel.close());
60
+ it("matches tryGetInstanceProps for ids and source/target pairs, preserving order", async () => {
61
+ const [e0, e1, e2, e3, e4, e5] = elementIds;
62
+ const criteria = [
63
+ relIds[2],
64
+ { sourceId: e0, targetId: e1 },
65
+ Id64.fromLocalAndBriefcaseIds(0x7fff, 0), // non-existent id
66
+ { sourceId: e3, targetId: e4 },
67
+ { sourceId: e0, targetId: e2 }, // both ends requested elsewhere, but not related as a pair
68
+ { sourceId: e4, targetId: e5 }, // unrelated
69
+ Id64.invalid,
70
+ relIds[2], // duplicate
71
+ { sourceId: e1, targetId: e2 },
72
+ relIds[0],
73
+ ];
74
+ const batch = await imodel.relationships.queryInstancesProps(ElementGroupsMembers.classFullName, criteria);
75
+ expect(batch.length).to.equal(criteria.length);
76
+ criteria.forEach((criterion, i) => {
77
+ expect(batch[i]).to.deep.equal(imodel.relationships.tryGetInstanceProps(ElementGroupsMembers.classFullName, criterion), `criterion ${i}`);
78
+ });
79
+ expect(batch[0]?.id).to.equal(relIds[2]);
80
+ expect(batch[0]?.classFullName).to.equal(ElementGroupsMembers.classFullName);
81
+ expect(batch[1].memberPriority).to.equal(0);
82
+ expect(batch[1]?.id).to.equal(relIds[0]);
83
+ expect(batch[2]).to.be.undefined;
84
+ expect(batch[3]?.id).to.equal(relIds[3]);
85
+ expect(batch[4]).to.be.undefined;
86
+ expect(batch[5]).to.be.undefined;
87
+ expect(batch[6]).to.be.undefined;
88
+ expect(batch[7]?.id).to.equal(relIds[2]);
89
+ expect(batch[8]?.id).to.equal(relIds[1]);
90
+ // The concurrent query connection sees the same saved data.
91
+ expect(await imodel.relationships.queryInstancesProps(ElementGroupsMembers.classFullName, criteria, { usePrimaryConn: false })).to.deep.equal(batch);
92
+ });
93
+ it("returns an empty array for empty criteria", async () => {
94
+ expect(await imodel.relationships.queryInstancesProps(ElementGroupsMembers.classFullName, [])).to.deep.equal([]);
95
+ expect(await imodel.relationships.queryInstances(ElementGroupsMembers.classFullName, [])).to.deep.equal([]);
96
+ });
97
+ it("queries polymorphically and respects the relationship class", async () => {
98
+ const criteria = [relIds[0], { sourceId: elementIds[1], targetId: elementIds[2] }];
99
+ const viaBase = await imodel.relationships.queryInstancesProps(ElementRefersToElements.classFullName, criteria);
100
+ expect(viaBase.map((p) => p?.classFullName)).to.deep.equal([ElementGroupsMembers.classFullName, ElementGroupsMembers.classFullName]);
101
+ criteria.forEach((criterion, i) => expect(viaBase[i]).to.deep.equal(imodel.relationships.tryGetInstanceProps(ElementRefersToElements.classFullName, criterion), `criterion ${i}`));
102
+ const viaOther = await imodel.relationships.queryInstancesProps(ElementDrivesElement.classFullName, criteria);
103
+ expect(viaOther).to.deep.equal([undefined, undefined]);
104
+ });
105
+ it("queryInstances constructs Relationship entities", async () => {
106
+ const criteria = [relIds[1], { sourceId: elementIds[3], targetId: elementIds[4] }, Id64.invalid];
107
+ const rels = await imodel.relationships.queryInstances(ElementGroupsMembers.classFullName, criteria);
108
+ expect(rels.length).to.equal(3);
109
+ expect(rels[0]).to.be.instanceOf(ElementGroupsMembers);
110
+ expect(rels[0]?.id).to.equal(relIds[1]);
111
+ expect(rels[0]?.memberPriority).to.equal(1);
112
+ expect(rels[1]).to.be.instanceOf(ElementGroupsMembers);
113
+ expect(rels[1]?.sourceId).to.equal(elementIds[3]);
114
+ expect(rels[1]?.targetId).to.equal(elementIds[4]);
115
+ expect(rels[2]).to.be.undefined;
116
+ criteria.forEach((criterion, i) => expect(rels[i]?.toJSON()).to.deep.equal(imodel.relationships.tryGetInstance(ElementGroupsMembers.classFullName, criterion)?.toJSON(), `criterion ${i}`));
117
+ });
118
+ it("sees unsaved changes by default", async () => {
119
+ const cls = ElementGroupsMembers.classFullName;
120
+ const pair = { sourceId: elementIds[4], targetId: elementIds[5] };
121
+ await withEditTxn(imodel, async (txn) => {
122
+ const id = txn.insertRelationship(ElementGroupsMembers.create(imodel, pair.sourceId, pair.targetId, 4).toJSON());
123
+ const found = await imodel.relationships.queryInstances(cls, [id, pair]);
124
+ expect(found.map((r) => r?.id)).to.deep.equal([id, id]);
125
+ txn.deleteRelationship(imodel.relationships.getInstanceProps(cls, id));
126
+ });
127
+ });
128
+ it("converts point, struct and array properties like tryGetInstanceProps", async () => {
129
+ const pair = { sourceId: elementIds[0], targetId: elementIds[5] };
130
+ await withEditTxn(imodel, async (txn) => {
131
+ const id = txn.insertRelationship({
132
+ classFullName: testRelClass,
133
+ ...pair,
134
+ location: { x: 1, y: 2, z: 3 },
135
+ info: { label: "a", origin: { x: 4, y: 5 } },
136
+ points: [{ x: 6, y: 7, z: 8 }, { x: 9, y: 10, z: 11 }],
137
+ });
138
+ const criteria = [id, pair];
139
+ const batch = await imodel.relationships.queryInstancesProps(testRelClass, criteria);
140
+ criteria.forEach((criterion, i) => expect(batch[i]).to.deep.equal(imodel.relationships.tryGetInstanceProps(testRelClass, criterion), `criterion ${i}`));
141
+ expect(batch[0]).to.include({ id, classFullName: testRelClass });
142
+ expect(batch[0]).to.deep.include({
143
+ location: { x: 1, y: 2, z: 3 },
144
+ info: { label: "a", origin: { x: 4, y: 5 } },
145
+ points: [{ x: 6, y: 7, z: 8 }, { x: 9, y: 10, z: 11 }],
146
+ });
147
+ txn.deleteRelationship(batch[0]);
148
+ });
149
+ });
150
+ it("reads only the requested pairs in a dense many-to-many graph", async () => {
151
+ const cls = ElementGroupsMembers.classFullName;
152
+ const n = 5;
153
+ await withEditTxn(imodel, async (txn) => {
154
+ const sources = [];
155
+ const targets = [];
156
+ for (let i = 0; i < 2 * n; ++i) {
157
+ const id = txn.insertElement({ classFullName: RepositoryLink.classFullName, model: IModel.repositoryModelId, code: Code.createEmpty(), repositoryGuid: Guid.createValue() });
158
+ (i < n ? sources : targets).push(id);
159
+ }
160
+ // Connect every source to every target, then request only the "diagonal" pairs.
161
+ for (const sourceId of sources)
162
+ for (const targetId of targets)
163
+ txn.insertRelationship(ElementGroupsMembers.create(imodel, sourceId, targetId, 0).toJSON());
164
+ const pairs = sources.map((sourceId, i) => ({ sourceId, targetId: targets[i] }));
165
+ let rowsRead = 0;
166
+ const createQueryReader = imodel.createQueryReader.bind(imodel);
167
+ const stub = sinon.stub(imodel, "createQueryReader").callsFake((ecsql, params, config) => {
168
+ const reader = createQueryReader(ecsql, params, config);
169
+ const step = reader.step.bind(reader);
170
+ reader.step = async () => {
171
+ const hasRow = await step();
172
+ if (hasRow)
173
+ ++rowsRead;
174
+ return hasRow;
175
+ };
176
+ return reader;
177
+ });
178
+ try {
179
+ const found = await imodel.relationships.queryInstancesProps(cls, pairs);
180
+ expect(found.map((p) => p && { sourceId: p.sourceId, targetId: p.targetId })).to.deep.equal(pairs);
181
+ }
182
+ finally {
183
+ stub.restore();
184
+ }
185
+ expect(rowsRead).to.equal(n);
186
+ });
187
+ });
188
+ it("supports relationships stored as navigation properties and ids from any briefcase", async () => {
189
+ const cls = "BisCore:ModelContainsElements";
190
+ const highBriefcaseId = Id64.fromLocalAndBriefcaseIds(1, 0xfffffd);
191
+ const criteria = [
192
+ elementIds[0],
193
+ { sourceId: IModel.repositoryModelId, targetId: elementIds[1] },
194
+ highBriefcaseId,
195
+ { sourceId: highBriefcaseId, targetId: elementIds[1] },
196
+ ];
197
+ const batch = await imodel.relationships.queryInstancesProps(cls, criteria);
198
+ criteria.forEach((criterion, i) => expect(batch[i]).to.deep.equal(imodel.relationships.tryGetInstanceProps(cls, criterion), `criterion ${i}`));
199
+ expect(batch.map((p) => p?.targetId)).to.deep.equal([elementIds[0], elementIds[1], undefined, undefined]);
200
+ });
201
+ });
202
+ //# sourceMappingURL=RelationshipBatchLookup.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelationshipBatchLookup.test.js","sourceRoot":"","sources":["../../../../src/test/element/RelationshipBatchLookup.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\nimport { expect } from \"chai\";\r\nimport * as sinon from \"sinon\";\r\nimport { Guid, Id64, Id64String } from \"@itwin/core-bentley\";\r\nimport { Code, IModel, RelationshipProps, RepositoryLinkProps, SourceAndTarget } from \"@itwin/core-common\";\r\nimport { withEditTxn } from \"../../EditTxn\";\r\nimport { RepositoryLink } from \"../../Element\";\r\nimport { SnapshotDb } from \"../../IModelDb\";\r\nimport { ElementDrivesElement, ElementGroupsMembers, ElementRefersToElements } from \"../../Relationship\";\r\nimport { IModelTestUtils } from \"../IModelTestUtils\";\r\n\r\ndescribe(\"Relationships batched lookup\", () => {\r\n const testSchema = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n <ECSchema schemaName=\"BatchLookupTest\" alias=\"blt\" version=\"01.00.00\" xmlns=\"http://www.bentley.com/schemas/Bentley.ECXML.3.2\">\r\n <ECSchemaReference name=\"BisCore\" version=\"01.00.00\" alias=\"bis\"/>\r\n <ECStructClass typeName=\"TestStruct\">\r\n <ECProperty propertyName=\"label\" typeName=\"string\"/>\r\n <ECProperty propertyName=\"origin\" typeName=\"point2d\"/>\r\n </ECStructClass>\r\n <ECRelationshipClass typeName=\"TestRefersToElements\" strength=\"referencing\" modifier=\"Sealed\">\r\n <BaseClass>bis:ElementRefersToElements</BaseClass>\r\n <Source multiplicity=\"(0..*)\" roleLabel=\"refers to\" polymorphic=\"true\">\r\n <Class class=\"bis:Element\"/>\r\n </Source>\r\n <Target multiplicity=\"(0..*)\" roleLabel=\"is referenced by\" polymorphic=\"true\">\r\n <Class class=\"bis:Element\"/>\r\n </Target>\r\n <ECProperty propertyName=\"Location\" typeName=\"point3d\"/>\r\n <ECStructProperty propertyName=\"Info\" typeName=\"TestStruct\"/>\r\n <ECArrayProperty propertyName=\"Points\" typeName=\"point3d\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\r\n </ECRelationshipClass>\r\n </ECSchema>`;\r\n const testRelClass = \"BatchLookupTest:TestRefersToElements\";\r\n\r\n let imodel: SnapshotDb;\r\n let elementIds: Id64String[];\r\n let relIds: Id64String[];\r\n\r\n before(async () => {\r\n imodel = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile(\"Relationship\", \"BatchLookup.bim\"), { rootSubject: { name: \"BatchLookup\" } });\r\n await imodel.importSchemaStrings([testSchema]);\r\n withEditTxn(imodel, (txn) => {\r\n elementIds = [];\r\n for (let i = 0; i < 6; ++i) {\r\n const props: RepositoryLinkProps = {\r\n classFullName: RepositoryLink.classFullName,\r\n model: IModel.repositoryModelId,\r\n code: Code.createEmpty(),\r\n repositoryGuid: Guid.createValue(),\r\n };\r\n elementIds.push(txn.insertElement(props));\r\n }\r\n\r\n // Chain e0->e1->e2->e3->e4 with memberPriority equal to the source index.\r\n relIds = [];\r\n for (let i = 0; i < 4; ++i)\r\n relIds.push(txn.insertRelationship(ElementGroupsMembers.create(imodel, elementIds[i], elementIds[i + 1], i).toJSON()));\r\n });\r\n });\r\n\r\n after(() => imodel.close());\r\n\r\n it(\"matches tryGetInstanceProps for ids and source/target pairs, preserving order\", async () => {\r\n const [e0, e1, e2, e3, e4, e5] = elementIds;\r\n const criteria: Array<Id64String | SourceAndTarget> = [\r\n relIds[2],\r\n { sourceId: e0, targetId: e1 },\r\n Id64.fromLocalAndBriefcaseIds(0x7fff, 0), // non-existent id\r\n { sourceId: e3, targetId: e4 },\r\n { sourceId: e0, targetId: e2 }, // both ends requested elsewhere, but not related as a pair\r\n { sourceId: e4, targetId: e5 }, // unrelated\r\n Id64.invalid,\r\n relIds[2], // duplicate\r\n { sourceId: e1, targetId: e2 },\r\n relIds[0],\r\n ];\r\n\r\n const batch = await imodel.relationships.queryInstancesProps(ElementGroupsMembers.classFullName, criteria);\r\n expect(batch.length).to.equal(criteria.length);\r\n criteria.forEach((criterion, i) => {\r\n expect(batch[i]).to.deep.equal(imodel.relationships.tryGetInstanceProps(ElementGroupsMembers.classFullName, criterion), `criterion ${i}`);\r\n });\r\n\r\n expect(batch[0]?.id).to.equal(relIds[2]);\r\n expect(batch[0]?.classFullName).to.equal(ElementGroupsMembers.classFullName);\r\n expect((batch[1] as any).memberPriority).to.equal(0);\r\n expect(batch[1]?.id).to.equal(relIds[0]);\r\n expect(batch[2]).to.be.undefined;\r\n expect(batch[3]?.id).to.equal(relIds[3]);\r\n expect(batch[4]).to.be.undefined;\r\n expect(batch[5]).to.be.undefined;\r\n expect(batch[6]).to.be.undefined;\r\n expect(batch[7]?.id).to.equal(relIds[2]);\r\n expect(batch[8]?.id).to.equal(relIds[1]);\r\n\r\n // The concurrent query connection sees the same saved data.\r\n expect(await imodel.relationships.queryInstancesProps(ElementGroupsMembers.classFullName, criteria, { usePrimaryConn: false })).to.deep.equal(batch);\r\n });\r\n\r\n it(\"returns an empty array for empty criteria\", async () => {\r\n expect(await imodel.relationships.queryInstancesProps(ElementGroupsMembers.classFullName, [])).to.deep.equal([]);\r\n expect(await imodel.relationships.queryInstances(ElementGroupsMembers.classFullName, [])).to.deep.equal([]);\r\n });\r\n\r\n it(\"queries polymorphically and respects the relationship class\", async () => {\r\n const criteria = [relIds[0], { sourceId: elementIds[1], targetId: elementIds[2] }];\r\n\r\n const viaBase = await imodel.relationships.queryInstancesProps(ElementRefersToElements.classFullName, criteria);\r\n expect(viaBase.map((p) => p?.classFullName)).to.deep.equal([ElementGroupsMembers.classFullName, ElementGroupsMembers.classFullName]);\r\n criteria.forEach((criterion, i) => expect(viaBase[i]).to.deep.equal(imodel.relationships.tryGetInstanceProps(ElementRefersToElements.classFullName, criterion), `criterion ${i}`));\r\n\r\n const viaOther = await imodel.relationships.queryInstancesProps(ElementDrivesElement.classFullName, criteria);\r\n expect(viaOther).to.deep.equal([undefined, undefined]);\r\n });\r\n\r\n it(\"queryInstances constructs Relationship entities\", async () => {\r\n const criteria = [relIds[1], { sourceId: elementIds[3], targetId: elementIds[4] }, Id64.invalid];\r\n const rels = await imodel.relationships.queryInstances<ElementGroupsMembers>(ElementGroupsMembers.classFullName, criteria);\r\n expect(rels.length).to.equal(3);\r\n expect(rels[0]).to.be.instanceOf(ElementGroupsMembers);\r\n expect(rels[0]?.id).to.equal(relIds[1]);\r\n expect(rels[0]?.memberPriority).to.equal(1);\r\n expect(rels[1]).to.be.instanceOf(ElementGroupsMembers);\r\n expect(rels[1]?.sourceId).to.equal(elementIds[3]);\r\n expect(rels[1]?.targetId).to.equal(elementIds[4]);\r\n expect(rels[2]).to.be.undefined;\r\n criteria.forEach((criterion, i) => expect(rels[i]?.toJSON()).to.deep.equal(imodel.relationships.tryGetInstance(ElementGroupsMembers.classFullName, criterion)?.toJSON(), `criterion ${i}`));\r\n });\r\n\r\n it(\"sees unsaved changes by default\", async () => {\r\n const cls = ElementGroupsMembers.classFullName;\r\n const pair = { sourceId: elementIds[4], targetId: elementIds[5] };\r\n await withEditTxn(imodel, async (txn) => {\r\n const id = txn.insertRelationship(ElementGroupsMembers.create(imodel, pair.sourceId, pair.targetId, 4).toJSON());\r\n const found = await imodel.relationships.queryInstances(cls, [id, pair]);\r\n expect(found.map((r) => r?.id)).to.deep.equal([id, id]);\r\n txn.deleteRelationship(imodel.relationships.getInstanceProps(cls, id));\r\n });\r\n });\r\n\r\n it(\"converts point, struct and array properties like tryGetInstanceProps\", async () => {\r\n const pair = { sourceId: elementIds[0], targetId: elementIds[5] };\r\n await withEditTxn(imodel, async (txn) => {\r\n const id = txn.insertRelationship({\r\n classFullName: testRelClass,\r\n ...pair,\r\n location: { x: 1, y: 2, z: 3 },\r\n info: { label: \"a\", origin: { x: 4, y: 5 } },\r\n points: [{ x: 6, y: 7, z: 8 }, { x: 9, y: 10, z: 11 }],\r\n } as RelationshipProps);\r\n\r\n const criteria = [id, pair];\r\n const batch = await imodel.relationships.queryInstancesProps(testRelClass, criteria);\r\n criteria.forEach((criterion, i) => expect(batch[i]).to.deep.equal(imodel.relationships.tryGetInstanceProps(testRelClass, criterion), `criterion ${i}`));\r\n expect(batch[0]).to.include({ id, classFullName: testRelClass });\r\n expect(batch[0]).to.deep.include({\r\n location: { x: 1, y: 2, z: 3 },\r\n info: { label: \"a\", origin: { x: 4, y: 5 } },\r\n points: [{ x: 6, y: 7, z: 8 }, { x: 9, y: 10, z: 11 }],\r\n });\r\n txn.deleteRelationship(batch[0]!);\r\n });\r\n });\r\n\r\n it(\"reads only the requested pairs in a dense many-to-many graph\", async () => {\r\n const cls = ElementGroupsMembers.classFullName;\r\n const n = 5;\r\n await withEditTxn(imodel, async (txn) => {\r\n const sources: Id64String[] = [];\r\n const targets: Id64String[] = [];\r\n for (let i = 0; i < 2 * n; ++i) {\r\n const id = txn.insertElement({ classFullName: RepositoryLink.classFullName, model: IModel.repositoryModelId, code: Code.createEmpty(), repositoryGuid: Guid.createValue() } as RepositoryLinkProps);\r\n (i < n ? sources : targets).push(id);\r\n }\r\n\r\n // Connect every source to every target, then request only the \"diagonal\" pairs.\r\n for (const sourceId of sources)\r\n for (const targetId of targets)\r\n txn.insertRelationship(ElementGroupsMembers.create(imodel, sourceId, targetId, 0).toJSON());\r\n\r\n const pairs = sources.map((sourceId, i) => ({ sourceId, targetId: targets[i] }));\r\n let rowsRead = 0;\r\n const createQueryReader = imodel.createQueryReader.bind(imodel);\r\n const stub = sinon.stub(imodel, \"createQueryReader\").callsFake((ecsql, params, config) => {\r\n const reader = createQueryReader(ecsql, params, config);\r\n const step = reader.step.bind(reader);\r\n reader.step = async () => {\r\n const hasRow = await step();\r\n if (hasRow)\r\n ++rowsRead;\r\n return hasRow;\r\n };\r\n return reader;\r\n });\r\n try {\r\n const found = await imodel.relationships.queryInstancesProps(cls, pairs);\r\n expect(found.map((p) => p && { sourceId: p.sourceId, targetId: p.targetId })).to.deep.equal(pairs);\r\n } finally {\r\n stub.restore();\r\n }\r\n expect(rowsRead).to.equal(n);\r\n });\r\n });\r\n\r\n it(\"supports relationships stored as navigation properties and ids from any briefcase\", async () => {\r\n const cls = \"BisCore:ModelContainsElements\";\r\n const highBriefcaseId = Id64.fromLocalAndBriefcaseIds(1, 0xfffffd);\r\n const criteria: Array<Id64String | SourceAndTarget> = [\r\n elementIds[0],\r\n { sourceId: IModel.repositoryModelId, targetId: elementIds[1] },\r\n highBriefcaseId,\r\n { sourceId: highBriefcaseId, targetId: elementIds[1] },\r\n ];\r\n const batch = await imodel.relationships.queryInstancesProps(cls, criteria);\r\n criteria.forEach((criterion, i) => expect(batch[i]).to.deep.equal(imodel.relationships.tryGetInstanceProps(cls, criterion), `criterion ${i}`));\r\n expect(batch.map((p) => p?.targetId)).to.deep.equal([elementIds[0], elementIds[1], undefined, undefined]);\r\n });\r\n});\r\n"]}
@@ -2,15 +2,19 @@
2
2
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
3
  * See LICENSE.md in the project root for license terms and full copyright notice.
4
4
  *--------------------------------------------------------------------------------------------*/
5
- import { assert } from "chai";
5
+ import * as chai from "chai";
6
+ import * as chaiAsPromised from "chai-as-promised";
6
7
  import { Guid } from "@itwin/core-bentley";
7
8
  import { BriefcaseIdValue } from "@itwin/core-common";
9
+ import { Range3d } from "@itwin/core-geometry";
8
10
  import { HubWrappers, IModelTestUtils } from "../IModelTestUtils";
9
11
  import { KnownTestLocations } from "../KnownTestLocations";
10
12
  import { HubMock } from "../../internal/HubMock";
11
13
  import { TestChangeSetUtility } from "../TestChangeSetUtility";
12
14
  import { _nativeDb, ChannelControl, ProgressStatus } from "../../core-backend";
13
15
  import { withEditTxn } from "../../EditTxn";
16
+ chai.use(chaiAsPromised);
17
+ const assert = chai.assert;
14
18
  describe("BriefcaseManager", async () => {
15
19
  const testITwinId = Guid.createValue();
16
20
  const managerAccessToken = "manager mock token";
@@ -234,5 +238,187 @@ describe("BriefcaseManager", async () => {
234
238
  await testUtility.deleteTestIModel();
235
239
  });
236
240
  });
241
+ describe("TxnManager.onPrePushValidation", () => {
242
+ const user2 = "user2 mock token";
243
+ const user3 = "user3 mock token";
244
+ /** Creates an empty iModel and opens a briefcase with one local, unpushed change to the root subject's user label. */
245
+ async function setupBriefcaseWithLocalChange(testName) {
246
+ HubMock.startup(testName, KnownTestLocations.outputDir);
247
+ const iTwinId = HubMock.iTwinId;
248
+ const iModelId = await HubWrappers.recreateIModel({ accessToken: user2, iTwinId, iModelName: testName, noLocks: true });
249
+ const briefcase = await HubWrappers.downloadAndOpenBriefcase({ accessToken: user2, iTwinId, iModelId });
250
+ briefcase.channels.addAllowedChannel(ChannelControl.sharedChannelName);
251
+ const rootEl = briefcase.elements.getRootSubject();
252
+ rootEl.userLabel = "local change";
253
+ withEditTxn(briefcase, (txn) => txn.updateElement(rootEl.toJSON()));
254
+ assert.isTrue(briefcase.txns.hasPendingTxns);
255
+ return { iTwinId, iModelId, briefcase };
256
+ }
257
+ it("delays the push until the listener's promise resolves", async () => {
258
+ const { briefcase } = await setupBriefcaseWithLocalChange("prePushDelay");
259
+ const changesetBeforePush = briefcase.changeset.id;
260
+ const events = [];
261
+ let changesetWhenListenerFinished;
262
+ let releaseListener;
263
+ const gate = new Promise((resolve) => { releaseListener = resolve; });
264
+ let listenerStarted;
265
+ const started = new Promise((resolve) => { listenerStarted = resolve; });
266
+ const removeListener = briefcase.txns.onPrePushValidation.addListener(async () => {
267
+ events.push("listener started");
268
+ listenerStarted();
269
+ await gate;
270
+ changesetWhenListenerFinished = briefcase.changeset.id;
271
+ events.push("listener finished");
272
+ });
273
+ try {
274
+ const pushPromise = briefcase.pushChanges({ accessToken: user2, description: "delayed push" }).then(() => { events.push("push settled"); });
275
+ await started;
276
+ events.push("listener released");
277
+ releaseListener();
278
+ await pushPromise;
279
+ assert.deepEqual(events, ["listener started", "listener released", "listener finished", "push settled"]);
280
+ assert.strictEqual(changesetWhenListenerFinished, changesetBeforePush, "nothing should be pushed before the listener resolves");
281
+ assert.notStrictEqual(briefcase.changeset.id, changesetBeforePush, "push should complete once the listener resolves");
282
+ assert.isFalse(briefcase.txns.hasPendingTxns);
283
+ }
284
+ finally {
285
+ releaseListener();
286
+ removeListener();
287
+ }
288
+ await HubWrappers.closeAndDeleteBriefcaseDb(user2, briefcase);
289
+ });
290
+ it("includes a Txn written by the listener in the pushed changeset", async () => {
291
+ const { iTwinId, iModelId, briefcase } = await setupBriefcaseWithLocalChange("prePushWriteTxn");
292
+ const changesetBeforePush = briefcase.changeset.id;
293
+ const txnCountBeforePush = Array.from(briefcase.txns.queryTxns()).length;
294
+ let txnCountInListener = 0;
295
+ const removeListener = briefcase.txns.onPrePushValidation.addListener(async () => {
296
+ const rootEl = briefcase.elements.getRootSubject();
297
+ rootEl.description = "written during pre-push validation";
298
+ withEditTxn(briefcase, (txn) => txn.updateElement(rootEl.toJSON()));
299
+ txnCountInListener = Array.from(briefcase.txns.queryTxns()).length;
300
+ });
301
+ try {
302
+ await briefcase.pushChanges({ accessToken: user2, description: "push with pre-push txn" });
303
+ }
304
+ finally {
305
+ removeListener();
306
+ }
307
+ assert.isAbove(txnCountInListener, txnCountBeforePush, "listener should have written a new Txn");
308
+ assert.notStrictEqual(briefcase.changeset.id, changesetBeforePush);
309
+ assert.isFalse(briefcase.txns.hasPendingTxns, "the listener's Txn should have been pushed, not left pending");
310
+ // A fresh briefcase at the tip only sees the listener's change if it was part of the pushed changeset.
311
+ const verifier = await HubWrappers.downloadAndOpenBriefcase({ accessToken: user3, iTwinId, iModelId });
312
+ const pushedRoot = verifier.elements.getRootSubject();
313
+ assert.strictEqual(pushedRoot.userLabel, "local change");
314
+ assert.strictEqual(pushedRoot.description, "written during pre-push validation");
315
+ await HubWrappers.closeAndDeleteBriefcaseDb(user3, verifier);
316
+ await HubWrappers.closeAndDeleteBriefcaseDb(user2, briefcase);
317
+ });
318
+ it("aborts the push when the listener rejects", async () => {
319
+ const { briefcase } = await setupBriefcaseWithLocalChange("prePushReject");
320
+ const changesetBeforePush = briefcase.changeset.id;
321
+ const removeListener = briefcase.txns.onPrePushValidation.addListener(async () => {
322
+ throw new Error("validation failed");
323
+ });
324
+ try {
325
+ await assert.isRejected(briefcase.pushChanges({ accessToken: user2, description: "rejected push" }), "validation failed");
326
+ }
327
+ finally {
328
+ removeListener();
329
+ }
330
+ assert.strictEqual(briefcase.changeset.id, changesetBeforePush, "briefcase should not have advanced");
331
+ assert.isTrue(briefcase.txns.hasPendingTxns, "local changes should still be pending");
332
+ // With the listener removed, the same changes can still be pushed.
333
+ await briefcase.pushChanges({ accessToken: user2, description: "retry push" });
334
+ assert.notStrictEqual(briefcase.changeset.id, changesetBeforePush);
335
+ assert.isFalse(briefcase.txns.hasPendingTxns);
336
+ await HubWrappers.closeAndDeleteBriefcaseDb(user2, briefcase);
337
+ });
338
+ it("handles a listener that calls pushChanges, recursing asynchronously", async () => {
339
+ const { briefcase } = await setupBriefcaseWithLocalChange("prePushRecursion");
340
+ const changesetBeforePush = briefcase.changeset.id;
341
+ let listenerCalls = 0;
342
+ let changesetAfterNestedPush;
343
+ // addOnce removes the listener before invoking it, so the nested push does not re-enter it endlessly.
344
+ const removeListener = briefcase.txns.onPrePushValidation.addOnce(async () => {
345
+ ++listenerCalls;
346
+ await briefcase.pushChanges({ accessToken: user2, description: "nested push" });
347
+ changesetAfterNestedPush = briefcase.changeset.id;
348
+ });
349
+ try {
350
+ // The nested push consumes all pending Txns, so the outer push has nothing left to push and completes quietly.
351
+ await briefcase.pushChanges({ accessToken: user2, description: "outer push" });
352
+ }
353
+ finally {
354
+ removeListener();
355
+ }
356
+ assert.strictEqual(listenerCalls, 1, "listener should only run for the outer push");
357
+ assert.isDefined(changesetAfterNestedPush);
358
+ assert.notStrictEqual(changesetAfterNestedPush, changesetBeforePush, "nested push should have pushed the changes");
359
+ assert.strictEqual(briefcase.changeset.id, changesetAfterNestedPush);
360
+ assert.isFalse(briefcase.txns.hasPendingTxns);
361
+ await HubWrappers.closeAndDeleteBriefcaseDb(user2, briefcase);
362
+ });
363
+ describe("after pull/merge", () => {
364
+ const incomingExtents = new Range3d(-100, -200, -300, 400, 500, 600);
365
+ // Note: only project extents are covered. The native getIModelProps("pullMerge") does not reload geolocation (ECEF location, global origin) after a merge.
366
+ /** user2 has a local unpushed change; user3 then pushes new project extents that user2 has not yet pulled. */
367
+ async function setup(testName) {
368
+ HubMock.startup(testName, KnownTestLocations.outputDir);
369
+ const iTwinId = HubMock.iTwinId;
370
+ const iModelId = await HubWrappers.recreateIModel({ accessToken: user2, iTwinId, iModelName: testName, noLocks: true });
371
+ const local = await HubWrappers.downloadAndOpenBriefcase({ accessToken: user2, iTwinId, iModelId });
372
+ local.channels.addAllowedChannel(ChannelControl.sharedChannelName);
373
+ const rootEl = local.elements.getRootSubject();
374
+ rootEl.userLabel = "local change";
375
+ withEditTxn(local, (txn) => txn.updateElement(rootEl.toJSON()));
376
+ const remote = await HubWrappers.downloadAndOpenBriefcase({ accessToken: user3, iTwinId, iModelId });
377
+ remote.channels.addAllowedChannel(ChannelControl.sharedChannelName);
378
+ withEditTxn(remote, (txn) => txn.updateProjectExtents(incomingExtents));
379
+ await remote.pushChanges({ accessToken: user3, description: "project extents" });
380
+ return { iTwinId, iModelId, local, remote };
381
+ }
382
+ it("exposes the merged project extents to listeners", async () => {
383
+ const { local, remote } = await setup("prePushMergedProps");
384
+ let seenExtents;
385
+ const removeListener = local.txns.onPrePushValidation.addListener(async () => {
386
+ seenExtents = local.projectExtents.clone();
387
+ });
388
+ try {
389
+ await local.pushChanges({ accessToken: user2, description: "push after merge" });
390
+ }
391
+ finally {
392
+ removeListener();
393
+ }
394
+ assert.isDefined(seenExtents);
395
+ assert.isTrue(seenExtents.isAlmostEqual(incomingExtents), "listener should see the merged project extents");
396
+ await HubWrappers.closeAndDeleteBriefcaseDb(user3, remote);
397
+ await HubWrappers.closeAndDeleteBriefcaseDb(user2, local);
398
+ });
399
+ it("lets a listener extend the merged project extents", async () => {
400
+ const { iTwinId, iModelId, local, remote } = await setup("prePushExtendExtents");
401
+ const extra = new Range3d(500, 600, 700, 800, 900, 1000);
402
+ const expected = incomingExtents.clone();
403
+ expected.extendRange(extra);
404
+ const removeListener = local.txns.onPrePushValidation.addListener(async () => {
405
+ const extended = local.projectExtents.clone();
406
+ extended.extendRange(extra);
407
+ withEditTxn(local, (txn) => txn.updateProjectExtents(extended));
408
+ });
409
+ try {
410
+ await local.pushChanges({ accessToken: user2, description: "push after merge" });
411
+ }
412
+ finally {
413
+ removeListener();
414
+ }
415
+ const verifier = await HubWrappers.downloadAndOpenBriefcase({ accessToken: user3, iTwinId, iModelId, briefcaseId: BriefcaseIdValue.Unassigned });
416
+ assert.isTrue(verifier.projectExtents.isAlmostEqual(expected), "pushed extents should extend the merged extents, not the stale pre-merge extents");
417
+ await HubWrappers.closeAndDeleteBriefcaseDb(user3, verifier);
418
+ await HubWrappers.closeAndDeleteBriefcaseDb(user3, remote);
419
+ await HubWrappers.closeAndDeleteBriefcaseDb(user2, local);
420
+ });
421
+ });
422
+ });
237
423
  });
238
424
  //# sourceMappingURL=BriefcaseManager.test.js.map