@itwin/core-backend 5.9.0-dev.8 → 5.9.0-dev.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/BackendHubAccess.d.ts +38 -0
- package/lib/cjs/BackendHubAccess.d.ts.map +1 -1
- package/lib/cjs/BackendHubAccess.js.map +1 -1
- package/lib/cjs/BackendLoggerCategory.js.map +1 -1
- package/lib/cjs/BisCoreSchema.js.map +1 -1
- package/lib/cjs/BlobContainerService.js.map +1 -1
- package/lib/cjs/BriefcaseManager.js.map +1 -1
- package/lib/cjs/CatalogDb.js.map +1 -1
- package/lib/cjs/Category.js.map +1 -1
- package/lib/cjs/ChangeSummaryManager.js +2 -2
- package/lib/cjs/ChangeSummaryManager.js.map +1 -1
- package/lib/cjs/ChangedElementsDb.js.map +1 -1
- package/lib/cjs/ChangesetECAdaptor.js +248 -248
- package/lib/cjs/ChangesetECAdaptor.js.map +1 -1
- package/lib/cjs/ChannelControl.js.map +1 -1
- package/lib/cjs/CheckpointManager.js.map +1 -1
- package/lib/cjs/ClassRegistry.js +5 -5
- package/lib/cjs/ClassRegistry.js.map +1 -1
- package/lib/cjs/CloudSqlite.js.map +1 -1
- package/lib/cjs/CodeService.js.map +1 -1
- package/lib/cjs/CodeSpecs.js.map +1 -1
- package/lib/cjs/ConcurrentQuery.js.map +1 -1
- package/lib/cjs/CustomViewState3dCreator.js.map +1 -1
- package/lib/cjs/DevTools.js.map +1 -1
- package/lib/cjs/DisplayStyle.js.map +1 -1
- package/lib/cjs/ECDb.js.map +1 -1
- package/lib/cjs/ECSchemaXmlContext.js.map +1 -1
- package/lib/cjs/ECSqlRowExecutor.js.map +1 -1
- package/lib/cjs/ECSqlStatement.js.map +1 -1
- package/lib/cjs/ECSqlSyncReader.js.map +1 -1
- package/lib/cjs/EditTxn.js.map +1 -1
- package/lib/cjs/Element.js.map +1 -1
- package/lib/cjs/ElementAspect.js.map +1 -1
- package/lib/cjs/ElementGraphics.js.map +1 -1
- package/lib/cjs/ElementTreeWalker.js.map +1 -1
- package/lib/cjs/Entity.js.map +1 -1
- package/lib/cjs/EntityReferences.js.map +1 -1
- package/lib/cjs/ExportGraphics.js.map +1 -1
- package/lib/cjs/ExternalSource.js.map +1 -1
- package/lib/cjs/FontFile.js.map +1 -1
- package/lib/cjs/GeoCoordConfig.js.map +1 -1
- package/lib/cjs/GeographicCRSServices.js.map +1 -1
- package/lib/cjs/GeometrySummary.js +47 -47
- package/lib/cjs/GeometrySummary.js.map +1 -1
- package/lib/cjs/IModelDb.js +9 -9
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/cjs/IModelDbFonts.js.map +1 -1
- package/lib/cjs/IModelElementCloneContext.js.map +1 -1
- package/lib/cjs/IModelHost.js.map +1 -1
- package/lib/cjs/IModelIncrementalSchemaLocater.js.map +1 -1
- package/lib/cjs/IModelJsFs.js.map +1 -1
- package/lib/cjs/ImageSourceConversion.js.map +1 -1
- package/lib/cjs/IpcHost.d.ts.map +1 -1
- package/lib/cjs/IpcHost.js +13 -4
- package/lib/cjs/IpcHost.js.map +1 -1
- package/lib/cjs/LineStyle.js.map +1 -1
- package/lib/cjs/LocalHub.js +1 -1
- package/lib/cjs/LocalHub.js.map +1 -1
- package/lib/cjs/LocalhostIpcHost.js.map +1 -1
- package/lib/cjs/LockControl.d.ts +85 -1
- package/lib/cjs/LockControl.d.ts.map +1 -1
- package/lib/cjs/LockControl.js.map +1 -1
- package/lib/cjs/Material.js.map +1 -1
- package/lib/cjs/Model.js.map +1 -1
- package/lib/cjs/NativeAppStorage.js.map +1 -1
- package/lib/cjs/NativeHost.js.map +1 -1
- package/lib/cjs/NavigationRelationship.js.map +1 -1
- package/lib/cjs/PromiseMemoizer.js.map +1 -1
- package/lib/cjs/PropertyStore.js.map +1 -1
- package/lib/cjs/Relationship.js.map +1 -1
- package/lib/cjs/RpcBackend.js.map +1 -1
- package/lib/cjs/SQLiteDb.js.map +1 -1
- package/lib/cjs/Schema.js.map +1 -1
- package/lib/cjs/SchemaSync.js.map +1 -1
- package/lib/cjs/SchemaUtils.js.map +1 -1
- package/lib/cjs/SheetIndex.js.map +1 -1
- package/lib/cjs/SqliteChangesetReader.js.map +1 -1
- package/lib/cjs/SqliteStatement.js.map +1 -1
- package/lib/cjs/StashManager.js.map +1 -1
- package/lib/cjs/Texture.js.map +1 -1
- package/lib/cjs/TileStorage.js.map +1 -1
- package/lib/cjs/TxnManager.d.ts +100 -4
- package/lib/cjs/TxnManager.d.ts.map +1 -1
- package/lib/cjs/TxnManager.js +171 -8
- package/lib/cjs/TxnManager.js.map +1 -1
- package/lib/cjs/ViewDefinition.js.map +1 -1
- package/lib/cjs/ViewStateHydrator.js.map +1 -1
- package/lib/cjs/ViewStore.js.map +1 -1
- package/lib/cjs/annotations/ElementDrivesTextAnnotation.js.map +1 -1
- package/lib/cjs/annotations/FrameGeometry.js.map +1 -1
- package/lib/cjs/annotations/LeaderGeometry.js.map +1 -1
- package/lib/cjs/annotations/TextAnnotationElement.js.map +1 -1
- package/lib/cjs/annotations/TextAnnotationGeometry.js.map +1 -1
- package/lib/cjs/annotations/TextBlockGeometry.js.map +1 -1
- package/lib/cjs/annotations/TextBlockLayout.js.map +1 -1
- package/lib/cjs/assets/IModelChange.02.00.00.ecschema.xml +90 -90
- package/lib/cjs/assets/Settings/Schemas/Base.Schema.json +32 -32
- package/lib/cjs/assets/Settings/Schemas/Gcs.schema.json +27 -27
- package/lib/cjs/assets/Settings/Schemas/Workspace.Schema.json +94 -94
- package/lib/cjs/assets/Settings/backend.setting.json5 +21 -21
- package/lib/cjs/core-backend.js.map +1 -1
- package/lib/cjs/domains/FunctionalElements.js.map +1 -1
- package/lib/cjs/domains/FunctionalSchema.js.map +1 -1
- package/lib/cjs/domains/GenericElements.js.map +1 -1
- package/lib/cjs/domains/GenericSchema.js.map +1 -1
- package/lib/cjs/internal/ChangesetConflictArgs.js.map +1 -1
- package/lib/cjs/internal/ChannelAdmin.js.map +1 -1
- package/lib/cjs/internal/ElementLRUCache.js.map +1 -1
- package/lib/cjs/internal/FontFileImpl.js.map +1 -1
- package/lib/cjs/internal/HubMock.d.ts +2 -0
- package/lib/cjs/internal/HubMock.d.ts.map +1 -1
- package/lib/cjs/internal/HubMock.js +7 -0
- package/lib/cjs/internal/HubMock.js.map +1 -1
- package/lib/cjs/internal/IModelDbFontsImpl.js.map +1 -1
- package/lib/cjs/internal/IntegrityCheck.js.map +1 -1
- package/lib/cjs/internal/NativePlatform.js.map +1 -1
- package/lib/cjs/internal/NoLocks.d.ts.map +1 -1
- package/lib/cjs/internal/NoLocks.js +6 -0
- package/lib/cjs/internal/NoLocks.js.map +1 -1
- package/lib/cjs/internal/OnlineStatus.js.map +1 -1
- package/lib/cjs/internal/ServerBasedLocks.d.ts +12 -0
- package/lib/cjs/internal/ServerBasedLocks.d.ts.map +1 -1
- package/lib/cjs/internal/ServerBasedLocks.js +285 -4
- package/lib/cjs/internal/ServerBasedLocks.js.map +1 -1
- package/lib/cjs/internal/Symbols.js.map +1 -1
- package/lib/cjs/internal/annotations/fields.js.map +1 -1
- package/lib/cjs/internal/cross-package.js.map +1 -1
- package/lib/cjs/internal/workspace/SettingsEditorImpl.js.map +1 -1
- package/lib/cjs/internal/workspace/SettingsImpl.js.map +1 -1
- package/lib/cjs/internal/workspace/SettingsSchemasImpl.js.map +1 -1
- package/lib/cjs/internal/workspace/WorkspaceImpl.js.map +1 -1
- package/lib/cjs/internal/workspace/WorkspaceSqliteDb.js.map +1 -1
- package/lib/cjs/rpc/multipart.js.map +1 -1
- package/lib/cjs/rpc/tracing.js.map +1 -1
- package/lib/cjs/rpc/web/logging.js.map +1 -1
- package/lib/cjs/rpc/web/request.js.map +1 -1
- package/lib/cjs/rpc/web/response.js.map +1 -1
- package/lib/cjs/rpc-impl/DevToolsRpcImpl.js.map +1 -1
- package/lib/cjs/rpc-impl/IModelReadRpcImpl.js.map +1 -1
- package/lib/cjs/rpc-impl/IModelTileRpcImpl.js.map +1 -1
- package/lib/cjs/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
- package/lib/cjs/rpc-impl/SnapshotIModelRpcImpl.js.map +1 -1
- package/lib/cjs/workspace/Settings.js.map +1 -1
- package/lib/cjs/workspace/SettingsDb.js.map +1 -1
- package/lib/cjs/workspace/SettingsEditor.js.map +1 -1
- package/lib/cjs/workspace/SettingsSchemas.js.map +1 -1
- package/lib/cjs/workspace/Workspace.js.map +1 -1
- package/lib/cjs/workspace/WorkspaceEditor.js.map +1 -1
- package/lib/esm/BackendHubAccess.d.ts +38 -0
- package/lib/esm/BackendHubAccess.d.ts.map +1 -1
- package/lib/esm/BackendHubAccess.js.map +1 -1
- package/lib/esm/BackendLoggerCategory.js.map +1 -1
- package/lib/esm/BisCoreSchema.js.map +1 -1
- package/lib/esm/BlobContainerService.js.map +1 -1
- package/lib/esm/BriefcaseManager.js.map +1 -1
- package/lib/esm/CatalogDb.js.map +1 -1
- package/lib/esm/Category.js.map +1 -1
- package/lib/esm/ChangeSummaryManager.js +2 -2
- package/lib/esm/ChangeSummaryManager.js.map +1 -1
- package/lib/esm/ChangedElementsDb.js.map +1 -1
- package/lib/esm/ChangesetECAdaptor.js +248 -248
- package/lib/esm/ChangesetECAdaptor.js.map +1 -1
- package/lib/esm/ChannelControl.js.map +1 -1
- package/lib/esm/CheckpointManager.js.map +1 -1
- package/lib/esm/ClassRegistry.js +5 -5
- package/lib/esm/ClassRegistry.js.map +1 -1
- package/lib/esm/CloudSqlite.js.map +1 -1
- package/lib/esm/CodeService.js.map +1 -1
- package/lib/esm/CodeSpecs.js.map +1 -1
- package/lib/esm/ConcurrentQuery.js.map +1 -1
- package/lib/esm/CustomViewState3dCreator.js.map +1 -1
- package/lib/esm/DevTools.js.map +1 -1
- package/lib/esm/DisplayStyle.js.map +1 -1
- package/lib/esm/ECDb.js.map +1 -1
- package/lib/esm/ECSchemaXmlContext.js.map +1 -1
- package/lib/esm/ECSqlRowExecutor.js.map +1 -1
- package/lib/esm/ECSqlStatement.js.map +1 -1
- package/lib/esm/ECSqlSyncReader.js.map +1 -1
- package/lib/esm/EditTxn.js.map +1 -1
- package/lib/esm/Element.js.map +1 -1
- package/lib/esm/ElementAspect.js.map +1 -1
- package/lib/esm/ElementGraphics.js.map +1 -1
- package/lib/esm/ElementTreeWalker.js.map +1 -1
- package/lib/esm/Entity.js.map +1 -1
- package/lib/esm/EntityReferences.js.map +1 -1
- package/lib/esm/ExportGraphics.js.map +1 -1
- package/lib/esm/ExternalSource.js.map +1 -1
- package/lib/esm/FontFile.js.map +1 -1
- package/lib/esm/GeoCoordConfig.js.map +1 -1
- package/lib/esm/GeographicCRSServices.js.map +1 -1
- package/lib/esm/GeometrySummary.js +47 -47
- package/lib/esm/GeometrySummary.js.map +1 -1
- package/lib/esm/IModelDb.js +9 -9
- package/lib/esm/IModelDb.js.map +1 -1
- package/lib/esm/IModelDbFonts.js.map +1 -1
- package/lib/esm/IModelElementCloneContext.js.map +1 -1
- package/lib/esm/IModelHost.js.map +1 -1
- package/lib/esm/IModelIncrementalSchemaLocater.js.map +1 -1
- package/lib/esm/IModelJsFs.js.map +1 -1
- package/lib/esm/ImageSourceConversion.js.map +1 -1
- package/lib/esm/IpcHost.d.ts.map +1 -1
- package/lib/esm/IpcHost.js +13 -4
- package/lib/esm/IpcHost.js.map +1 -1
- package/lib/esm/LineStyle.js.map +1 -1
- package/lib/esm/LocalHub.js +1 -1
- package/lib/esm/LocalHub.js.map +1 -1
- package/lib/esm/LocalhostIpcHost.js.map +1 -1
- package/lib/esm/LockControl.d.ts +85 -1
- package/lib/esm/LockControl.d.ts.map +1 -1
- package/lib/esm/LockControl.js.map +1 -1
- package/lib/esm/Material.js.map +1 -1
- package/lib/esm/Model.js.map +1 -1
- package/lib/esm/NativeAppStorage.js.map +1 -1
- package/lib/esm/NativeHost.js.map +1 -1
- package/lib/esm/NavigationRelationship.js.map +1 -1
- package/lib/esm/PromiseMemoizer.js.map +1 -1
- package/lib/esm/PropertyStore.js.map +1 -1
- package/lib/esm/Relationship.js.map +1 -1
- package/lib/esm/RpcBackend.js.map +1 -1
- package/lib/esm/SQLiteDb.js.map +1 -1
- package/lib/esm/Schema.js.map +1 -1
- package/lib/esm/SchemaSync.js.map +1 -1
- package/lib/esm/SchemaUtils.js.map +1 -1
- package/lib/esm/SheetIndex.js.map +1 -1
- package/lib/esm/SqliteChangesetReader.js.map +1 -1
- package/lib/esm/SqliteStatement.js.map +1 -1
- package/lib/esm/StashManager.js.map +1 -1
- package/lib/esm/Texture.js.map +1 -1
- package/lib/esm/TileStorage.js.map +1 -1
- package/lib/esm/TxnManager.d.ts +100 -4
- package/lib/esm/TxnManager.d.ts.map +1 -1
- package/lib/esm/TxnManager.js +171 -8
- package/lib/esm/TxnManager.js.map +1 -1
- package/lib/esm/ViewDefinition.js.map +1 -1
- package/lib/esm/ViewStateHydrator.js.map +1 -1
- package/lib/esm/ViewStore.js.map +1 -1
- package/lib/esm/annotations/ElementDrivesTextAnnotation.js.map +1 -1
- package/lib/esm/annotations/FrameGeometry.js.map +1 -1
- package/lib/esm/annotations/LeaderGeometry.js.map +1 -1
- package/lib/esm/annotations/TextAnnotationElement.js.map +1 -1
- package/lib/esm/annotations/TextAnnotationGeometry.js.map +1 -1
- package/lib/esm/annotations/TextBlockGeometry.js.map +1 -1
- package/lib/esm/annotations/TextBlockLayout.js.map +1 -1
- package/lib/esm/core-backend.js.map +1 -1
- package/lib/esm/domains/FunctionalElements.js.map +1 -1
- package/lib/esm/domains/FunctionalSchema.js.map +1 -1
- package/lib/esm/domains/GenericElements.js.map +1 -1
- package/lib/esm/domains/GenericSchema.js.map +1 -1
- package/lib/esm/internal/ChangesetConflictArgs.js.map +1 -1
- package/lib/esm/internal/ChannelAdmin.js.map +1 -1
- package/lib/esm/internal/ElementLRUCache.js.map +1 -1
- package/lib/esm/internal/FontFileImpl.js.map +1 -1
- package/lib/esm/internal/HubMock.d.ts +2 -0
- package/lib/esm/internal/HubMock.d.ts.map +1 -1
- package/lib/esm/internal/HubMock.js +7 -0
- package/lib/esm/internal/HubMock.js.map +1 -1
- package/lib/esm/internal/IModelDbFontsImpl.js.map +1 -1
- package/lib/esm/internal/IntegrityCheck.js.map +1 -1
- package/lib/esm/internal/NativePlatform.js.map +1 -1
- package/lib/esm/internal/NoLocks.d.ts.map +1 -1
- package/lib/esm/internal/NoLocks.js +6 -0
- package/lib/esm/internal/NoLocks.js.map +1 -1
- package/lib/esm/internal/OnlineStatus.js.map +1 -1
- package/lib/esm/internal/ServerBasedLocks.d.ts +12 -0
- package/lib/esm/internal/ServerBasedLocks.d.ts.map +1 -1
- package/lib/esm/internal/ServerBasedLocks.js +286 -5
- package/lib/esm/internal/ServerBasedLocks.js.map +1 -1
- package/lib/esm/internal/Symbols.js.map +1 -1
- package/lib/esm/internal/annotations/fields.js.map +1 -1
- package/lib/esm/internal/cross-package.js.map +1 -1
- package/lib/esm/internal/workspace/SettingsEditorImpl.js.map +1 -1
- package/lib/esm/internal/workspace/SettingsImpl.js.map +1 -1
- package/lib/esm/internal/workspace/SettingsSchemasImpl.js.map +1 -1
- package/lib/esm/internal/workspace/WorkspaceImpl.js.map +1 -1
- package/lib/esm/internal/workspace/WorkspaceSqliteDb.js.map +1 -1
- package/lib/esm/rpc/multipart.js.map +1 -1
- package/lib/esm/rpc/tracing.js.map +1 -1
- package/lib/esm/rpc/web/logging.js.map +1 -1
- package/lib/esm/rpc/web/request.js.map +1 -1
- package/lib/esm/rpc/web/response.js.map +1 -1
- package/lib/esm/rpc-impl/DevToolsRpcImpl.js.map +1 -1
- package/lib/esm/rpc-impl/IModelReadRpcImpl.js.map +1 -1
- package/lib/esm/rpc-impl/IModelTileRpcImpl.js.map +1 -1
- package/lib/esm/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
- package/lib/esm/rpc-impl/SnapshotIModelRpcImpl.js.map +1 -1
- package/lib/esm/test/AdvancedEqual.js.map +1 -1
- package/lib/esm/test/AnnotationTestUtils.js.map +1 -1
- package/lib/esm/test/AttachDb.test.js +11 -11
- package/lib/esm/test/AttachDb.test.js.map +1 -1
- package/lib/esm/test/ElementDrivesElement.test.js +23 -23
- package/lib/esm/test/ElementDrivesElement.test.js.map +1 -1
- package/lib/esm/test/ElementLRUCache.test.js.map +1 -1
- package/lib/esm/test/GeometryTestUtil.js.map +1 -1
- package/lib/esm/test/IModelHost.test.js.map +1 -1
- package/lib/esm/test/IModelTestUtils.js.map +1 -1
- package/lib/esm/test/ImageSourceConversion.test.js.map +1 -1
- package/lib/esm/test/IpcHost.test.js.map +1 -1
- package/lib/esm/test/KnownTestLocations.js.map +1 -1
- package/lib/esm/test/PrintElementTree.js.map +1 -1
- package/lib/esm/test/PropertyDb.test.js.map +1 -1
- package/lib/esm/test/RevisionUtility.js.map +1 -1
- package/lib/esm/test/SchemaUtils.test.js +25 -25
- package/lib/esm/test/SchemaUtils.test.js.map +1 -1
- package/lib/esm/test/SequentialLogMatcher.js.map +1 -1
- package/lib/esm/test/SquashSchemaAndDataChanges.test.js +129 -129
- package/lib/esm/test/SquashSchemaAndDataChanges.test.js.map +1 -1
- package/lib/esm/test/TestChangeSetUtility.js.map +1 -1
- package/lib/esm/test/TestEditTxn.js.map +1 -1
- package/lib/esm/test/TestUtils.js.map +1 -1
- package/lib/esm/test/annotations/Fields.test.js +53 -53
- package/lib/esm/test/annotations/Fields.test.js.map +1 -1
- package/lib/esm/test/annotations/FrameGeometry.test.js.map +1 -1
- package/lib/esm/test/annotations/LeaderGeometry.test.js.map +1 -1
- package/lib/esm/test/annotations/TextAnnotation.test.js.map +1 -1
- package/lib/esm/test/annotations/TextBlock.test.js.map +1 -1
- package/lib/esm/test/assets/IncrementalSchemaLocater/configs/old.config.js.map +1 -1
- package/lib/esm/test/assets/IncrementalSchemaLocater/configs/simple.config.js.map +1 -1
- package/lib/esm/test/categories/Category.test.js.map +1 -1
- package/lib/esm/test/codespec/CodeSpec.test.js.map +1 -1
- package/lib/esm/test/ecdb/CTE.test.js +88 -88
- package/lib/esm/test/ecdb/CTE.test.js.map +1 -1
- package/lib/esm/test/ecdb/ConcurrentQuery.test.js +19 -19
- package/lib/esm/test/ecdb/ConcurrentQuery.test.js.map +1 -1
- package/lib/esm/test/ecdb/ConcurrentQueryLoad.test.js +15 -15
- package/lib/esm/test/ecdb/ConcurrentQueryLoad.test.js.map +1 -1
- package/lib/esm/test/ecdb/ECDb.test.js +72 -72
- package/lib/esm/test/ecdb/ECDb.test.js.map +1 -1
- package/lib/esm/test/ecdb/ECDbTestHelper.js.map +1 -1
- package/lib/esm/test/ecdb/ECSchemaXmlContext.test.js.map +1 -1
- package/lib/esm/test/ecdb/ECSqlAst.test.js +65 -65
- package/lib/esm/test/ecdb/ECSqlAst.test.js.map +1 -1
- package/lib/esm/test/ecdb/ECSqlQuery.test.js +4 -4
- package/lib/esm/test/ecdb/ECSqlQuery.test.js.map +1 -1
- package/lib/esm/test/ecdb/ECSqlStatement.test.js +332 -332
- package/lib/esm/test/ecdb/ECSqlStatement.test.js.map +1 -1
- package/lib/esm/test/ecdb/ECSqlSyncReader.test.js.map +1 -1
- package/lib/esm/test/ecdb/QueryReaders.test.js +31 -31
- package/lib/esm/test/ecdb/QueryReaders.test.js.map +1 -1
- package/lib/esm/test/ecdb/SqliteStatement.test.js.map +1 -1
- package/lib/esm/test/ecsql/dataset/ECSqlDatasets.js.map +1 -1
- package/lib/esm/test/ecsql/src/ECSqlTestGenerator.js +21 -21
- package/lib/esm/test/ecsql/src/ECSqlTestGenerator.js.map +1 -1
- package/lib/esm/test/ecsql/src/ECSqlTestParser.js.map +1 -1
- package/lib/esm/test/ecsql/src/ECSqlTestRunner.test.js.map +1 -1
- package/lib/esm/test/element/DeleteDefinitionElements.test.js.map +1 -1
- package/lib/esm/test/element/ElementAspect.test.js +22 -22
- package/lib/esm/test/element/ElementAspect.test.js.map +1 -1
- package/lib/esm/test/element/ElementDependencyGraph.test.js.map +1 -1
- package/lib/esm/test/element/ElementRoundTrip.test.js +139 -139
- package/lib/esm/test/element/ElementRoundTrip.test.js.map +1 -1
- package/lib/esm/test/element/ExcludedElements.test.js.map +1 -1
- package/lib/esm/test/element/ExternalSource.test.js.map +1 -1
- package/lib/esm/test/element/NullStructArray.test.js +13 -13
- package/lib/esm/test/element/NullStructArray.test.js.map +1 -1
- package/lib/esm/test/element/ProjectInformationRecord.test.js.map +1 -1
- package/lib/esm/test/element/SheetInformationAspect.test.js.map +1 -1
- package/lib/esm/test/element/UrlLink.test.js.map +1 -1
- package/lib/esm/test/font/FontFile.test.js.map +1 -1
- package/lib/esm/test/font/IModelDbFonts.test.js.map +1 -1
- package/lib/esm/test/hubaccess/ApplyChangeset.test.js +32 -32
- package/lib/esm/test/hubaccess/ApplyChangeset.test.js.map +1 -1
- package/lib/esm/test/hubaccess/BriefcaseManager.test.js.map +1 -1
- package/lib/esm/test/hubaccess/CheckpointManager.test.js.map +1 -1
- package/lib/esm/test/hubaccess/Rebase.test.js +56 -56
- package/lib/esm/test/hubaccess/Rebase.test.js.map +1 -1
- package/lib/esm/test/hubaccess/SemanticRebase.test.js +145 -145
- package/lib/esm/test/hubaccess/SemanticRebase.test.js.map +1 -1
- package/lib/esm/test/imageData.js.map +1 -1
- package/lib/esm/test/imodel/Code.test.js.map +1 -1
- package/lib/esm/test/imodel/ElementTreeWalker.test.js.map +1 -1
- package/lib/esm/test/imodel/GetTextureImage.test.js.map +1 -1
- package/lib/esm/test/imodel/IModel.test.js +44 -44
- package/lib/esm/test/imodel/IModel.test.js.map +1 -1
- package/lib/esm/test/imodel/ProjectExtents.test.js.map +1 -1
- package/lib/esm/test/imodel/SchemaXmlImport.test.js +13 -13
- package/lib/esm/test/imodel/SchemaXmlImport.test.js.map +1 -1
- package/lib/esm/test/incrementalSchemaLocater/ECSqlQueries.test.js.map +1 -1
- package/lib/esm/test/incrementalSchemaLocater/IncrementalLoading.test.js.map +1 -1
- package/lib/esm/test/incrementalSchemaLocater/TestContext.js.map +1 -1
- package/lib/esm/test/index.js.map +1 -1
- package/lib/esm/test/misc/DevTools.test.js.map +1 -1
- package/lib/esm/test/misc/EntitySubClasses.test.js.map +1 -1
- package/lib/esm/test/misc/GeoServices.test.js.map +1 -1
- package/lib/esm/test/misc/PromiseMemoizer.test.js.map +1 -1
- package/lib/esm/test/native/DgnDbWorker.test.js.map +1 -1
- package/lib/esm/test/rpc/response.test.js.map +1 -1
- package/lib/esm/test/schema/ClassRegistry.test.js +99 -99
- package/lib/esm/test/schema/ClassRegistry.test.js.map +1 -1
- package/lib/esm/test/schema/FunctionalDomain.test.js.map +1 -1
- package/lib/esm/test/schema/GenericDomain.test.js.map +1 -1
- package/lib/esm/test/schema/IModelSchemaContext.test.js +9 -9
- package/lib/esm/test/schema/IModelSchemaContext.test.js.map +1 -1
- package/lib/esm/test/schema/SchemaImportCallbacks.test.js +19 -19
- package/lib/esm/test/schema/SchemaImportCallbacks.test.js.map +1 -1
- package/lib/esm/test/sheetindex/SheetIndex.test.js.map +1 -1
- package/lib/esm/test/standalone/ChangeMerge.test.js.map +1 -1
- package/lib/esm/test/standalone/ChangesetReader.test.js +135 -135
- package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
- package/lib/esm/test/standalone/CustomViewState3dCreator.test.js.map +1 -1
- package/lib/esm/test/standalone/DisplayStyle.test.js.map +1 -1
- package/lib/esm/test/standalone/Drawing.test.js.map +1 -1
- package/lib/esm/test/standalone/EditTxn.test.js.map +1 -1
- package/lib/esm/test/standalone/ElementGraphics.test.js.map +1 -1
- package/lib/esm/test/standalone/ElementMesh.test.js.map +1 -1
- package/lib/esm/test/standalone/ExportGraphics.test.js +14 -14
- package/lib/esm/test/standalone/ExportGraphics.test.js.map +1 -1
- package/lib/esm/test/standalone/GeometryChangeEvents.test.js.map +1 -1
- package/lib/esm/test/standalone/GeometryStream.test.js.map +1 -1
- package/lib/esm/test/standalone/HubMock.test.js.map +1 -1
- package/lib/esm/test/standalone/IModelLimits.test.js.map +1 -1
- package/lib/esm/test/standalone/IModelWrite.test.js +27 -27
- package/lib/esm/test/standalone/IModelWrite.test.js.map +1 -1
- package/lib/esm/test/standalone/ITwinWorkspace.test.js.map +1 -1
- package/lib/esm/test/standalone/InlineGeometryPartReferences.test.js.map +1 -1
- package/lib/esm/test/standalone/IntegrityCheck.test.js.map +1 -1
- package/lib/esm/test/standalone/MergeConflict.test.js.map +1 -1
- package/lib/esm/test/standalone/NativeAppStorage.test.js.map +1 -1
- package/lib/esm/test/standalone/RenderMaterialElement.test.js.map +1 -1
- package/lib/esm/test/standalone/RenderTimeline.test.js.map +1 -1
- package/lib/esm/test/standalone/SQLiteDb.test.js.map +1 -1
- package/lib/esm/test/standalone/SchemaUtils.test.js.map +1 -1
- package/lib/esm/test/standalone/SectionDrawing.test.js.map +1 -1
- package/lib/esm/test/standalone/ServerBasedLocks.test.js +907 -3
- package/lib/esm/test/standalone/ServerBasedLocks.test.js.map +1 -1
- package/lib/esm/test/standalone/Setting.test.js.map +1 -1
- package/lib/esm/test/standalone/Settings.test.js.map +1 -1
- package/lib/esm/test/standalone/SettingsSchemas.test.js.map +1 -1
- package/lib/esm/test/standalone/SnapshotDb.test.js.map +1 -1
- package/lib/esm/test/standalone/StandaloneDb.test.js +20 -20
- package/lib/esm/test/standalone/StandaloneDb.test.js.map +1 -1
- package/lib/esm/test/standalone/Texture.test.js.map +1 -1
- package/lib/esm/test/standalone/TileCache.test.js.map +1 -1
- package/lib/esm/test/standalone/TileTree.test.js.map +1 -1
- package/lib/esm/test/standalone/TxnManager.test.js.map +1 -1
- package/lib/esm/test/standalone/ViewDefinition.test.js.map +1 -1
- package/lib/esm/test/standalone/ViewStoreDb.test.js.map +1 -1
- package/lib/esm/test/standalone/Workspace.test.js.map +1 -1
- package/lib/esm/test/standalone/iModelDb.test.js.map +1 -1
- package/lib/esm/test/workspace/SettingsDb.test.js.map +1 -1
- package/lib/esm/workspace/Settings.js.map +1 -1
- package/lib/esm/workspace/SettingsDb.js.map +1 -1
- package/lib/esm/workspace/SettingsEditor.js.map +1 -1
- package/lib/esm/workspace/SettingsSchemas.js.map +1 -1
- package/lib/esm/workspace/Workspace.js.map +1 -1
- package/lib/esm/workspace/WorkspaceEditor.js.map +1 -1
- package/package.json +12 -12
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
import * as chai from "chai";
|
|
6
6
|
import * as chaiAsPromised from "chai-as-promised";
|
|
7
|
-
import { restore as sinonRestore, spy as sinonSpy } from "sinon";
|
|
8
|
-
import { Guid, Id64 } from "@itwin/core-bentley";
|
|
7
|
+
import { match as sinonMatch, restore as sinonRestore, spy as sinonSpy, stub as sinonStub } from "sinon";
|
|
8
|
+
import { Guid, Id64, IModelStatus } from "@itwin/core-bentley";
|
|
9
9
|
import { Code, IModel, IModelError, LockState } from "@itwin/core-common";
|
|
10
10
|
import { BriefcaseManager } from "../../BriefcaseManager";
|
|
11
11
|
import { PhysicalObject } from "../../domains/GenericElements";
|
|
@@ -15,7 +15,7 @@ import { ElementOwnsChildElements } from "../../NavigationRelationship";
|
|
|
15
15
|
import { HubMock } from "../../internal/HubMock";
|
|
16
16
|
import { ExtensiveTestScenario, IModelTestUtils } from "../IModelTestUtils";
|
|
17
17
|
import { KnownTestLocations } from "../KnownTestLocations";
|
|
18
|
-
import { ChannelControl } from "../../core-backend";
|
|
18
|
+
import { ChannelControl, LockConflict } from "../../core-backend";
|
|
19
19
|
import { _hubAccess, _releaseAllLocks } from "../../internal/Symbols";
|
|
20
20
|
import { EditTxn, withEditTxn } from "../../EditTxn";
|
|
21
21
|
const expect = chai.expect;
|
|
@@ -277,5 +277,909 @@ describe("Server-based locks", () => {
|
|
|
277
277
|
expectUnlocked();
|
|
278
278
|
});
|
|
279
279
|
});
|
|
280
|
+
describe("abandonLocksForReversedTxn", () => {
|
|
281
|
+
let bc;
|
|
282
|
+
let bc2;
|
|
283
|
+
let locks;
|
|
284
|
+
beforeEach(async () => {
|
|
285
|
+
bc = await BriefcaseDb.open({ fileName: briefcase1Props.fileName });
|
|
286
|
+
expect(bc.locks.isServerBased).to.be.true;
|
|
287
|
+
locks = bc.locks;
|
|
288
|
+
bc.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
289
|
+
});
|
|
290
|
+
afterEach(async () => {
|
|
291
|
+
sinonRestore();
|
|
292
|
+
await locks[_releaseAllLocks]();
|
|
293
|
+
bc.close();
|
|
294
|
+
if (bc2 !== undefined) {
|
|
295
|
+
await bc2.locks.releaseAllLocks();
|
|
296
|
+
bc2.close();
|
|
297
|
+
bc2 = undefined;
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
it("abandons locks acquired in the current, unsaved txn", async () => {
|
|
301
|
+
const lockSpy = sinonSpy(IModelHost[_hubAccess], "abandonLocks");
|
|
302
|
+
const childId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
303
|
+
const txnId = bc.txns.getCurrentTxnId();
|
|
304
|
+
await locks.acquireLocks({ exclusive: childId });
|
|
305
|
+
expect(locks.holdsExclusiveLock(childId)).to.be.true;
|
|
306
|
+
expect(locks.getLockCount(LockState.Exclusive)).to.equal(1);
|
|
307
|
+
expect(locks.getLockCount(LockState.Shared)).to.be.greaterThan(0);
|
|
308
|
+
expect(await locks.abandonLocksForReversedTxn(txnId)).to.be.true;
|
|
309
|
+
expect(lockSpy.callCount).to.equal(1);
|
|
310
|
+
const releasedLocks = lockSpy.getCall(0).args[1];
|
|
311
|
+
expect(releasedLocks.size).to.be.greaterThan(0);
|
|
312
|
+
for (const state of releasedLocks.values())
|
|
313
|
+
expect(state).to.equal(LockState.None);
|
|
314
|
+
expect(locks.holdsExclusiveLock(childId)).to.be.false;
|
|
315
|
+
expect(locks.getLockCount(LockState.Exclusive)).to.equal(0);
|
|
316
|
+
expect(locks.getLockCount(LockState.Shared)).to.equal(0);
|
|
317
|
+
});
|
|
318
|
+
it("abandons locks acquired in the most recent saved txn", async () => {
|
|
319
|
+
const lockSpy = sinonSpy(IModelHost[_hubAccess], "abandonLocks");
|
|
320
|
+
const childId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
321
|
+
const txnId = bc.txns.getCurrentTxnId();
|
|
322
|
+
await withEditTxn(bc, async (txn) => {
|
|
323
|
+
await locks.acquireLocks({ exclusive: childId });
|
|
324
|
+
const element = bc.elements.getElement(childId);
|
|
325
|
+
element.setUserProperties("foo", Guid.createValue());
|
|
326
|
+
element.update(txn);
|
|
327
|
+
});
|
|
328
|
+
expect(locks.holdsExclusiveLock(childId)).to.be.true;
|
|
329
|
+
expect(locks.getLockCount(LockState.Exclusive)).to.equal(1);
|
|
330
|
+
expect(locks.getLockCount(LockState.Shared)).to.be.greaterThan(0);
|
|
331
|
+
bc.txns.reverseSingleTxn();
|
|
332
|
+
expect(await locks.abandonLocksForReversedTxn(txnId)).to.be.true;
|
|
333
|
+
expect(lockSpy.callCount).to.equal(1);
|
|
334
|
+
const releasedLocks = lockSpy.getCall(0).args[1];
|
|
335
|
+
expect(releasedLocks.size).to.be.greaterThan(0);
|
|
336
|
+
for (const state of releasedLocks.values())
|
|
337
|
+
expect(state).to.equal(LockState.None);
|
|
338
|
+
expect(locks.holdsExclusiveLock(childId)).to.be.false;
|
|
339
|
+
expect(locks.getLockCount(LockState.Exclusive)).to.equal(0);
|
|
340
|
+
expect(locks.getLockCount(LockState.Shared)).to.equal(0);
|
|
341
|
+
});
|
|
342
|
+
it("does not release locks acquired by a different txn", async () => {
|
|
343
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
344
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
345
|
+
const txn1 = bc.txns.getCurrentTxnId();
|
|
346
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
347
|
+
await withEditTxn(bc, async (txn) => {
|
|
348
|
+
const element = bc.elements.getElement(elementId1);
|
|
349
|
+
element.setUserProperties("foo", Guid.createValue());
|
|
350
|
+
element.update(txn);
|
|
351
|
+
});
|
|
352
|
+
const txn2 = bc.txns.getCurrentTxnId();
|
|
353
|
+
expect(txn2).not.to.equal(txn1);
|
|
354
|
+
await withEditTxn(bc, async (txn) => {
|
|
355
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
356
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
357
|
+
element2.setUserProperties("bar", Guid.createValue());
|
|
358
|
+
element2.update(txn);
|
|
359
|
+
});
|
|
360
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
361
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
362
|
+
bc.txns.reverseTxns(1);
|
|
363
|
+
expect(await locks.abandonLocksForReversedTxn(txn2)).to.be.true;
|
|
364
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
365
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
366
|
+
});
|
|
367
|
+
it("invalidates discovered locks", async () => {
|
|
368
|
+
const elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
369
|
+
const ownerModeltId = bc.elements.getElementProps(elementId).model;
|
|
370
|
+
const ownersOwnerModelId = bc.elements.getElementProps(ownerModeltId).model;
|
|
371
|
+
await locks.acquireLocks({ exclusive: ownersOwnerModelId });
|
|
372
|
+
expect(locks.holdsExclusiveLock(ownerModeltId)).to.be.true;
|
|
373
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.true;
|
|
374
|
+
const txnId = bc.txns.getCurrentTxnId();
|
|
375
|
+
bc.txns.reverseTxns(1);
|
|
376
|
+
expect(await locks.abandonLocksForReversedTxn(txnId)).to.be.true;
|
|
377
|
+
expect(locks.holdsExclusiveLock(ownerModeltId)).to.be.false;
|
|
378
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
379
|
+
});
|
|
380
|
+
it("restores lock to its previous state if it was upgraded by the reversed txn", async () => {
|
|
381
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
382
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
383
|
+
const firstTxnId = bc.txns.getCurrentTxnId();
|
|
384
|
+
await locks.acquireLocks({ exclusive: elementId1, shared: elementId2 });
|
|
385
|
+
expect(locks.holdsSharedLock(elementId2)).to.be.true;
|
|
386
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
387
|
+
// We must actually edit something in order to start a new Txn.
|
|
388
|
+
await withEditTxn(bc, async (txn) => {
|
|
389
|
+
const element = bc.elements.getElement(elementId1);
|
|
390
|
+
element.setUserProperties("foo", { test: true });
|
|
391
|
+
element.update(txn);
|
|
392
|
+
});
|
|
393
|
+
const secondTxnId = bc.txns.getCurrentTxnId();
|
|
394
|
+
expect(firstTxnId).not.to.equal(secondTxnId);
|
|
395
|
+
await locks.acquireLocks({ exclusive: elementId2 }); // upgrade lock from shared to exclusive
|
|
396
|
+
expect(locks.holdsSharedLock(elementId2)).to.be.true;
|
|
397
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
398
|
+
expect(await locks.abandonLocksForReversedTxn(secondTxnId)).to.be.true;
|
|
399
|
+
expect(locks.holdsSharedLock(elementId2)).to.be.true;
|
|
400
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
401
|
+
});
|
|
402
|
+
it("does not update changesetid when releasing locks", async () => {
|
|
403
|
+
bc2 = await BriefcaseDb.open({ fileName: briefcase2Props.fileName });
|
|
404
|
+
expect(bc2.locks.isServerBased).to.be.true;
|
|
405
|
+
bc2.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
406
|
+
// Make sure both briefcase initially have all changes.
|
|
407
|
+
await bc.pullChanges({ accessToken: "token" });
|
|
408
|
+
await bc2.pullChanges({ accessToken: "token" });
|
|
409
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
410
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
411
|
+
// Edit an element in the first briefcase and push the change. This will
|
|
412
|
+
// create a new changeset.
|
|
413
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
414
|
+
await withEditTxn(bc, async (txn) => {
|
|
415
|
+
const element = bc.elements.getElement(elementId1);
|
|
416
|
+
element.setUserProperties("foo", { test: true });
|
|
417
|
+
element.update(txn);
|
|
418
|
+
});
|
|
419
|
+
await bc.pushChanges({ accessToken: "token", description: "changes" });
|
|
420
|
+
const secondTxnId = bc.txns.getCurrentTxnId();
|
|
421
|
+
// In that same briefcase, lock and edit a different element, but then reverse
|
|
422
|
+
// the change and release the lock.
|
|
423
|
+
await bc.locks.acquireLocks({ exclusive: elementId2 });
|
|
424
|
+
await withEditTxn(bc, async (txn) => {
|
|
425
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
426
|
+
element2.setUserProperties("bar", { test: true });
|
|
427
|
+
element2.update(txn);
|
|
428
|
+
});
|
|
429
|
+
bc.txns.reverseTxns(1);
|
|
430
|
+
expect(await locks.abandonLocksForReversedTxn(secondTxnId)).to.be.true;
|
|
431
|
+
// Now, in a separate briefcase, which has not yet pulled the changes pushed by the first,
|
|
432
|
+
// attempt to lock the same element whose lock was just released. This should work because
|
|
433
|
+
// the lock release by releaseLocksForReversedTxn should not have updated the changeset
|
|
434
|
+
// associated with that lock.
|
|
435
|
+
await bc2.locks.acquireLocks({ exclusive: elementId2 });
|
|
436
|
+
});
|
|
437
|
+
it("does not release on the server an implicit lock held for a new element", async () => {
|
|
438
|
+
const childId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
439
|
+
const childElement = bc.elements.getElement(childId);
|
|
440
|
+
const parentId = childElement.parent.id;
|
|
441
|
+
const modelId = childElement.model;
|
|
442
|
+
const physicalProps = {
|
|
443
|
+
classFullName: PhysicalObject.classFullName,
|
|
444
|
+
model: modelId,
|
|
445
|
+
parent: new ElementOwnsChildElements(parentId),
|
|
446
|
+
category: childElement.category,
|
|
447
|
+
code: Code.createEmpty(),
|
|
448
|
+
};
|
|
449
|
+
const txnId = bc.txns.getCurrentTxnId();
|
|
450
|
+
await locks.acquireLocks({ shared: [modelId, parentId] });
|
|
451
|
+
const newElementId = await withEditTxn(bc, async (txn) => {
|
|
452
|
+
return txn.insertElement(physicalProps);
|
|
453
|
+
});
|
|
454
|
+
expect(locks.holdsExclusiveLock(newElementId)).to.be.true;
|
|
455
|
+
const lockSpy = sinonSpy(IModelHost[_hubAccess], "acquireLocks");
|
|
456
|
+
bc.txns.reverseTxns(1);
|
|
457
|
+
expect(await locks.abandonLocksForReversedTxn(txnId)).to.be.true;
|
|
458
|
+
expect(lockSpy.calledWithMatch(sinonMatch.any, sinonMatch((lockMap) => lockMap.has(newElementId)))).to.be.false;
|
|
459
|
+
});
|
|
460
|
+
it("releases locks for later txns, too", async () => {
|
|
461
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
462
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
463
|
+
const txn1 = bc.txns.getCurrentTxnId();
|
|
464
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
465
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
466
|
+
await withEditTxn(bc, async (txn) => {
|
|
467
|
+
element1.setUserProperties("foo", Guid.createValue());
|
|
468
|
+
element1.update(txn);
|
|
469
|
+
});
|
|
470
|
+
const txn2 = bc.txns.getCurrentTxnId();
|
|
471
|
+
expect(txn2).not.to.equal(txn1);
|
|
472
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
473
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
474
|
+
await withEditTxn(bc, async (txn) => {
|
|
475
|
+
element2.setUserProperties("bar", Guid.createValue());
|
|
476
|
+
element2.update(txn);
|
|
477
|
+
});
|
|
478
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
479
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
480
|
+
// Reverse both txns, then abandon locks starting from the earlier one.
|
|
481
|
+
// This will release locks for the later one, too.
|
|
482
|
+
bc.txns.reverseTxns(2);
|
|
483
|
+
expect(await locks.abandonLocksForReversedTxn(txn1)).to.be.true;
|
|
484
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.false;
|
|
485
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
486
|
+
});
|
|
487
|
+
it("throws if asked to abandon locks for a txn that has not been reversed", async () => {
|
|
488
|
+
const elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
489
|
+
const txnId = bc.txns.getCurrentTxnId();
|
|
490
|
+
await locks.acquireLocks({ exclusive: elementId });
|
|
491
|
+
const element = bc.elements.getElement(elementId);
|
|
492
|
+
await withEditTxn(bc, async (txn) => {
|
|
493
|
+
element.setUserProperties("foo", Guid.createValue());
|
|
494
|
+
element.update(txn);
|
|
495
|
+
});
|
|
496
|
+
// The txn has not been reversed, so abandonLocksForReversedTxn should throw.
|
|
497
|
+
await expect(locks.abandonLocksForReversedTxn(txnId)).to.eventually.be.rejectedWith("has not been reversed");
|
|
498
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.true;
|
|
499
|
+
});
|
|
500
|
+
it("throws if asked to abandon locks for the current txn and there are unsaved changes", async () => {
|
|
501
|
+
const elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
502
|
+
await locks.acquireLocks({ exclusive: elementId });
|
|
503
|
+
const element = bc.elements.getElement(elementId);
|
|
504
|
+
await withEditTxn(bc, async (txn) => {
|
|
505
|
+
element.setUserProperties("foo", Guid.createValue());
|
|
506
|
+
element.update(txn);
|
|
507
|
+
// The current txn has unsaved changes, so abandonLocksForReversedTxn should throw.
|
|
508
|
+
const txnId = bc.txns.getCurrentTxnId();
|
|
509
|
+
await expect(locks.abandonLocksForReversedTxn(txnId)).to.eventually.be.rejectedWith("unsaved changes");
|
|
510
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.true;
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
it("throws if asked to abandon locks for a nonexistent txn", async () => {
|
|
514
|
+
const elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
515
|
+
await locks.acquireLocks({ exclusive: elementId });
|
|
516
|
+
const element = bc.elements.getElement(elementId);
|
|
517
|
+
await withEditTxn(bc, async (txn) => {
|
|
518
|
+
element.setUserProperties("foo", Guid.createValue());
|
|
519
|
+
element.update(txn);
|
|
520
|
+
});
|
|
521
|
+
bc.txns.reverseSingleTxn();
|
|
522
|
+
// Use an ID that is not a valid txn.
|
|
523
|
+
await expect(locks.abandonLocksForReversedTxn("0xffffffffffff")).to.eventually.be.rejectedWith("does not exist");
|
|
524
|
+
// The lock should still be held.
|
|
525
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.true;
|
|
526
|
+
});
|
|
527
|
+
it("returns false when called a second time for an already-abandoned txn", async () => {
|
|
528
|
+
const elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
529
|
+
const txnId = bc.txns.getCurrentTxnId();
|
|
530
|
+
await locks.acquireLocks({ exclusive: elementId });
|
|
531
|
+
const element = bc.elements.getElement(elementId);
|
|
532
|
+
await withEditTxn(bc, async (txn) => {
|
|
533
|
+
element.setUserProperties("foo", Guid.createValue());
|
|
534
|
+
element.update(txn);
|
|
535
|
+
});
|
|
536
|
+
bc.txns.reverseSingleTxn();
|
|
537
|
+
expect(await locks.abandonLocksForReversedTxn(txnId)).to.be.true;
|
|
538
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
539
|
+
// Calling again should be a no-op and return false because the locks are already abandoned.
|
|
540
|
+
expect(await locks.abandonLocksForReversedTxn(txnId)).to.be.false;
|
|
541
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
describe("acquireLocksForReinstatingTxn", () => {
|
|
545
|
+
let bc;
|
|
546
|
+
let bc2;
|
|
547
|
+
let locks;
|
|
548
|
+
beforeEach(async () => {
|
|
549
|
+
bc = await BriefcaseDb.open({ fileName: briefcase1Props.fileName });
|
|
550
|
+
expect(bc.locks.isServerBased).to.be.true;
|
|
551
|
+
locks = bc.locks;
|
|
552
|
+
bc.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
553
|
+
});
|
|
554
|
+
afterEach(async () => {
|
|
555
|
+
await locks[_releaseAllLocks]();
|
|
556
|
+
bc.close();
|
|
557
|
+
if (bc2 !== undefined) {
|
|
558
|
+
await bc2.locks[_releaseAllLocks]();
|
|
559
|
+
bc2.close();
|
|
560
|
+
bc2 = undefined;
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
it("reacquires locks for reinstating a txn", async () => {
|
|
564
|
+
const childId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
565
|
+
const txnId = bc.txns.getCurrentTxnId();
|
|
566
|
+
await locks.acquireLocks({ exclusive: childId });
|
|
567
|
+
expect(locks.holdsExclusiveLock(childId)).to.be.true;
|
|
568
|
+
// We must actually edit something in order to start a new Txn.
|
|
569
|
+
const element = bc.elements.getElement(childId);
|
|
570
|
+
await withEditTxn(bc, async (txn) => {
|
|
571
|
+
element.setUserProperties("foo", { test: true });
|
|
572
|
+
element.update(txn);
|
|
573
|
+
});
|
|
574
|
+
bc.txns.reverseTxns(1);
|
|
575
|
+
await locks.abandonLocksForReversedTxn(txnId);
|
|
576
|
+
expect(locks.holdsExclusiveLock(childId)).to.be.false;
|
|
577
|
+
expect(await locks.acquireLocksForReinstatingTxn(txnId)).to.be.true;
|
|
578
|
+
bc.txns.reinstateTxn();
|
|
579
|
+
expect(locks.holdsExclusiveLock(childId)).to.be.true;
|
|
580
|
+
});
|
|
581
|
+
it("reupgrades a shared lock to exclusive", async () => {
|
|
582
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
583
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
584
|
+
const firstTxnId = bc.txns.getCurrentTxnId();
|
|
585
|
+
await locks.acquireLocks({ exclusive: elementId1, shared: elementId2 });
|
|
586
|
+
expect(locks.holdsSharedLock(elementId2)).to.be.true;
|
|
587
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
588
|
+
// We must actually edit something in order to start a new Txn.
|
|
589
|
+
const element = bc.elements.getElement(elementId1);
|
|
590
|
+
await withEditTxn(bc, async (txn) => {
|
|
591
|
+
element.setUserProperties("foo", { test: true });
|
|
592
|
+
element.update(txn);
|
|
593
|
+
});
|
|
594
|
+
const secondTxnId = bc.txns.getCurrentTxnId();
|
|
595
|
+
expect(firstTxnId).not.to.equal(secondTxnId);
|
|
596
|
+
await locks.acquireLocks({ exclusive: elementId2 }); // upgrade lock from shared to exclusive
|
|
597
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
598
|
+
await withEditTxn(bc, async (txn) => {
|
|
599
|
+
element2.setUserProperties("bar", { test: true });
|
|
600
|
+
element2.update(txn);
|
|
601
|
+
});
|
|
602
|
+
expect(locks.holdsSharedLock(elementId2)).to.be.true;
|
|
603
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
604
|
+
bc.txns.reverseSingleTxn();
|
|
605
|
+
await locks.abandonLocksForReversedTxn(secondTxnId);
|
|
606
|
+
expect(locks.holdsSharedLock(elementId2)).to.be.true;
|
|
607
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
608
|
+
expect(await locks.acquireLocksForReinstatingTxn(secondTxnId)).to.be.true;
|
|
609
|
+
bc.txns.reinstateTxn();
|
|
610
|
+
expect(locks.holdsSharedLock(elementId2)).to.be.true;
|
|
611
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
612
|
+
});
|
|
613
|
+
it("does not acquire on the server an implicit lock originally held for a new element", async () => {
|
|
614
|
+
const childId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
615
|
+
const childElement = bc.elements.getElement(childId);
|
|
616
|
+
const parentId = childElement.parent.id;
|
|
617
|
+
const modelId = childElement.model;
|
|
618
|
+
const physicalProps = {
|
|
619
|
+
classFullName: PhysicalObject.classFullName,
|
|
620
|
+
model: modelId,
|
|
621
|
+
parent: new ElementOwnsChildElements(parentId),
|
|
622
|
+
category: childElement.category,
|
|
623
|
+
code: Code.createEmpty(),
|
|
624
|
+
};
|
|
625
|
+
await locks.acquireLocks({ shared: [modelId, parentId] });
|
|
626
|
+
const txnId = bc.txns.getCurrentTxnId();
|
|
627
|
+
const newElementId = await withEditTxn(bc, async (txn) => {
|
|
628
|
+
return txn.insertElement(physicalProps);
|
|
629
|
+
});
|
|
630
|
+
bc.txns.reverseTxns(1);
|
|
631
|
+
expect(await locks.abandonLocksForReversedTxn(txnId)).to.be.true;
|
|
632
|
+
expect(await locks.acquireLocksForReinstatingTxn(txnId)).to.be.true;
|
|
633
|
+
bc.txns.reinstateTxn();
|
|
634
|
+
expect(locks.holdsExclusiveLock(newElementId)).to.be.true;
|
|
635
|
+
});
|
|
636
|
+
it("acquires locks for earlier txns, too", async () => {
|
|
637
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
638
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
639
|
+
const txn1 = bc.txns.getCurrentTxnId();
|
|
640
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
641
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
642
|
+
await withEditTxn(bc, async (txn) => {
|
|
643
|
+
element1.setUserProperties("foo", Guid.createValue());
|
|
644
|
+
element1.update(txn);
|
|
645
|
+
});
|
|
646
|
+
const txn2 = bc.txns.getCurrentTxnId();
|
|
647
|
+
expect(txn2).not.to.equal(txn1);
|
|
648
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
649
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
650
|
+
await withEditTxn(bc, async (txn) => {
|
|
651
|
+
element2.setUserProperties("bar", Guid.createValue());
|
|
652
|
+
element2.update(txn);
|
|
653
|
+
});
|
|
654
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
655
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
656
|
+
// Reverse both txns and abandon locks starting from the earlier one.
|
|
657
|
+
bc.txns.reverseTxns(2);
|
|
658
|
+
expect(await locks.abandonLocksForReversedTxn(txn1)).to.be.true;
|
|
659
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.false;
|
|
660
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
661
|
+
// Acquire locks for the later txn. This should also acquire locks for the earlier one.
|
|
662
|
+
expect(await locks.acquireLocksForReinstatingTxn(txn2)).to.be.true;
|
|
663
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
664
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
665
|
+
});
|
|
666
|
+
it("cannot acquire locks for a reversed txn after new changes are made", async () => {
|
|
667
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
668
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
669
|
+
const txn1 = bc.txns.getCurrentTxnId();
|
|
670
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
671
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
672
|
+
await withEditTxn(bc, async (txn) => {
|
|
673
|
+
element1.setUserProperties("foo", Guid.createValue());
|
|
674
|
+
element1.update(txn);
|
|
675
|
+
});
|
|
676
|
+
bc.txns.reverseTxns(1);
|
|
677
|
+
expect(await locks.abandonLocksForReversedTxn(txn1)).to.be.true;
|
|
678
|
+
// Make other changes, making the reversed txn inaccessible.
|
|
679
|
+
const txn2 = bc.txns.getCurrentTxnId();
|
|
680
|
+
expect(txn1).to.equal(txn2);
|
|
681
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
682
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
683
|
+
await withEditTxn(bc, async (txn) => {
|
|
684
|
+
element2.setUserProperties("bar", Guid.createValue());
|
|
685
|
+
element2.update(txn);
|
|
686
|
+
});
|
|
687
|
+
// Attempting to reinstate the txn should not acquire any locks.
|
|
688
|
+
expect(await locks.acquireLocksForReinstatingTxn(txn1)).to.be.false;
|
|
689
|
+
// We should still not have a lock on elementId1, because txn1 is now a different txn after the original
|
|
690
|
+
// was reversed and we started making further changes under the reused txn id.
|
|
691
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.false;
|
|
692
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
693
|
+
});
|
|
694
|
+
it("throws if another briefcase holds a conflicting lock", async () => {
|
|
695
|
+
const elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
696
|
+
const txnId = bc.txns.getCurrentTxnId();
|
|
697
|
+
await locks.acquireLocks({ exclusive: elementId });
|
|
698
|
+
const element = bc.elements.getElement(elementId);
|
|
699
|
+
await withEditTxn(bc, async (txn) => {
|
|
700
|
+
element.setUserProperties("foo", Guid.createValue());
|
|
701
|
+
element.update(txn);
|
|
702
|
+
});
|
|
703
|
+
// Reverse and abandon the lock so it's released on the server.
|
|
704
|
+
bc.txns.reverseSingleTxn();
|
|
705
|
+
expect(await locks.abandonLocksForReversedTxn(txnId)).to.be.true;
|
|
706
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
707
|
+
// Have a second briefcase acquire the same lock.
|
|
708
|
+
bc2 = await BriefcaseDb.open({ fileName: briefcase2Props.fileName });
|
|
709
|
+
bc2.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
710
|
+
await bc2.pullChanges();
|
|
711
|
+
await bc2.locks.acquireLocks({ exclusive: elementId });
|
|
712
|
+
// Attempting to reacquire the lock for reinstatement should fail because bc2 holds it.
|
|
713
|
+
await expect(locks.acquireLocksForReinstatingTxn(txnId)).to.eventually.be.rejectedWith("lock is already held");
|
|
714
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
715
|
+
});
|
|
716
|
+
it("is a no-op when locks were never abandoned", async () => {
|
|
717
|
+
const childId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
718
|
+
const txnId = bc.txns.getCurrentTxnId();
|
|
719
|
+
await locks.acquireLocks({ exclusive: childId });
|
|
720
|
+
const element = bc.elements.getElement(childId);
|
|
721
|
+
await withEditTxn(bc, async (txn) => {
|
|
722
|
+
element.setUserProperties("foo", Guid.createValue());
|
|
723
|
+
element.update(txn);
|
|
724
|
+
});
|
|
725
|
+
// Reverse the txn but do NOT abandon locks.
|
|
726
|
+
bc.txns.reverseSingleTxn();
|
|
727
|
+
expect(locks.holdsExclusiveLock(childId)).to.be.true;
|
|
728
|
+
// acquireLocksForReinstatingTxn should be a harmless no-op since locks were never abandoned.
|
|
729
|
+
expect(await locks.acquireLocksForReinstatingTxn(txnId)).to.be.false;
|
|
730
|
+
expect(locks.holdsExclusiveLock(childId)).to.be.true;
|
|
731
|
+
});
|
|
732
|
+
it("throws for an invalid txnId", async () => {
|
|
733
|
+
await expect(locks.acquireLocksForReinstatingTxn("0xffffffffffff")).to.eventually.be.rejectedWith("does not exist");
|
|
734
|
+
});
|
|
735
|
+
});
|
|
736
|
+
describe("TxnManager integration", () => {
|
|
737
|
+
let bc;
|
|
738
|
+
let locks;
|
|
739
|
+
beforeEach(async () => {
|
|
740
|
+
bc = await BriefcaseDb.open({ fileName: briefcase1Props.fileName });
|
|
741
|
+
expect(bc.locks.isServerBased).to.be.true;
|
|
742
|
+
locks = bc.locks;
|
|
743
|
+
bc.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
744
|
+
});
|
|
745
|
+
afterEach(async () => {
|
|
746
|
+
await locks[_releaseAllLocks]();
|
|
747
|
+
bc.close();
|
|
748
|
+
});
|
|
749
|
+
describe("reverseTxnsAsync", () => {
|
|
750
|
+
it("reverses a single txn and abandons its locks", async () => {
|
|
751
|
+
const elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
752
|
+
await locks.acquireLocks({ exclusive: elementId });
|
|
753
|
+
const element = bc.elements.getElement(elementId);
|
|
754
|
+
const originalProps = element.getUserProperties("foo");
|
|
755
|
+
await withEditTxn(bc, async (txn) => {
|
|
756
|
+
element.setUserProperties("foo", Guid.createValue());
|
|
757
|
+
element.update(txn);
|
|
758
|
+
});
|
|
759
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.true;
|
|
760
|
+
await bc.txns.reverseTxnsAsync(1);
|
|
761
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
762
|
+
// Verify the element change was actually reversed.
|
|
763
|
+
const revertedElement = bc.elements.getElement(elementId);
|
|
764
|
+
expect(revertedElement.getUserProperties("foo")).to.deep.equal(originalProps);
|
|
765
|
+
});
|
|
766
|
+
it("reverses multiple txns and abandons all their locks", async () => {
|
|
767
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
768
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
769
|
+
const orig1 = bc.elements.getElement(elementId1).getUserProperties("foo");
|
|
770
|
+
const orig2 = bc.elements.getElement(elementId2).getUserProperties("bar");
|
|
771
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
772
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
773
|
+
await withEditTxn(bc, async (txn) => {
|
|
774
|
+
element1.setUserProperties("foo", Guid.createValue());
|
|
775
|
+
element1.update(txn);
|
|
776
|
+
});
|
|
777
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
778
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
779
|
+
await withEditTxn(bc, async (txn) => {
|
|
780
|
+
element2.setUserProperties("bar", Guid.createValue());
|
|
781
|
+
element2.update(txn);
|
|
782
|
+
});
|
|
783
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
784
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
785
|
+
await bc.txns.reverseTxnsAsync(2);
|
|
786
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.false;
|
|
787
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
788
|
+
// Verify the element changes were actually reversed.
|
|
789
|
+
expect(bc.elements.getElement(elementId1).getUserProperties("foo")).to.deep.equal(orig1);
|
|
790
|
+
expect(bc.elements.getElement(elementId2).getUserProperties("bar")).to.deep.equal(orig2);
|
|
791
|
+
});
|
|
792
|
+
it("when an earlier txn is reversed using reverseTxns, and a later one with reverseTxnsAsync, the latter call abandons the earlier locks, too", async () => {
|
|
793
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
794
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
795
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
796
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
797
|
+
await withEditTxn(bc, async (txn) => {
|
|
798
|
+
element1.setUserProperties("foo", Guid.createValue());
|
|
799
|
+
element1.update(txn);
|
|
800
|
+
});
|
|
801
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
802
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
803
|
+
await withEditTxn(bc, async (txn) => {
|
|
804
|
+
element2.setUserProperties("bar", Guid.createValue());
|
|
805
|
+
element2.update(txn);
|
|
806
|
+
});
|
|
807
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
808
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
809
|
+
// Reverse the later txn using reverseTxns (no lock abandonment).
|
|
810
|
+
bc.txns.reverseTxns(1);
|
|
811
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
812
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
813
|
+
// Reverse the earlier txn using reverseTxnsAsync.
|
|
814
|
+
// This should also abandon locks for the already-reversed later txn.
|
|
815
|
+
await bc.txns.reverseTxnsAsync(1);
|
|
816
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.false;
|
|
817
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
818
|
+
});
|
|
819
|
+
});
|
|
820
|
+
describe("reverseSingleTxnAsync", () => {
|
|
821
|
+
it("reverses a single txn and abandons its locks", async () => {
|
|
822
|
+
const elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
823
|
+
await locks.acquireLocks({ exclusive: elementId });
|
|
824
|
+
const element = bc.elements.getElement(elementId);
|
|
825
|
+
const originalProps = element.getUserProperties("foo");
|
|
826
|
+
await withEditTxn(bc, async (txn) => {
|
|
827
|
+
element.setUserProperties("foo", Guid.createValue());
|
|
828
|
+
element.update(txn);
|
|
829
|
+
});
|
|
830
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.true;
|
|
831
|
+
await bc.txns.reverseSingleTxnAsync();
|
|
832
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
833
|
+
// Verify the element change was actually reversed.
|
|
834
|
+
const revertedElement = bc.elements.getElement(elementId);
|
|
835
|
+
expect(revertedElement.getUserProperties("foo")).to.deep.equal(originalProps);
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
describe("reverseAllTxnsAsync", () => {
|
|
839
|
+
it("reverses all txns and abandons all their locks", async () => {
|
|
840
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
841
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
842
|
+
const orig1 = bc.elements.getElement(elementId1).getUserProperties("foo");
|
|
843
|
+
const orig2 = bc.elements.getElement(elementId2).getUserProperties("bar");
|
|
844
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
845
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
846
|
+
await withEditTxn(bc, async (txn) => {
|
|
847
|
+
element1.setUserProperties("foo", Guid.createValue());
|
|
848
|
+
element1.update(txn);
|
|
849
|
+
});
|
|
850
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
851
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
852
|
+
await withEditTxn(bc, async (txn) => {
|
|
853
|
+
element2.setUserProperties("bar", Guid.createValue());
|
|
854
|
+
element2.update(txn);
|
|
855
|
+
});
|
|
856
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
857
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
858
|
+
await bc.txns.reverseAllTxnsAsync();
|
|
859
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.false;
|
|
860
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
861
|
+
// Verify the element changes were actually reversed.
|
|
862
|
+
expect(bc.elements.getElement(elementId1).getUserProperties("foo")).to.deep.equal(orig1);
|
|
863
|
+
expect(bc.elements.getElement(elementId2).getUserProperties("bar")).to.deep.equal(orig2);
|
|
864
|
+
});
|
|
865
|
+
});
|
|
866
|
+
describe("reverseToTxnAsync", () => {
|
|
867
|
+
it("reverses to a specific txn and abandons locks for all reversed txns", async () => {
|
|
868
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
869
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
870
|
+
const origProps2 = bc.elements.getElement(elementId2).getUserProperties("bar");
|
|
871
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
872
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
873
|
+
await withEditTxn(bc, async (txn) => {
|
|
874
|
+
element1.setUserProperties("foo", Guid.createValue());
|
|
875
|
+
element1.update(txn);
|
|
876
|
+
});
|
|
877
|
+
const txnAfterFirst = bc.txns.getCurrentTxnId();
|
|
878
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
879
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
880
|
+
await withEditTxn(bc, async (txn) => {
|
|
881
|
+
element2.setUserProperties("bar", Guid.createValue());
|
|
882
|
+
element2.update(txn);
|
|
883
|
+
});
|
|
884
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
885
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
886
|
+
await bc.txns.reverseToTxnAsync(txnAfterFirst);
|
|
887
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
888
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
889
|
+
// Verify only the second element's changes were reversed.
|
|
890
|
+
expect(bc.elements.getElement(elementId2).getUserProperties("bar")).to.deep.equal(origProps2);
|
|
891
|
+
});
|
|
892
|
+
});
|
|
893
|
+
describe("cancelToTxnAsync", () => {
|
|
894
|
+
it("cancels to a specific txn and abandons locks for all cancelled txns", async () => {
|
|
895
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
896
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
897
|
+
const origProps2 = bc.elements.getElement(elementId2).getUserProperties("bar");
|
|
898
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
899
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
900
|
+
await withEditTxn(bc, async (txn) => {
|
|
901
|
+
element1.setUserProperties("foo", Guid.createValue());
|
|
902
|
+
element1.update(txn);
|
|
903
|
+
});
|
|
904
|
+
const txnAfterFirst = bc.txns.getCurrentTxnId();
|
|
905
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
906
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
907
|
+
await withEditTxn(bc, async (txn) => {
|
|
908
|
+
element2.setUserProperties("bar", Guid.createValue());
|
|
909
|
+
element2.update(txn);
|
|
910
|
+
});
|
|
911
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
912
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
913
|
+
await bc.txns.cancelToTxnAsync(txnAfterFirst);
|
|
914
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
915
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
916
|
+
// Verify the second element's changes were cancelled.
|
|
917
|
+
expect(bc.elements.getElement(elementId2).getUserProperties("bar")).to.deep.equal(origProps2);
|
|
918
|
+
// cancelTo should not allow redo
|
|
919
|
+
expect(bc.txns.isRedoPossible).to.be.false;
|
|
920
|
+
});
|
|
921
|
+
it("after regular cancelTo, locks can be abandoned, but not re-acquired", async () => {
|
|
922
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
923
|
+
const txnId = bc.txns.getCurrentTxnId();
|
|
924
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
925
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
926
|
+
await withEditTxn(bc, async (txn) => {
|
|
927
|
+
element1.setUserProperties("foo", Guid.createValue());
|
|
928
|
+
element1.update(txn);
|
|
929
|
+
});
|
|
930
|
+
// Use the regular cancelTo which does not abandon locks.
|
|
931
|
+
bc.txns.cancelTo(txnId);
|
|
932
|
+
// Now try to abandon the locks, which should work.
|
|
933
|
+
expect(await locks.abandonLocksForReversedTxn(txnId)).to.be.true;
|
|
934
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.false;
|
|
935
|
+
// However, it doesn't make sense to reacquire these locks because the Txn no longer exists.
|
|
936
|
+
await expect(locks.acquireLocksForReinstatingTxn(txnId)).to.eventually.be.rejectedWith("does not exist");
|
|
937
|
+
});
|
|
938
|
+
it("canceling a txn prevents subsequent reacquisition of that txn's locks", async () => {
|
|
939
|
+
const elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
940
|
+
const txnBefore = bc.txns.getCurrentTxnId();
|
|
941
|
+
await locks.acquireLocks({ exclusive: elementId });
|
|
942
|
+
const element = bc.elements.getElement(elementId);
|
|
943
|
+
await withEditTxn(bc, async (txn) => {
|
|
944
|
+
element.setUserProperties("foo", Guid.createValue());
|
|
945
|
+
element.update(txn);
|
|
946
|
+
});
|
|
947
|
+
await bc.txns.cancelToTxnAsync(txnBefore);
|
|
948
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
949
|
+
// Attempting to acquire locks for the cancelled transaction should fail
|
|
950
|
+
// because the transaction itself no longer exists and its lock records have been cleared.
|
|
951
|
+
await expect(locks.acquireLocksForReinstatingTxn(txnBefore)).to.eventually.be.rejectedWith("does not exist");
|
|
952
|
+
});
|
|
953
|
+
it("reverses but does not cancel if lock abandonment fails", async () => {
|
|
954
|
+
const elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
955
|
+
const txnBefore = bc.txns.getCurrentTxnId();
|
|
956
|
+
await locks.acquireLocks({ exclusive: elementId });
|
|
957
|
+
const element = bc.elements.getElement(elementId);
|
|
958
|
+
await withEditTxn(bc, async (txn) => {
|
|
959
|
+
element.setUserProperties("foo", Guid.createValue());
|
|
960
|
+
element.update(txn);
|
|
961
|
+
});
|
|
962
|
+
const error = new IModelError(IModelStatus.BadRequest, "Lock abandonment failed");
|
|
963
|
+
sinonStub(locks, "abandonLocksForReversedTxn").rejects(error);
|
|
964
|
+
await expect(bc.txns.cancelToTxnAsync(txnBefore)).to.eventually.be.rejectedWith(IModelError, "Lock abandonment failed");
|
|
965
|
+
// The txn was reversed but not canceled, so we can reinstate. Locks are still held.
|
|
966
|
+
expect(bc.txns.isRedoPossible).to.be.true;
|
|
967
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.true;
|
|
968
|
+
// And now we can try releasing the locks again.
|
|
969
|
+
sinonRestore();
|
|
970
|
+
await bc.locks.abandonLocksForReversedTxn(txnBefore);
|
|
971
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
972
|
+
// And cancel the already-reversed txn.
|
|
973
|
+
expect(bc.txns.cancelTo(txnBefore)).to.equal(IModelStatus.NothingToUndo);
|
|
974
|
+
expect(bc.txns.isRedoPossible).to.be.false;
|
|
975
|
+
});
|
|
976
|
+
});
|
|
977
|
+
describe("reinstateTxnAndAcquireLocks", () => {
|
|
978
|
+
it("reinstates a reversed txn and reacquires its locks", async () => {
|
|
979
|
+
const elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
980
|
+
await locks.acquireLocks({ exclusive: elementId });
|
|
981
|
+
const element = bc.elements.getElement(elementId);
|
|
982
|
+
const originalProps = element.getUserProperties("foo");
|
|
983
|
+
const newValue = Guid.createValue();
|
|
984
|
+
await withEditTxn(bc, async (txn) => {
|
|
985
|
+
element.setUserProperties("foo", newValue);
|
|
986
|
+
element.update(txn);
|
|
987
|
+
});
|
|
988
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.true;
|
|
989
|
+
await bc.txns.reverseTxnsAsync(1);
|
|
990
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
991
|
+
// Verify the element change was reversed.
|
|
992
|
+
expect(bc.elements.getElement(elementId).getUserProperties("foo")).to.deep.equal(originalProps);
|
|
993
|
+
await bc.txns.reinstateTxnAsync();
|
|
994
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.true;
|
|
995
|
+
// Verify the element change was reinstated.
|
|
996
|
+
expect(bc.elements.getElement(elementId).getUserProperties("foo")).to.equal(newValue);
|
|
997
|
+
});
|
|
998
|
+
it("multiple txns reversed together are reinstated as one", async () => {
|
|
999
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
1000
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
1001
|
+
const orig1 = bc.elements.getElement(elementId1).getUserProperties("foo");
|
|
1002
|
+
const orig2 = bc.elements.getElement(elementId2).getUserProperties("bar");
|
|
1003
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
1004
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
1005
|
+
const newValue1 = Guid.createValue();
|
|
1006
|
+
await withEditTxn(bc, async (txn) => {
|
|
1007
|
+
element1.setUserProperties("foo", newValue1);
|
|
1008
|
+
element1.update(txn);
|
|
1009
|
+
});
|
|
1010
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
1011
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
1012
|
+
const newValue2 = Guid.createValue();
|
|
1013
|
+
await withEditTxn(bc, async (txn) => {
|
|
1014
|
+
element2.setUserProperties("bar", newValue2);
|
|
1015
|
+
element2.update(txn);
|
|
1016
|
+
});
|
|
1017
|
+
await bc.txns.reverseTxnsAsync(2);
|
|
1018
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.false;
|
|
1019
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
1020
|
+
// Verify both element changes were reversed.
|
|
1021
|
+
expect(bc.elements.getElement(elementId1).getUserProperties("foo")).to.deep.equal(orig1);
|
|
1022
|
+
expect(bc.elements.getElement(elementId2).getUserProperties("bar")).to.deep.equal(orig2);
|
|
1023
|
+
// A single reinstate should bring back both txns and reacquire all locks.
|
|
1024
|
+
await bc.txns.reinstateTxnAsync();
|
|
1025
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
1026
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
1027
|
+
// Verify both element changes are reinstated.
|
|
1028
|
+
expect(bc.elements.getElement(elementId1).getUserProperties("foo")).to.equal(newValue1);
|
|
1029
|
+
expect(bc.elements.getElement(elementId2).getUserProperties("bar")).to.equal(newValue2);
|
|
1030
|
+
});
|
|
1031
|
+
it("abandons unsaved changes before reinstatement", async () => {
|
|
1032
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
1033
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
1034
|
+
// Make a change, save it, and reverse it.
|
|
1035
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
1036
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
1037
|
+
const originalProps1 = element1.getUserProperties("foo");
|
|
1038
|
+
const newValue1 = Guid.createValue();
|
|
1039
|
+
await withEditTxn(bc, async (txn) => {
|
|
1040
|
+
element1.setUserProperties("foo", newValue1);
|
|
1041
|
+
element1.update(txn);
|
|
1042
|
+
});
|
|
1043
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
1044
|
+
await bc.txns.reverseTxnsAsync(1);
|
|
1045
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.false;
|
|
1046
|
+
// Verify the element change was reversed.
|
|
1047
|
+
expect(bc.elements.getElement(elementId1).getUserProperties("foo")).to.deep.equal(originalProps1);
|
|
1048
|
+
// Now make an unsaved change to a different element.
|
|
1049
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
1050
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
1051
|
+
const originalProps2 = element2.getUserProperties("bar");
|
|
1052
|
+
const newValue2 = Guid.createValue();
|
|
1053
|
+
await withEditTxn(bc, async (txn) => {
|
|
1054
|
+
element2.setUserProperties("bar", newValue2);
|
|
1055
|
+
element2.update(txn);
|
|
1056
|
+
// Verify the unsaved change is present and lock is held.
|
|
1057
|
+
expect(bc.txns.hasUnsavedChanges).to.be.true;
|
|
1058
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.true;
|
|
1059
|
+
expect(bc.elements.getElement(elementId2).getUserProperties("bar")).to.equal(newValue2);
|
|
1060
|
+
// Reinstate the first txn. This should:
|
|
1061
|
+
// 1. Abandon the unsaved change to element2
|
|
1062
|
+
// 2. Abandon the lock on element2
|
|
1063
|
+
// 3. Reinstate the change to element1
|
|
1064
|
+
// 4. Reacquire the lock on element1
|
|
1065
|
+
await bc.txns.reinstateTxnAsync();
|
|
1066
|
+
// Verify the unsaved change was abandoned.
|
|
1067
|
+
expect(bc.txns.hasUnsavedChanges).to.be.false;
|
|
1068
|
+
expect(bc.elements.getElement(elementId2).getUserProperties("bar")).to.deep.equal(originalProps2);
|
|
1069
|
+
expect(locks.holdsExclusiveLock(elementId2)).to.be.false;
|
|
1070
|
+
// Verify the first change was reinstated and its lock reacquired.
|
|
1071
|
+
expect(bc.elements.getElement(elementId1).getUserProperties("foo")).to.equal(newValue1);
|
|
1072
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
1073
|
+
});
|
|
1074
|
+
});
|
|
1075
|
+
describe("throws and leaves locks acquired if reinstateTxn fails", async () => {
|
|
1076
|
+
let elementId;
|
|
1077
|
+
let fooValue;
|
|
1078
|
+
beforeEach(async () => {
|
|
1079
|
+
elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
1080
|
+
await locks.acquireLocks({ exclusive: elementId });
|
|
1081
|
+
const element = bc.elements.getElement(elementId);
|
|
1082
|
+
fooValue = Guid.createValue();
|
|
1083
|
+
await withEditTxn(bc, async (txn) => {
|
|
1084
|
+
element.setUserProperties("foo", fooValue);
|
|
1085
|
+
element.update(txn);
|
|
1086
|
+
});
|
|
1087
|
+
await bc.txns.reverseTxnsAsync(1);
|
|
1088
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
1089
|
+
sinonStub(bc.txns, "reinstateTxn").returns(IModelStatus.BadRequest);
|
|
1090
|
+
await expect(bc.txns.reinstateTxnAsync()).to.eventually.be.rejectedWith(IModelError, "Bad Request");
|
|
1091
|
+
// Even though it failed to reinstate, it obtained the lock and did not release it.
|
|
1092
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.true;
|
|
1093
|
+
});
|
|
1094
|
+
it("and we can try reinstating again, without any problems caused by the locks already being acquired", async () => {
|
|
1095
|
+
sinonRestore();
|
|
1096
|
+
await bc.txns.reinstateTxnAsync();
|
|
1097
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.true;
|
|
1098
|
+
expect(bc.elements.getElement(elementId).getUserProperties("foo")).to.equal(fooValue);
|
|
1099
|
+
});
|
|
1100
|
+
it("and we can then release the locks", async () => {
|
|
1101
|
+
await locks.abandonLocksForReversedTxn(bc.txns.getCurrentTxnId());
|
|
1102
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
1103
|
+
});
|
|
1104
|
+
});
|
|
1105
|
+
it("throws and does not reinstate if acquireLocksForReinstatingTxn fails", async () => {
|
|
1106
|
+
const elementId = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
1107
|
+
await locks.acquireLocks({ exclusive: elementId });
|
|
1108
|
+
const element = bc.elements.getElement(elementId);
|
|
1109
|
+
const originalProps = element.getUserProperties("foo");
|
|
1110
|
+
const newValue = Guid.createValue();
|
|
1111
|
+
await withEditTxn(bc, async (txn) => {
|
|
1112
|
+
element.setUserProperties("foo", newValue);
|
|
1113
|
+
element.update(txn);
|
|
1114
|
+
});
|
|
1115
|
+
await bc.txns.reverseTxnsAsync(1);
|
|
1116
|
+
expect(locks.holdsExclusiveLock(elementId)).to.be.false;
|
|
1117
|
+
// Verify the element change was reversed.
|
|
1118
|
+
expect(bc.elements.getElement(elementId).getUserProperties("foo")).to.deep.equal(originalProps);
|
|
1119
|
+
// Simulate acquireLocksForReinstatingTxn failing (e.g. because another briefcase holds the lock)
|
|
1120
|
+
const error = new LockConflict(0x12345, "other briefcase alias", "exclusive lock is already held");
|
|
1121
|
+
sinonStub(locks, "acquireLocksForReinstatingTxn").rejects(error);
|
|
1122
|
+
await expect(bc.txns.reinstateTxnAsync()).to.eventually.be.rejectedWith("exclusive lock is already held");
|
|
1123
|
+
// Verify it was NOT reinstated
|
|
1124
|
+
expect(bc.elements.getElement(elementId).getUserProperties("foo")).to.deep.equal(originalProps);
|
|
1125
|
+
});
|
|
1126
|
+
it("new changes after reversal truncate the redo stack and clear reversible lock records", async () => {
|
|
1127
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
1128
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
1129
|
+
// 1. Make a change to element 1 and save it.
|
|
1130
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
1131
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
1132
|
+
await withEditTxn(bc, async (txn) => {
|
|
1133
|
+
element1.setUserProperties("foo", Guid.createValue());
|
|
1134
|
+
element1.update(txn);
|
|
1135
|
+
});
|
|
1136
|
+
const txn1Id = bc.txns.getCurrentTxnId();
|
|
1137
|
+
// 2. Reverse the txn and abandon locks.
|
|
1138
|
+
await bc.txns.reverseTxnsAsync(1);
|
|
1139
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.false;
|
|
1140
|
+
expect(bc.txns.isRedoPossible).to.be.true;
|
|
1141
|
+
// 3. Make a change to element 2 and save it. This truncates the redo stack.
|
|
1142
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
1143
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
1144
|
+
await withEditTxn(bc, async (txn) => {
|
|
1145
|
+
element2.setUserProperties("bar", Guid.createValue());
|
|
1146
|
+
element2.update(txn);
|
|
1147
|
+
});
|
|
1148
|
+
// The redo stack should now be truncated.
|
|
1149
|
+
expect(bc.txns.isRedoPossible).to.be.false;
|
|
1150
|
+
// 4. Try to reacquire locks for the truncated transaction.
|
|
1151
|
+
await expect(locks.acquireLocksForReinstatingTxn(txn1Id)).to.eventually.be.rejectedWith("does not exist");
|
|
1152
|
+
});
|
|
1153
|
+
it("abandoned changes after reversal do not prevent reinstatement", async () => {
|
|
1154
|
+
const elementId1 = IModelTestUtils.queryByUserLabel(bc, "PhysicalObject2");
|
|
1155
|
+
const elementId2 = IModelTestUtils.queryByUserLabel(bc, "ChildObject1B");
|
|
1156
|
+
// 1. Make a change to element 1 and save it.
|
|
1157
|
+
await locks.acquireLocks({ exclusive: elementId1 });
|
|
1158
|
+
const element1 = bc.elements.getElement(elementId1);
|
|
1159
|
+
const newValue1 = Guid.createValue();
|
|
1160
|
+
await withEditTxn(bc, async (txn) => {
|
|
1161
|
+
element1.setUserProperties("foo", newValue1);
|
|
1162
|
+
element1.update(txn);
|
|
1163
|
+
});
|
|
1164
|
+
// 2. Reverse the txn and abandon locks.
|
|
1165
|
+
await bc.txns.reverseTxnsAsync(1);
|
|
1166
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.false;
|
|
1167
|
+
expect(bc.txns.isRedoPossible).to.be.true;
|
|
1168
|
+
// 3. Make a change to element 2 and abandon it.
|
|
1169
|
+
await locks.acquireLocks({ exclusive: elementId2 });
|
|
1170
|
+
const element2 = bc.elements.getElement(elementId2);
|
|
1171
|
+
await withEditTxn(bc, async (txn) => {
|
|
1172
|
+
element2.setUserProperties("bar", Guid.createValue());
|
|
1173
|
+
element2.update(txn);
|
|
1174
|
+
txn.abandonChanges();
|
|
1175
|
+
});
|
|
1176
|
+
expect(bc.txns.isRedoPossible).to.be.true;
|
|
1177
|
+
// 4. Reinstate should still work.
|
|
1178
|
+
await bc.txns.reinstateTxnAsync();
|
|
1179
|
+
expect(locks.holdsExclusiveLock(elementId1)).to.be.true;
|
|
1180
|
+
expect(bc.elements.getElement(elementId1).getUserProperties("foo")).to.equal(newValue1);
|
|
1181
|
+
});
|
|
1182
|
+
});
|
|
1183
|
+
});
|
|
280
1184
|
});
|
|
281
1185
|
//# sourceMappingURL=ServerBasedLocks.test.js.map
|