@itwin/core-backend 3.7.0 → 3.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -1
- package/lib/cjs/AliCloudStorageService.d.ts +23 -23
- package/lib/cjs/AliCloudStorageService.js +71 -71
- package/lib/cjs/AliCloudStorageService.js.map +1 -1
- package/lib/cjs/BackendHubAccess.d.ts +224 -224
- package/lib/cjs/BackendHubAccess.js +41 -41
- package/lib/cjs/BackendHubAccess.js.map +1 -1
- package/lib/cjs/BackendLoggerCategory.d.ts +91 -91
- package/lib/cjs/BackendLoggerCategory.js +99 -99
- package/lib/cjs/BackendLoggerCategory.js.map +1 -1
- package/lib/cjs/BisCoreSchema.d.ts +21 -21
- package/lib/cjs/BisCoreSchema.js +60 -60
- package/lib/cjs/BisCoreSchema.js.map +1 -1
- package/lib/cjs/BriefcaseManager.d.ts +170 -170
- package/lib/cjs/BriefcaseManager.js +414 -414
- package/lib/cjs/BriefcaseManager.js.map +1 -1
- package/lib/cjs/Category.d.ts +153 -153
- package/lib/cjs/Category.js +243 -243
- package/lib/cjs/Category.js.map +1 -1
- package/lib/cjs/ChangeSummaryManager.d.ts +187 -187
- package/lib/cjs/ChangeSummaryManager.js +447 -447
- package/lib/cjs/ChangeSummaryManager.js.map +1 -1
- package/lib/cjs/ChangedElementsDb.d.ts +99 -99
- package/lib/cjs/ChangedElementsDb.js +165 -165
- package/lib/cjs/ChangedElementsDb.js.map +1 -1
- package/lib/cjs/ChangedElementsManager.d.ts +39 -39
- package/lib/cjs/ChangedElementsManager.js +88 -88
- package/lib/cjs/ChangedElementsManager.js.map +1 -1
- package/lib/cjs/CheckpointManager.d.ts +121 -121
- package/lib/cjs/CheckpointManager.js +349 -349
- package/lib/cjs/CheckpointManager.js.map +1 -1
- package/lib/cjs/ClassRegistry.d.ts +79 -79
- package/lib/cjs/ClassRegistry.js +250 -250
- package/lib/cjs/ClassRegistry.js.map +1 -1
- package/lib/cjs/CloudSqlite.d.ts +350 -350
- package/lib/cjs/CloudSqlite.js +153 -153
- package/lib/cjs/CloudSqlite.js.map +1 -1
- package/lib/cjs/CloudStorageBackend.d.ts +58 -58
- package/lib/cjs/CloudStorageBackend.js +173 -173
- package/lib/cjs/CloudStorageBackend.js.map +1 -1
- package/lib/cjs/CodeService.d.ts +396 -396
- package/lib/cjs/CodeService.js +63 -63
- package/lib/cjs/CodeService.js.map +1 -1
- package/lib/cjs/CodeSpecs.d.ts +55 -55
- package/lib/cjs/CodeSpecs.js +124 -124
- package/lib/cjs/CodeSpecs.js.map +1 -1
- package/lib/cjs/ConcurrentQuery.d.ts +13 -13
- package/lib/cjs/ConcurrentQuery.js +36 -36
- package/lib/cjs/ConcurrentQuery.js.map +1 -1
- package/lib/cjs/CustomViewState3dCreator.d.ts +21 -21
- package/lib/cjs/CustomViewState3dCreator.js +82 -82
- package/lib/cjs/CustomViewState3dCreator.js.map +1 -1
- package/lib/cjs/DevTools.d.ts +71 -71
- package/lib/cjs/DevTools.js +154 -154
- package/lib/cjs/DevTools.js.map +1 -1
- package/lib/cjs/DisplayStyle.d.ts +106 -106
- package/lib/cjs/DisplayStyle.js +256 -256
- package/lib/cjs/DisplayStyle.js.map +1 -1
- package/lib/cjs/ECDb.d.ts +194 -194
- package/lib/cjs/ECDb.js +355 -355
- package/lib/cjs/ECDb.js.map +1 -1
- package/lib/cjs/ECSchemaXmlContext.d.ts +18 -18
- package/lib/cjs/ECSchemaXmlContext.js +41 -41
- package/lib/cjs/ECSchemaXmlContext.js.map +1 -1
- package/lib/cjs/ECSqlStatement.d.ts +473 -473
- package/lib/cjs/ECSqlStatement.js +791 -791
- package/lib/cjs/ECSqlStatement.js.map +1 -1
- package/lib/cjs/Element.d.ts +1109 -1109
- package/lib/cjs/Element.js +1427 -1427
- package/lib/cjs/Element.js.map +1 -1
- package/lib/cjs/ElementAspect.d.ts +179 -179
- package/lib/cjs/ElementAspect.js +211 -211
- package/lib/cjs/ElementAspect.js.map +1 -1
- package/lib/cjs/ElementGraphics.d.ts +9 -9
- package/lib/cjs/ElementGraphics.js +41 -41
- package/lib/cjs/ElementGraphics.js.map +1 -1
- package/lib/cjs/ElementTreeWalker.d.ts +162 -162
- package/lib/cjs/ElementTreeWalker.js +409 -409
- package/lib/cjs/ElementTreeWalker.js.map +1 -1
- package/lib/cjs/Entity.d.ts +106 -106
- package/lib/cjs/Entity.js +134 -134
- package/lib/cjs/Entity.js.map +1 -1
- package/lib/cjs/EntityReferences.d.ts +49 -49
- package/lib/cjs/EntityReferences.js +95 -95
- package/lib/cjs/EntityReferences.js.map +1 -1
- package/lib/cjs/ExportGraphics.d.ts +317 -317
- package/lib/cjs/ExportGraphics.js +208 -208
- package/lib/cjs/ExportGraphics.js.map +1 -1
- package/lib/cjs/ExternalSource.d.ts +96 -96
- package/lib/cjs/ExternalSource.js +131 -131
- package/lib/cjs/ExternalSource.js.map +1 -1
- package/lib/cjs/GeoCoordConfig.d.ts +19 -19
- package/lib/cjs/GeoCoordConfig.js +85 -85
- package/lib/cjs/GeoCoordConfig.js.map +1 -1
- package/lib/cjs/GeometrySummary.d.ts +4 -4
- package/lib/cjs/GeometrySummary.js +380 -380
- package/lib/cjs/GeometrySummary.js.map +1 -1
- package/lib/cjs/HubMock.d.ts +94 -94
- package/lib/cjs/HubMock.js +222 -222
- package/lib/cjs/HubMock.js.map +1 -1
- package/lib/cjs/IModelCloneContext.d.ts +8 -8
- package/lib/cjs/IModelCloneContext.js +15 -15
- package/lib/cjs/IModelCloneContext.js.map +1 -1
- package/lib/cjs/IModelDb.d.ts +1213 -1213
- package/lib/cjs/IModelDb.js +2392 -2392
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/cjs/IModelElementCloneContext.d.ts +89 -89
- package/lib/cjs/IModelElementCloneContext.js +159 -159
- package/lib/cjs/IModelElementCloneContext.js.map +1 -1
- package/lib/cjs/IModelHost.d.ts +369 -369
- package/lib/cjs/IModelHost.js +496 -496
- package/lib/cjs/IModelHost.js.map +1 -1
- package/lib/cjs/IModelJsFs.d.ts +62 -62
- package/lib/cjs/IModelJsFs.js +140 -140
- package/lib/cjs/IModelJsFs.js.map +1 -1
- package/lib/cjs/IpcHost.d.ts +104 -104
- package/lib/cjs/IpcHost.js +278 -278
- package/lib/cjs/IpcHost.js.map +1 -1
- package/lib/cjs/LineStyle.d.ts +311 -311
- package/lib/cjs/LineStyle.js +295 -295
- package/lib/cjs/LineStyle.js.map +1 -1
- package/lib/cjs/LocalHub.d.ts +162 -162
- package/lib/cjs/LocalHub.js +585 -585
- package/lib/cjs/LocalHub.js.map +1 -1
- package/lib/cjs/LocalhostIpcHost.d.ts +20 -20
- package/lib/cjs/LocalhostIpcHost.js +81 -81
- package/lib/cjs/LocalhostIpcHost.js.map +1 -1
- package/lib/cjs/Material.d.ts +131 -131
- package/lib/cjs/Material.js +181 -181
- package/lib/cjs/Material.js.map +1 -1
- package/lib/cjs/Model.d.ts +392 -392
- package/lib/cjs/Model.js +485 -485
- package/lib/cjs/Model.js.map +1 -1
- package/lib/cjs/NativeAppStorage.d.ts +51 -51
- package/lib/cjs/NativeAppStorage.js +202 -202
- package/lib/cjs/NativeAppStorage.js.map +1 -1
- package/lib/cjs/NativeHost.d.ts +52 -52
- package/lib/cjs/NativeHost.js +163 -163
- package/lib/cjs/NativeHost.js.map +1 -1
- package/lib/cjs/NavigationRelationship.d.ts +156 -156
- package/lib/cjs/NavigationRelationship.js +225 -225
- package/lib/cjs/NavigationRelationship.js.map +1 -1
- package/lib/cjs/PromiseMemoizer.d.ts +51 -51
- package/lib/cjs/PromiseMemoizer.js +101 -101
- package/lib/cjs/PromiseMemoizer.js.map +1 -1
- package/lib/cjs/PropertyStore.d.ts +132 -132
- package/lib/cjs/PropertyStore.js +11 -11
- package/lib/cjs/PropertyStore.js.map +1 -1
- package/lib/cjs/Relationship.d.ts +416 -416
- package/lib/cjs/Relationship.js +494 -494
- package/lib/cjs/Relationship.js.map +1 -1
- package/lib/cjs/RpcBackend.d.ts +5 -5
- package/lib/cjs/RpcBackend.js +32 -32
- package/lib/cjs/RpcBackend.js.map +1 -1
- package/lib/cjs/SQLiteDb.d.ts +219 -219
- package/lib/cjs/SQLiteDb.js +214 -214
- package/lib/cjs/SQLiteDb.js.map +1 -1
- package/lib/cjs/Schema.d.ts +55 -55
- package/lib/cjs/Schema.js +83 -83
- package/lib/cjs/Schema.js.map +1 -1
- package/lib/cjs/ServerBasedLocks.d.ts +57 -57
- package/lib/cjs/ServerBasedLocks.js +168 -168
- package/lib/cjs/ServerBasedLocks.js.map +1 -1
- package/lib/cjs/SqliteStatement.d.ts +360 -360
- package/lib/cjs/SqliteStatement.js +586 -586
- package/lib/cjs/SqliteStatement.js.map +1 -1
- package/lib/cjs/Texture.d.ts +58 -58
- package/lib/cjs/Texture.js +82 -82
- package/lib/cjs/Texture.js.map +1 -1
- package/lib/cjs/TileStorage.d.ts +18 -18
- package/lib/cjs/TileStorage.d.ts.map +1 -1
- package/lib/cjs/TileStorage.js +90 -90
- package/lib/cjs/TileStorage.js.map +1 -1
- package/lib/cjs/TxnManager.d.ts +202 -202
- package/lib/cjs/TxnManager.js +356 -356
- package/lib/cjs/TxnManager.js.map +1 -1
- package/lib/cjs/ViewDefinition.d.ts +439 -439
- package/lib/cjs/ViewDefinition.js +646 -646
- package/lib/cjs/ViewDefinition.js.map +1 -1
- package/lib/cjs/ViewStateHydrator.d.ts +13 -13
- package/lib/cjs/ViewStateHydrator.js +103 -103
- package/lib/cjs/ViewStateHydrator.js.map +1 -1
- package/lib/cjs/assets/IModelChange.02.00.00.ecschema.xml +90 -90
- package/lib/cjs/assets/Settings/Schemas/Cloud.Schema.json +67 -67
- package/lib/cjs/assets/Settings/Schemas/Gcs.schema.json +31 -31
- package/lib/cjs/assets/Settings/Schemas/Workspace.Schema.json +52 -52
- package/lib/cjs/assets/Settings/backend.setting.json5 +138 -138
- package/lib/cjs/core-backend.d.ts +184 -184
- package/lib/cjs/core-backend.js +204 -204
- package/lib/cjs/core-backend.js.map +1 -1
- package/lib/cjs/domains/FunctionalElements.d.ts +101 -101
- package/lib/cjs/domains/FunctionalElements.js +145 -145
- package/lib/cjs/domains/FunctionalElements.js.map +1 -1
- package/lib/cjs/domains/FunctionalSchema.d.ts +13 -13
- package/lib/cjs/domains/FunctionalSchema.js +40 -40
- package/lib/cjs/domains/FunctionalSchema.js.map +1 -1
- package/lib/cjs/domains/GenericElements.d.ts +177 -177
- package/lib/cjs/domains/GenericElements.js +263 -263
- package/lib/cjs/domains/GenericElements.js.map +1 -1
- package/lib/cjs/domains/GenericSchema.d.ts +10 -10
- package/lib/cjs/domains/GenericSchema.js +28 -28
- package/lib/cjs/domains/GenericSchema.js.map +1 -1
- package/lib/cjs/rpc/multipart.d.ts +11 -11
- package/lib/cjs/rpc/multipart.js +71 -71
- package/lib/cjs/rpc/multipart.js.map +1 -1
- package/lib/cjs/rpc/tracing.d.ts +28 -28
- package/lib/cjs/rpc/tracing.js +74 -74
- package/lib/cjs/rpc/tracing.js.map +1 -1
- package/lib/cjs/rpc/web/logging.d.ts +9 -9
- package/lib/cjs/rpc/web/logging.js +43 -43
- package/lib/cjs/rpc/web/logging.js.map +1 -1
- package/lib/cjs/rpc/web/request.d.ts +6 -6
- package/lib/cjs/rpc/web/request.js +74 -74
- package/lib/cjs/rpc/web/request.js.map +1 -1
- package/lib/cjs/rpc/web/response.d.ts +6 -6
- package/lib/cjs/rpc/web/response.js +106 -106
- package/lib/cjs/rpc/web/response.js.map +1 -1
- package/lib/cjs/rpc-impl/DevToolsRpcImpl.d.ts +15 -15
- package/lib/cjs/rpc-impl/DevToolsRpcImpl.js +32 -32
- package/lib/cjs/rpc-impl/DevToolsRpcImpl.js.map +1 -1
- package/lib/cjs/rpc-impl/IModelReadRpcImpl.d.ts +47 -47
- package/lib/cjs/rpc-impl/IModelReadRpcImpl.js +294 -294
- package/lib/cjs/rpc-impl/IModelReadRpcImpl.js.map +1 -1
- package/lib/cjs/rpc-impl/IModelTileRpcImpl.d.ts +22 -22
- package/lib/cjs/rpc-impl/IModelTileRpcImpl.js +205 -205
- package/lib/cjs/rpc-impl/IModelTileRpcImpl.js.map +1 -1
- package/lib/cjs/rpc-impl/RpcBriefcaseUtility.d.ts +36 -36
- package/lib/cjs/rpc-impl/RpcBriefcaseUtility.js +168 -168
- package/lib/cjs/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
- package/lib/cjs/rpc-impl/SnapshotIModelRpcImpl.d.ts +16 -16
- package/lib/cjs/rpc-impl/SnapshotIModelRpcImpl.js +43 -43
- package/lib/cjs/rpc-impl/SnapshotIModelRpcImpl.js.map +1 -1
- package/lib/cjs/rpc-impl/WipRpcImpl.d.ts +15 -15
- package/lib/cjs/rpc-impl/WipRpcImpl.js +37 -37
- package/lib/cjs/rpc-impl/WipRpcImpl.js.map +1 -1
- package/lib/cjs/workspace/Settings.d.ts +186 -186
- package/lib/cjs/workspace/Settings.js +155 -155
- package/lib/cjs/workspace/Settings.js.map +1 -1
- package/lib/cjs/workspace/SettingsSchemas.d.ts +75 -75
- package/lib/cjs/workspace/SettingsSchemas.js +191 -191
- package/lib/cjs/workspace/SettingsSchemas.js.map +1 -1
- package/lib/cjs/workspace/Workspace.d.ts +414 -414
- package/lib/cjs/workspace/Workspace.js +543 -543
- package/lib/cjs/workspace/Workspace.js.map +1 -1
- package/package.json +12 -12
package/lib/cjs/IModelDb.js
CHANGED
|
@@ -1,2393 +1,2393 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*---------------------------------------------------------------------------------------------
|
|
3
|
-
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
-
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
-
*--------------------------------------------------------------------------------------------*/
|
|
6
|
-
/** @packageDocumentation
|
|
7
|
-
* @module iModels
|
|
8
|
-
*/
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.StandaloneDb = exports.SnapshotDb = exports.BriefcaseDb = exports.IModelDb = exports.BriefcaseLocalValue = void 0;
|
|
11
|
-
const path_1 = require("path");
|
|
12
|
-
const core_bentley_1 = require("@itwin/core-bentley");
|
|
13
|
-
const core_common_1 = require("@itwin/core-common");
|
|
14
|
-
const core_geometry_1 = require("@itwin/core-geometry");
|
|
15
|
-
const BackendLoggerCategory_1 = require("./BackendLoggerCategory");
|
|
16
|
-
const BriefcaseManager_1 = require("./BriefcaseManager");
|
|
17
|
-
const CheckpointManager_1 = require("./CheckpointManager");
|
|
18
|
-
const ClassRegistry_1 = require("./ClassRegistry");
|
|
19
|
-
const CodeService_1 = require("./CodeService");
|
|
20
|
-
const CodeSpecs_1 = require("./CodeSpecs");
|
|
21
|
-
const ConcurrentQuery_1 = require("./ConcurrentQuery");
|
|
22
|
-
const ECSqlStatement_1 = require("./ECSqlStatement");
|
|
23
|
-
const Element_1 = require("./Element");
|
|
24
|
-
const ElementAspect_1 = require("./ElementAspect");
|
|
25
|
-
const ElementGraphics_1 = require("./ElementGraphics");
|
|
26
|
-
const GeoCoordConfig_1 = require("./GeoCoordConfig");
|
|
27
|
-
const IModelHost_1 = require("./IModelHost");
|
|
28
|
-
const IModelJsFs_1 = require("./IModelJsFs");
|
|
29
|
-
const IpcHost_1 = require("./IpcHost");
|
|
30
|
-
const Model_1 = require("./Model");
|
|
31
|
-
const Relationship_1 = require("./Relationship");
|
|
32
|
-
const ServerBasedLocks_1 = require("./ServerBasedLocks");
|
|
33
|
-
const SqliteStatement_1 = require("./SqliteStatement");
|
|
34
|
-
const TxnManager_1 = require("./TxnManager");
|
|
35
|
-
const ViewDefinition_1 = require("./ViewDefinition");
|
|
36
|
-
const Settings_1 = require("./workspace/Settings");
|
|
37
|
-
const Workspace_1 = require("./workspace/Workspace");
|
|
38
|
-
// spell:ignore fontid fontmap
|
|
39
|
-
const loggerCategory = BackendLoggerCategory_1.BackendLoggerCategory.IModelDb;
|
|
40
|
-
/** A null-implementation of LockControl that does not attempt to limit access between briefcases. This relies on change-merging to resolve conflicts. */
|
|
41
|
-
class NoLocks {
|
|
42
|
-
get isServerBased() { return false; }
|
|
43
|
-
close() { }
|
|
44
|
-
clearAllLocks() { }
|
|
45
|
-
holdsExclusiveLock() { return false; }
|
|
46
|
-
holdsSharedLock() { return false; }
|
|
47
|
-
checkExclusiveLock() { }
|
|
48
|
-
checkSharedLock() { }
|
|
49
|
-
elementWasCreated() { }
|
|
50
|
-
async acquireLocks() { }
|
|
51
|
-
async releaseAllLocks() { }
|
|
52
|
-
}
|
|
53
|
-
/** @internal */
|
|
54
|
-
var BriefcaseLocalValue;
|
|
55
|
-
(function (BriefcaseLocalValue) {
|
|
56
|
-
BriefcaseLocalValue["StandaloneEdit"] = "StandaloneEdit";
|
|
57
|
-
BriefcaseLocalValue["NoLocking"] = "NoLocking";
|
|
58
|
-
})(BriefcaseLocalValue = exports.BriefcaseLocalValue || (exports.BriefcaseLocalValue = {}));
|
|
59
|
-
// function to open an briefcaseDb, perform an operation, and then close it.
|
|
60
|
-
const withBriefcaseDb = async (briefcase, fn) => {
|
|
61
|
-
const db = await BriefcaseDb.open(briefcase);
|
|
62
|
-
try {
|
|
63
|
-
return await fn(db);
|
|
64
|
-
}
|
|
65
|
-
finally {
|
|
66
|
-
db.close();
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
/**
|
|
70
|
-
* Settings for an individual iModel. May only include settings priority for iModel, iTwin and organization.
|
|
71
|
-
* @note if there is more than one iModel for an iTwin or organization, they will *each* hold a copy of the settings for those priorities.
|
|
72
|
-
*/
|
|
73
|
-
class IModelSettings extends Settings_1.BaseSettings {
|
|
74
|
-
verifyPriority(priority) {
|
|
75
|
-
if (priority <= Settings_1.SettingsPriority.application)
|
|
76
|
-
throw new Error("Use IModelHost.appSettings");
|
|
77
|
-
}
|
|
78
|
-
// attempt to resolve a setting from this iModel's settings, otherwise use appWorkspace's settings, otherwise defaultValue.
|
|
79
|
-
resolveSetting(name, resolver, defaultValue) {
|
|
80
|
-
var _a;
|
|
81
|
-
return (_a = super.resolveSetting(name, resolver)) !== null && _a !== void 0 ? _a : IModelHost_1.IModelHost.appWorkspace.settings.resolveSetting(name, resolver, defaultValue);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
/** An iModel database file. The database file can either be a briefcase or a snapshot.
|
|
85
|
-
* @see [Accessing iModels]($docs/learning/backend/AccessingIModels.md)
|
|
86
|
-
* @see [About IModelDb]($docs/learning/backend/IModelDb.md)
|
|
87
|
-
* @public
|
|
88
|
-
*/
|
|
89
|
-
class IModelDb extends core_common_1.IModel {
|
|
90
|
-
/** @internal */
|
|
91
|
-
constructor(args) {
|
|
92
|
-
super({ ...args, iTwinId: args.nativeDb.getITwinId(), iModelId: args.nativeDb.getIModelId() });
|
|
93
|
-
this._initialized = false;
|
|
94
|
-
this.models = new IModelDb.Models(this);
|
|
95
|
-
this.elements = new IModelDb.Elements(this);
|
|
96
|
-
this.views = new IModelDb.Views(this);
|
|
97
|
-
this.tiles = new IModelDb.Tiles(this);
|
|
98
|
-
this._statementCache = new SqliteStatement_1.StatementCache();
|
|
99
|
-
this._sqliteStatementCache = new SqliteStatement_1.StatementCache();
|
|
100
|
-
this._snaps = new Map();
|
|
101
|
-
/** @internal */
|
|
102
|
-
this._locks = new NoLocks();
|
|
103
|
-
/** Event called after a changeset is applied to this IModelDb. */
|
|
104
|
-
this.onChangesetApplied = new core_bentley_1.BeEvent();
|
|
105
|
-
/** Event called when the iModel is about to be closed. */
|
|
106
|
-
this.onBeforeClose = new core_bentley_1.BeEvent();
|
|
107
|
-
this._nativeDb = args.nativeDb;
|
|
108
|
-
this.nativeDb.setIModelDb(this);
|
|
109
|
-
this.loadSettingDictionaries();
|
|
110
|
-
GeoCoordConfig_1.GeoCoordConfig.loadForImodel(this.workspace.settings); // load gcs data specified by iModel's settings dictionaries, must be done before calling initializeIModelDb
|
|
111
|
-
this.initializeIModelDb();
|
|
112
|
-
IModelDb._openDbs.set(this._fileKey, this);
|
|
113
|
-
if (undefined === IModelDb._shutdownListener) { // the first time we create an IModelDb, add a listener to close any orphan files at shutdown.
|
|
114
|
-
IModelDb._shutdownListener = IModelHost_1.IModelHost.onBeforeShutdown.addListener(() => {
|
|
115
|
-
IModelDb._openDbs.forEach((db) => {
|
|
116
|
-
try {
|
|
117
|
-
db.abandonChanges();
|
|
118
|
-
db.close();
|
|
119
|
-
}
|
|
120
|
-
catch { }
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
/** @alpha */
|
|
126
|
-
get codeService() { return this._codeService; }
|
|
127
|
-
/**
|
|
128
|
-
* Get the [[LockControl]] for this iModel.
|
|
129
|
-
* @beta
|
|
130
|
-
*/
|
|
131
|
-
get locks() { return this._locks; } // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
132
|
-
/**
|
|
133
|
-
* Get the [[Workspace]] for this iModel.
|
|
134
|
-
* @beta
|
|
135
|
-
*/
|
|
136
|
-
get workspace() {
|
|
137
|
-
if (undefined === this._workspace)
|
|
138
|
-
this._workspace = new Workspace_1.ITwinWorkspace(new IModelSettings());
|
|
139
|
-
return this._workspace;
|
|
140
|
-
}
|
|
141
|
-
/** Acquire the exclusive schema lock on this iModel.
|
|
142
|
-
* > Note: To acquire the schema lock, all other briefcases must first release *all* their locks. No other briefcases
|
|
143
|
-
* will be able to acquire *any* locks while the schema lock is held.
|
|
144
|
-
*/
|
|
145
|
-
async acquireSchemaLock() {
|
|
146
|
-
return this.locks.acquireLocks({ exclusive: core_common_1.IModel.repositoryModelId });
|
|
147
|
-
}
|
|
148
|
-
/** determine whether the schema lock is currently held for this iModel. */
|
|
149
|
-
get holdsSchemaLock() {
|
|
150
|
-
return this.locks.holdsExclusiveLock(core_common_1.IModel.repositoryModelId);
|
|
151
|
-
}
|
|
152
|
-
/** @internal */
|
|
153
|
-
notifyChangesetApplied() {
|
|
154
|
-
this.changeset = this.nativeDb.getCurrentChangeset();
|
|
155
|
-
this.onChangesetApplied.raiseEvent();
|
|
156
|
-
}
|
|
157
|
-
get fontMap() {
|
|
158
|
-
var _a;
|
|
159
|
-
return (_a = this._fontMap) !== null && _a !== void 0 ? _a : (this._fontMap = new core_common_1.FontMap(this.nativeDb.readFontMap()));
|
|
160
|
-
}
|
|
161
|
-
/** @internal */
|
|
162
|
-
clearFontMap() {
|
|
163
|
-
this._fontMap = undefined;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Add a new font name/type to the FontMap for this iModel and return its FontId.
|
|
167
|
-
* @param name The name of the font to add
|
|
168
|
-
* @param type The type of the font. Default is TrueType.
|
|
169
|
-
* @returns The FontId for the newly added font. If a font by that name/type already exists, this method does not fail, it returns the existing Id.
|
|
170
|
-
* @see [FontId and FontMap]($docs/learning/backend/Fonts.md#fontid-and-fontmap)
|
|
171
|
-
* @beta
|
|
172
|
-
*/
|
|
173
|
-
addNewFont(name, type) {
|
|
174
|
-
this.locks.checkExclusiveLock(core_common_1.IModel.repositoryModelId, "schema", "addNewFont");
|
|
175
|
-
this.clearFontMap();
|
|
176
|
-
return this.nativeDb.addNewFont({ name, type: type !== null && type !== void 0 ? type : core_common_1.FontType.TrueType });
|
|
177
|
-
}
|
|
178
|
-
/** Check if this iModel has been opened read-only or not. */
|
|
179
|
-
get isReadonly() { return this.openMode === core_bentley_1.OpenMode.Readonly; }
|
|
180
|
-
/** The Guid that identifies this iModel. */
|
|
181
|
-
get iModelId() {
|
|
182
|
-
(0, core_bentley_1.assert)(undefined !== super.iModelId);
|
|
183
|
-
return super.iModelId;
|
|
184
|
-
} // GuidString | undefined for the IModel superclass, but required for all IModelDb subclasses
|
|
185
|
-
/** @internal*/
|
|
186
|
-
get nativeDb() { return this._nativeDb; } // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
187
|
-
/** Get the full path fileName of this iModelDb
|
|
188
|
-
* @note this member is only valid while the iModel is opened.
|
|
189
|
-
*/
|
|
190
|
-
get pathName() { return this.nativeDb.getFilePath(); }
|
|
191
|
-
/** Close this IModel, if it is currently open. */
|
|
192
|
-
close() {
|
|
193
|
-
var _a, _b;
|
|
194
|
-
if (!this.isOpen)
|
|
195
|
-
return; // don't continue if already closed
|
|
196
|
-
this.beforeClose();
|
|
197
|
-
IModelDb._openDbs.delete(this._fileKey);
|
|
198
|
-
(_a = this._workspace) === null || _a === void 0 ? void 0 : _a.close();
|
|
199
|
-
this.locks.close();
|
|
200
|
-
this._locks = undefined;
|
|
201
|
-
(_b = this._codeService) === null || _b === void 0 ? void 0 : _b.close();
|
|
202
|
-
this._codeService = undefined;
|
|
203
|
-
this.nativeDb.closeIModel();
|
|
204
|
-
this._nativeDb = undefined; // the underlying nativeDb has been freed by closeIModel
|
|
205
|
-
}
|
|
206
|
-
/** @internal */
|
|
207
|
-
async refreshContainerSas(_userAccessToken) { }
|
|
208
|
-
/**
|
|
209
|
-
* Called by derived classes before closing the connection
|
|
210
|
-
* @internal
|
|
211
|
-
*/
|
|
212
|
-
beforeClose() {
|
|
213
|
-
this.onBeforeClose.raiseEvent();
|
|
214
|
-
this.clearCaches();
|
|
215
|
-
}
|
|
216
|
-
/** @internal */
|
|
217
|
-
initializeIModelDb() {
|
|
218
|
-
const props = this.nativeDb.getIModelProps();
|
|
219
|
-
super.initialize(props.rootSubject.name, props);
|
|
220
|
-
if (this._initialized)
|
|
221
|
-
return;
|
|
222
|
-
this._initialized = true;
|
|
223
|
-
const db = this.isBriefcaseDb() ? this : undefined;
|
|
224
|
-
if (!db || !IpcHost_1.IpcHost.isValid)
|
|
225
|
-
return;
|
|
226
|
-
db.onNameChanged.addListener(() => IpcHost_1.IpcHost.notifyTxns(db, "notifyIModelNameChanged", db.name));
|
|
227
|
-
db.onRootSubjectChanged.addListener(() => IpcHost_1.IpcHost.notifyTxns(db, "notifyRootSubjectChanged", db.rootSubject));
|
|
228
|
-
db.onProjectExtentsChanged.addListener(() => IpcHost_1.IpcHost.notifyTxns(db, "notifyProjectExtentsChanged", db.projectExtents.toJSON()));
|
|
229
|
-
db.onGlobalOriginChanged.addListener(() => IpcHost_1.IpcHost.notifyTxns(db, "notifyGlobalOriginChanged", db.globalOrigin.toJSON()));
|
|
230
|
-
db.onEcefLocationChanged.addListener(() => { var _a; return IpcHost_1.IpcHost.notifyTxns(db, "notifyEcefLocationChanged", (_a = db.ecefLocation) === null || _a === void 0 ? void 0 : _a.toJSON()); });
|
|
231
|
-
db.onGeographicCoordinateSystemChanged.addListener(() => { var _a; return IpcHost_1.IpcHost.notifyTxns(db, "notifyGeographicCoordinateSystemChanged", (_a = db.geographicCoordinateSystem) === null || _a === void 0 ? void 0 : _a.toJSON()); });
|
|
232
|
-
}
|
|
233
|
-
/** Returns true if this is a BriefcaseDb
|
|
234
|
-
* @see [[BriefcaseDb.open]]
|
|
235
|
-
*/
|
|
236
|
-
get isBriefcase() { return false; }
|
|
237
|
-
/** Type guard for instanceof [[BriefcaseDb]] */
|
|
238
|
-
isBriefcaseDb() { return this.isBriefcase; }
|
|
239
|
-
/** Returns true if this is a SnapshotDb
|
|
240
|
-
* @see [[SnapshotDb.open]]
|
|
241
|
-
*/
|
|
242
|
-
get isSnapshot() { return false; }
|
|
243
|
-
/** Type guard for instanceof [[SnapshotDb]] */
|
|
244
|
-
isSnapshotDb() { return this.isSnapshot; }
|
|
245
|
-
/** Returns true if this is a *standalone* iModel
|
|
246
|
-
* @see [[StandaloneDb.open]]
|
|
247
|
-
* @internal
|
|
248
|
-
*/
|
|
249
|
-
get isStandalone() { return false; }
|
|
250
|
-
/** Type guard for instanceof [[StandaloneDb]]
|
|
251
|
-
* @internal
|
|
252
|
-
*/
|
|
253
|
-
isStandaloneDb() { return this.isStandalone; }
|
|
254
|
-
/** Return `true` if the underlying nativeDb is open and valid.
|
|
255
|
-
* @internal
|
|
256
|
-
*/
|
|
257
|
-
get isOpen() { return undefined !== this.nativeDb; }
|
|
258
|
-
/** Get the briefcase Id of this iModel */
|
|
259
|
-
getBriefcaseId() { return this.isOpen ? this.nativeDb.getBriefcaseId() : core_common_1.BriefcaseIdValue.Illegal; }
|
|
260
|
-
/**
|
|
261
|
-
* Use a prepared ECSQL statement, potentially from the statement cache. If the requested statement doesn't exist
|
|
262
|
-
* in the statement cache, a new statement is prepared. After the callback completes, the statement is reset and saved
|
|
263
|
-
* in the statement cache so it can be reused in the future. Use this method for ECSQL statements that will be
|
|
264
|
-
* reused often and are expensive to prepare. The statement cache holds the most recently used statements, discarding
|
|
265
|
-
* the oldest statements as it fills. For statements you don't intend to reuse, instead use [[withStatement]].
|
|
266
|
-
* @param sql The SQLite SQL statement to execute
|
|
267
|
-
* @param callback the callback to invoke on the prepared statement
|
|
268
|
-
* @param logErrors Determines if error will be logged if statement fail to prepare
|
|
269
|
-
* @returns the value returned by `callback`.
|
|
270
|
-
* @see [[withStatement]]
|
|
271
|
-
* @public
|
|
272
|
-
*/
|
|
273
|
-
withPreparedStatement(ecsql, callback, logErrors = true) {
|
|
274
|
-
var _a;
|
|
275
|
-
const stmt = (_a = this._statementCache.findAndRemove(ecsql)) !== null && _a !== void 0 ? _a : this.prepareStatement(ecsql, logErrors);
|
|
276
|
-
const release = () => this._statementCache.addOrDispose(stmt);
|
|
277
|
-
try {
|
|
278
|
-
const val = callback(stmt);
|
|
279
|
-
if (val instanceof Promise) {
|
|
280
|
-
val.then(release, release);
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
release();
|
|
284
|
-
}
|
|
285
|
-
return val;
|
|
286
|
-
}
|
|
287
|
-
catch (err) {
|
|
288
|
-
release();
|
|
289
|
-
throw err;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Prepared and execute a callback on an ECSQL statement. After the callback completes the statement is disposed.
|
|
294
|
-
* Use this method for ECSQL statements are either not expected to be reused, or are not expensive to prepare.
|
|
295
|
-
* For statements that will be reused often, instead use [[withPreparedStatement]].
|
|
296
|
-
* @param sql The SQLite SQL statement to execute
|
|
297
|
-
* @param callback the callback to invoke on the prepared statement
|
|
298
|
-
* @param logErrors Determines if error will be logged if statement fail to prepare
|
|
299
|
-
* @returns the value returned by `callback`.
|
|
300
|
-
* @see [[withPreparedStatement]]
|
|
301
|
-
* @public
|
|
302
|
-
*/
|
|
303
|
-
withStatement(ecsql, callback, logErrors = true) {
|
|
304
|
-
const stmt = this.prepareStatement(ecsql, logErrors);
|
|
305
|
-
const release = () => stmt.dispose();
|
|
306
|
-
try {
|
|
307
|
-
const val = callback(stmt);
|
|
308
|
-
if (val instanceof Promise) {
|
|
309
|
-
val.then(release, release);
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
release();
|
|
313
|
-
}
|
|
314
|
-
return val;
|
|
315
|
-
}
|
|
316
|
-
catch (err) {
|
|
317
|
-
release();
|
|
318
|
-
throw err;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
/** Allow to execute query and read results along with meta data. The result are streamed.
|
|
322
|
-
* @param params The values to bind to the parameters (if the ECSQL has any).
|
|
323
|
-
* @param config Allow to specify certain flags which control how query is executed.
|
|
324
|
-
* @returns Returns an [ECSqlReader]($common) which helps iterate over the result set and also give access to metadata.
|
|
325
|
-
* @beta
|
|
326
|
-
* */
|
|
327
|
-
createQueryReader(ecsql, params, config) {
|
|
328
|
-
if (!this._nativeDb || !this._nativeDb.isOpen()) {
|
|
329
|
-
throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "db not open");
|
|
330
|
-
}
|
|
331
|
-
const executor = {
|
|
332
|
-
execute: async (request) => {
|
|
333
|
-
return ConcurrentQuery_1.ConcurrentQuery.executeQueryRequest(this.nativeDb, request);
|
|
334
|
-
},
|
|
335
|
-
};
|
|
336
|
-
return new core_common_1.ECSqlReader(executor, ecsql, params, config);
|
|
337
|
-
}
|
|
338
|
-
/** Execute a query and stream its results
|
|
339
|
-
* The result of the query is async iterator over the rows. The iterator will get next page automatically once rows in current page has been read.
|
|
340
|
-
* [ECSQL row]($docs/learning/ECSQLRowFormat).
|
|
341
|
-
*
|
|
342
|
-
* See also:
|
|
343
|
-
* - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL)
|
|
344
|
-
* - [Code Examples]($docs/learning/backend/ECSQLCodeExamples)
|
|
345
|
-
*
|
|
346
|
-
* @param ecsql The ECSQL statement to execute
|
|
347
|
-
* @param params The values to bind to the parameters (if the ECSQL has any).
|
|
348
|
-
* @param options Allow to specify certain flags which control how query is executed.
|
|
349
|
-
* @returns Returns the query result as an *AsyncIterableIterator<any>* which lazy load result as needed. The row format is determined by *rowFormat* parameter.
|
|
350
|
-
* See [ECSQL row format]($docs/learning/ECSQLRowFormat) for details about the format of the returned rows.
|
|
351
|
-
* @throws [IModelError]($common) If there was any error while submitting, preparing or stepping into query
|
|
352
|
-
* @deprecated in 3.7. Use [[createQueryReader]] instead; it accepts the same parameters.
|
|
353
|
-
*/
|
|
354
|
-
async *query(ecsql, params, options) {
|
|
355
|
-
const builder = new core_common_1.QueryOptionsBuilder(options);
|
|
356
|
-
const reader = this.createQueryReader(ecsql, params, builder.getOptions());
|
|
357
|
-
while (await reader.step())
|
|
358
|
-
yield reader.formatCurrentRow();
|
|
359
|
-
}
|
|
360
|
-
/** Compute number of rows that would be returned by the ECSQL.
|
|
361
|
-
*
|
|
362
|
-
* See also:
|
|
363
|
-
* - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL)
|
|
364
|
-
* - [Code Examples]($docs/learning/backend/ECSQLCodeExamples)
|
|
365
|
-
*
|
|
366
|
-
* @param ecsql The ECSQL statement to execute
|
|
367
|
-
* @param params The values to bind to the parameters (if the ECSQL has any).
|
|
368
|
-
* See "[iTwin.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" for details.
|
|
369
|
-
* @returns Return row count.
|
|
370
|
-
* @throws [IModelError]($common) If the statement is invalid
|
|
371
|
-
* @deprecated in 3.7. Count the number of results using `count(*)` where the original query is a subquery instead. E.g., `SELECT count(*) FROM (<query-whose-rows-to-count>)`.
|
|
372
|
-
*/
|
|
373
|
-
async queryRowCount(ecsql, params) {
|
|
374
|
-
// eslint-disable-next-line deprecation/deprecation
|
|
375
|
-
for await (const row of this.query(`select count(*) from (${ecsql})`, params)) {
|
|
376
|
-
return row[0];
|
|
377
|
-
}
|
|
378
|
-
throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "Failed to get row count");
|
|
379
|
-
}
|
|
380
|
-
/** Cancel any previous query with same token and run execute the current specified query.
|
|
381
|
-
* The result of the query is async iterator over the rows. The iterator will get next page automatically once rows in current page has been read.
|
|
382
|
-
* [ECSQL row]($docs/learning/ECSQLRowFormat).
|
|
383
|
-
*
|
|
384
|
-
* See also:
|
|
385
|
-
* - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL)
|
|
386
|
-
* - [Code Examples]($docs/learning/backend/ECSQLCodeExamples)
|
|
387
|
-
*
|
|
388
|
-
* @param ecsql The ECSQL statement to execute
|
|
389
|
-
* @param token None empty restart token. The previous query with same token would be cancelled. This would cause
|
|
390
|
-
* exception which user code must handle.
|
|
391
|
-
* @param params The values to bind to the parameters (if the ECSQL has any).
|
|
392
|
-
* @param options Allow to specify certain flags which control how query is executed.
|
|
393
|
-
* @returns Returns the query result as an *AsyncIterableIterator<any>* which lazy load result as needed. The row format is determined by *rowFormat* parameter.
|
|
394
|
-
* See [ECSQL row format]($docs/learning/ECSQLRowFormat) for details about the format of the returned rows.
|
|
395
|
-
* @throws [IModelError]($common) If there was any error while submitting, preparing or stepping into query
|
|
396
|
-
* @deprecated in 3.7. Use [[createQueryReader]] instead. Pass in the restart token as part of the `config` argument; e.g., `{ restartToken: myToken }` or `new QueryOptionsBuilder().setRestartToken(myToken).getOptions()`.
|
|
397
|
-
*/
|
|
398
|
-
async *restartQuery(token, ecsql, params, options) {
|
|
399
|
-
// eslint-disable-next-line deprecation/deprecation
|
|
400
|
-
for await (const row of this.query(ecsql, params, new core_common_1.QueryOptionsBuilder(options).setRestartToken(token).getOptions())) {
|
|
401
|
-
yield row;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* Use a prepared SQL statement, potentially from the statement cache. If the requested statement doesn't exist
|
|
406
|
-
* in the statement cache, a new statement is prepared. After the callback completes, the statement is reset and saved
|
|
407
|
-
* in the statement cache so it can be reused in the future. Use this method for SQL statements that will be
|
|
408
|
-
* reused often and are expensive to prepare. The statement cache holds the most recently used statements, discarding
|
|
409
|
-
* the oldest statements as it fills. For statements you don't intend to reuse, instead use [[withSqliteStatement]].
|
|
410
|
-
* @param sql The SQLite SQL statement to execute
|
|
411
|
-
* @param callback the callback to invoke on the prepared statement
|
|
412
|
-
* @param logErrors Determine if errors are logged or not
|
|
413
|
-
* @returns the value returned by `callback`.
|
|
414
|
-
* @see [[withPreparedStatement]]
|
|
415
|
-
* @public
|
|
416
|
-
*/
|
|
417
|
-
withPreparedSqliteStatement(sql, callback, logErrors = true) {
|
|
418
|
-
var _a;
|
|
419
|
-
const stmt = (_a = this._sqliteStatementCache.findAndRemove(sql)) !== null && _a !== void 0 ? _a : this.prepareSqliteStatement(sql, logErrors);
|
|
420
|
-
const release = () => this._sqliteStatementCache.addOrDispose(stmt);
|
|
421
|
-
try {
|
|
422
|
-
const val = callback(stmt);
|
|
423
|
-
if (val instanceof Promise) {
|
|
424
|
-
val.then(release, release);
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
release();
|
|
428
|
-
}
|
|
429
|
-
return val;
|
|
430
|
-
}
|
|
431
|
-
catch (err) {
|
|
432
|
-
release();
|
|
433
|
-
throw err;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* Prepared and execute a callback on a SQL statement. After the callback completes the statement is disposed.
|
|
438
|
-
* Use this method for SQL statements are either not expected to be reused, or are not expensive to prepare.
|
|
439
|
-
* For statements that will be reused often, instead use [[withPreparedSqliteStatement]].
|
|
440
|
-
* @param sql The SQLite SQL statement to execute
|
|
441
|
-
* @param callback the callback to invoke on the prepared statement
|
|
442
|
-
* @param logErrors Determine if errors are logged or not
|
|
443
|
-
* @returns the value returned by `callback`.
|
|
444
|
-
* @public
|
|
445
|
-
*/
|
|
446
|
-
withSqliteStatement(sql, callback, logErrors = true) {
|
|
447
|
-
const stmt = this.prepareSqliteStatement(sql, logErrors);
|
|
448
|
-
const release = () => stmt.dispose();
|
|
449
|
-
try {
|
|
450
|
-
const val = callback(stmt);
|
|
451
|
-
if (val instanceof Promise) {
|
|
452
|
-
val.then(release, release);
|
|
453
|
-
}
|
|
454
|
-
else {
|
|
455
|
-
release();
|
|
456
|
-
}
|
|
457
|
-
return val;
|
|
458
|
-
}
|
|
459
|
-
catch (err) {
|
|
460
|
-
release();
|
|
461
|
-
throw err;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
/** Prepare an SQL statement.
|
|
465
|
-
* @param sql The SQL statement to prepare
|
|
466
|
-
* @throws [[IModelError]] if there is a problem preparing the statement.
|
|
467
|
-
* @internal
|
|
468
|
-
*/
|
|
469
|
-
prepareSqliteStatement(sql, logErrors = true) {
|
|
470
|
-
const stmt = new SqliteStatement_1.SqliteStatement(sql);
|
|
471
|
-
stmt.prepare(this.nativeDb, logErrors);
|
|
472
|
-
return stmt;
|
|
473
|
-
}
|
|
474
|
-
/**
|
|
475
|
-
* queries the BisCore.SubCategory table for the entries that are children of the passed categoryIds
|
|
476
|
-
* @param categoryIds categoryIds to query
|
|
477
|
-
* @returns array of SubCategoryResultRow
|
|
478
|
-
* @internal
|
|
479
|
-
*/
|
|
480
|
-
async querySubCategories(categoryIds) {
|
|
481
|
-
const result = [];
|
|
482
|
-
const where = [...categoryIds].join(",");
|
|
483
|
-
const query = `SELECT ECInstanceId as id, Parent.Id as parentId, Properties as appearance FROM BisCore.SubCategory WHERE Parent.Id IN (${where})`;
|
|
484
|
-
try {
|
|
485
|
-
// eslint-disable-next-line deprecation/deprecation
|
|
486
|
-
for await (const row of this.query(query, undefined, { rowFormat: core_common_1.QueryRowFormat.UseJsPropertyNames })) {
|
|
487
|
-
result.push(row);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
catch {
|
|
491
|
-
// We can ignore the error here, and just return whatever we were able to query.
|
|
492
|
-
}
|
|
493
|
-
return result;
|
|
494
|
-
}
|
|
495
|
-
/** Query for a set of entity ids, given an EntityQueryParams
|
|
496
|
-
* @param params The query parameters. The `limit` and `offset` members should be used to page results.
|
|
497
|
-
* @returns an Id64Set with results of query
|
|
498
|
-
* @throws [[IModelError]] if the generated statement is invalid or [IModelDb.maxLimit]($backend) exceeded when collecting ids.
|
|
499
|
-
*
|
|
500
|
-
* *Example:*
|
|
501
|
-
* ``` ts
|
|
502
|
-
* [[include:ECSQL-backend-queries.select-element-by-code-value-using-queryEntityIds]]
|
|
503
|
-
* ```
|
|
504
|
-
*/
|
|
505
|
-
queryEntityIds(params) {
|
|
506
|
-
let sql = "SELECT ECInstanceId FROM ";
|
|
507
|
-
if (params.only)
|
|
508
|
-
sql += "ONLY ";
|
|
509
|
-
sql += params.from;
|
|
510
|
-
if (params.where)
|
|
511
|
-
sql += ` WHERE ${params.where}`;
|
|
512
|
-
if (params.orderBy)
|
|
513
|
-
sql += ` ORDER BY ${params.orderBy}`;
|
|
514
|
-
if (typeof params.limit === "number" && params.limit > 0)
|
|
515
|
-
sql += ` LIMIT ${params.limit}`;
|
|
516
|
-
if (typeof params.offset === "number" && params.offset > 0)
|
|
517
|
-
sql += ` OFFSET ${params.offset}`;
|
|
518
|
-
const ids = new Set();
|
|
519
|
-
this.withPreparedStatement(sql, (stmt) => {
|
|
520
|
-
if (params.bindings)
|
|
521
|
-
stmt.bindValues(params.bindings);
|
|
522
|
-
for (const row of stmt) {
|
|
523
|
-
if (row.id !== undefined) {
|
|
524
|
-
ids.add(row.id);
|
|
525
|
-
if (ids.size > IModelDb.maxLimit) {
|
|
526
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "Max LIMIT exceeded in SELECT statement");
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
});
|
|
531
|
-
return ids;
|
|
532
|
-
}
|
|
533
|
-
/** Clear all in-memory caches held in this IModelDb. */
|
|
534
|
-
clearCaches() {
|
|
535
|
-
this._statementCache.clear();
|
|
536
|
-
this._sqliteStatementCache.clear();
|
|
537
|
-
}
|
|
538
|
-
/** Update the project extents for this iModel.
|
|
539
|
-
* <p><em>Example:</em>
|
|
540
|
-
* ``` ts
|
|
541
|
-
* [[include:IModelDb.updateProjectExtents]]
|
|
542
|
-
* ```
|
|
543
|
-
*/
|
|
544
|
-
updateProjectExtents(newExtents) {
|
|
545
|
-
this.projectExtents = newExtents;
|
|
546
|
-
this.updateIModelProps();
|
|
547
|
-
}
|
|
548
|
-
/** Compute an appropriate project extents for this iModel based on the ranges of all spatial elements.
|
|
549
|
-
* Typically, the result is simply the union of the ranges of all spatial elements. However, the algorithm also detects "outlier elements",
|
|
550
|
-
* whose placements locate them so far from the rest of the spatial geometry that they are considered statistically insignificant. The
|
|
551
|
-
* range of an outlier element does not contribute to the computed extents.
|
|
552
|
-
* @param options Specifies the level of detail desired in the return value.
|
|
553
|
-
* @returns the computed extents.
|
|
554
|
-
* @note This method does not modify the IModel's stored project extents. @see [[updateProjectExtents]].
|
|
555
|
-
*/
|
|
556
|
-
computeProjectExtents(options) {
|
|
557
|
-
const wantFullExtents = true === (options === null || options === void 0 ? void 0 : options.reportExtentsWithOutliers);
|
|
558
|
-
const wantOutliers = true === (options === null || options === void 0 ? void 0 : options.reportOutliers);
|
|
559
|
-
const result = this.nativeDb.computeProjectExtents(wantFullExtents, wantOutliers);
|
|
560
|
-
return {
|
|
561
|
-
extents: core_geometry_1.Range3d.fromJSON(result.extents),
|
|
562
|
-
extentsWithOutliers: result.fullExtents ? core_geometry_1.Range3d.fromJSON(result.fullExtents) : undefined,
|
|
563
|
-
outliers: result.outliers,
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
/** Update the [EcefLocation]($docs/learning/glossary#eceflocation) of this iModel. */
|
|
567
|
-
updateEcefLocation(ecef) {
|
|
568
|
-
this.setEcefLocation(ecef);
|
|
569
|
-
this.updateIModelProps();
|
|
570
|
-
}
|
|
571
|
-
/** Update the IModelProps of this iModel in the database. */
|
|
572
|
-
updateIModelProps() {
|
|
573
|
-
this.nativeDb.updateIModelProps(this.toJSON());
|
|
574
|
-
}
|
|
575
|
-
/** Commit pending changes to this iModel.
|
|
576
|
-
* @param description Optional description of the changes
|
|
577
|
-
* @throws [[IModelError]] if there is a problem saving changes or if there are pending, un-processed lock or code requests.
|
|
578
|
-
*/
|
|
579
|
-
saveChanges(description) {
|
|
580
|
-
if (this.openMode === core_bentley_1.OpenMode.Readonly)
|
|
581
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.ReadOnly, "IModelDb was opened read-only");
|
|
582
|
-
const stat = this.nativeDb.saveChanges(description);
|
|
583
|
-
if (core_bentley_1.DbResult.BE_SQLITE_OK !== stat)
|
|
584
|
-
throw new core_common_1.IModelError(stat, `Could not save changes (${description})`);
|
|
585
|
-
}
|
|
586
|
-
/** Abandon pending changes in this iModel. */
|
|
587
|
-
abandonChanges() {
|
|
588
|
-
this.nativeDb.abandonChanges();
|
|
589
|
-
}
|
|
590
|
-
/**
|
|
591
|
-
* Save all changes and perform a [checkpoint](https://www.sqlite.org/c3ref/wal_checkpoint_v2.html) on this IModelDb.
|
|
592
|
-
* This ensures that all changes to the database since it was opened are saved to its file and the WAL file is truncated.
|
|
593
|
-
* @note Checkpoint automatically happens when IModelDbs are closed. However, the checkpoint
|
|
594
|
-
* operation itself can take some time. It may be useful to call this method prior to closing so that the checkpoint "penalty" is paid earlier.
|
|
595
|
-
* @note Another use for this function is to permit the file to be copied while it is open for write. iModel files should
|
|
596
|
-
* rarely be copied, and even less so while they're opened. But this scenario is sometimes encountered for tests.
|
|
597
|
-
*/
|
|
598
|
-
performCheckpoint() {
|
|
599
|
-
if (!this.isReadonly) {
|
|
600
|
-
this.saveChanges();
|
|
601
|
-
this.nativeDb.performCheckpoint();
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
/** @internal */
|
|
605
|
-
reverseTxns(numOperations) {
|
|
606
|
-
return this.nativeDb.reverseTxns(numOperations);
|
|
607
|
-
}
|
|
608
|
-
/** @internal */
|
|
609
|
-
reinstateTxn() {
|
|
610
|
-
return this.nativeDb.reinstateTxn();
|
|
611
|
-
}
|
|
612
|
-
/** @internal */
|
|
613
|
-
restartTxnSession() {
|
|
614
|
-
return this.nativeDb.restartTxnSession();
|
|
615
|
-
}
|
|
616
|
-
/** Import an ECSchema. On success, the schema definition is stored in the iModel.
|
|
617
|
-
* This method is asynchronous (must be awaited) because, in the case where this IModelDb is a briefcase, this method first obtains the schema lock from the iModel server.
|
|
618
|
-
* You must import a schema into an iModel before you can insert instances of the classes in that schema. See [[Element]]
|
|
619
|
-
* @param schemaFileName array of Full paths to ECSchema.xml files to be imported.
|
|
620
|
-
* @param {SchemaImportOptions} options - options during schema import.
|
|
621
|
-
* @throws [[IModelError]] if the schema lock cannot be obtained or there is a problem importing the schema.
|
|
622
|
-
* @note Changes are saved if importSchemas is successful and abandoned if not successful.
|
|
623
|
-
* @see querySchemaVersion
|
|
624
|
-
*/
|
|
625
|
-
async importSchemas(schemaFileNames, options) {
|
|
626
|
-
var _a;
|
|
627
|
-
if (this.nativeDb.getITwinId() !== core_bentley_1.Guid.empty) // if this iModel is associated with an iTwin, importing schema requires the schema lock
|
|
628
|
-
await this.acquireSchemaLock();
|
|
629
|
-
const maybeCustomNativeContext = (_a = options === null || options === void 0 ? void 0 : options.ecSchemaXmlContext) === null || _a === void 0 ? void 0 : _a.nativeContext;
|
|
630
|
-
const nativeImportOptions = {
|
|
631
|
-
schemaLockHeld: true,
|
|
632
|
-
ecSchemaXmlContext: maybeCustomNativeContext,
|
|
633
|
-
};
|
|
634
|
-
const stat = this.nativeDb.importSchemas(schemaFileNames, nativeImportOptions);
|
|
635
|
-
if (core_bentley_1.DbResult.BE_SQLITE_OK !== stat) {
|
|
636
|
-
throw new core_common_1.IModelError(stat, "Error importing schema");
|
|
637
|
-
}
|
|
638
|
-
this.clearCaches();
|
|
639
|
-
}
|
|
640
|
-
/** Import ECSchema(s) serialized to XML. On success, the schema definition is stored in the iModel.
|
|
641
|
-
* This method is asynchronous (must be awaited) because, in the case where this IModelDb is a briefcase, this method first obtains the schema lock from the iModel server.
|
|
642
|
-
* You must import a schema into an iModel before you can insert instances of the classes in that schema. See [[Element]]
|
|
643
|
-
* @param serializedXmlSchemas The xml string(s) created from a serialized ECSchema.
|
|
644
|
-
* @throws [[IModelError]] if the schema lock cannot be obtained or there is a problem importing the schema.
|
|
645
|
-
* @note Changes are saved if importSchemaStrings is successful and abandoned if not successful.
|
|
646
|
-
* @see querySchemaVersion
|
|
647
|
-
* @alpha
|
|
648
|
-
*/
|
|
649
|
-
async importSchemaStrings(serializedXmlSchemas) {
|
|
650
|
-
if (this.iTwinId && this.iTwinId !== core_bentley_1.Guid.empty) // if this iModel is associated with an iTwin, importing schema requires the schema lock
|
|
651
|
-
await this.acquireSchemaLock();
|
|
652
|
-
const stat = this.nativeDb.importXmlSchemas(serializedXmlSchemas, { schemaLockHeld: true });
|
|
653
|
-
if (core_bentley_1.DbResult.BE_SQLITE_OK !== stat)
|
|
654
|
-
throw new core_common_1.IModelError(stat, "Error importing schema");
|
|
655
|
-
this.clearCaches();
|
|
656
|
-
}
|
|
657
|
-
/** Find an opened instance of any subclass of IModelDb, by filename
|
|
658
|
-
* @note this method returns an IModelDb if the filename is open for *any* subclass of IModelDb
|
|
659
|
-
*/
|
|
660
|
-
static findByFilename(fileName) {
|
|
661
|
-
for (const entry of this._openDbs) {
|
|
662
|
-
if (entry[1].pathName === fileName)
|
|
663
|
-
return entry[1];
|
|
664
|
-
}
|
|
665
|
-
return undefined;
|
|
666
|
-
}
|
|
667
|
-
/** Find an open IModelDb by its key.
|
|
668
|
-
* @note This method is mainly for use by RPC implementations.
|
|
669
|
-
* @throws [[IModelNotFoundResponse]] if an open IModelDb matching the key is not found.
|
|
670
|
-
* @see [IModel.key]($common)
|
|
671
|
-
*/
|
|
672
|
-
static findByKey(key) {
|
|
673
|
-
const iModelDb = this.tryFindByKey(key);
|
|
674
|
-
if (undefined === iModelDb)
|
|
675
|
-
throw new core_common_1.IModelNotFoundResponse(); // a very specific status for the RpcManager
|
|
676
|
-
return iModelDb;
|
|
677
|
-
}
|
|
678
|
-
/** Attempt to find an open IModelDb by key.
|
|
679
|
-
* @returns The matching IModelDb or `undefined`.
|
|
680
|
-
*/
|
|
681
|
-
static tryFindByKey(key) {
|
|
682
|
-
return this._openDbs.get(key);
|
|
683
|
-
}
|
|
684
|
-
/** @internal */
|
|
685
|
-
static openDgnDb(file, openMode, upgradeOptions, props) {
|
|
686
|
-
var _a;
|
|
687
|
-
file.key = (_a = file.key) !== null && _a !== void 0 ? _a : core_bentley_1.Guid.createValue();
|
|
688
|
-
if (this.tryFindByKey(file.key))
|
|
689
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.AlreadyOpen, `key [${file.key}] for file [${file.path}] is already in use`);
|
|
690
|
-
const isUpgradeRequested = (upgradeOptions === null || upgradeOptions === void 0 ? void 0 : upgradeOptions.domain) === core_common_1.DomainOptions.Upgrade || (upgradeOptions === null || upgradeOptions === void 0 ? void 0 : upgradeOptions.profile) === core_common_1.ProfileOptions.Upgrade;
|
|
691
|
-
if (isUpgradeRequested && openMode !== core_bentley_1.OpenMode.ReadWrite)
|
|
692
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.UpgradeFailed, "Cannot upgrade a Readonly Db");
|
|
693
|
-
try {
|
|
694
|
-
const nativeDb = new IModelHost_1.IModelHost.platform.DgnDb();
|
|
695
|
-
nativeDb.openIModel(file.path, openMode, upgradeOptions, props, props === null || props === void 0 ? void 0 : props.container);
|
|
696
|
-
return nativeDb;
|
|
697
|
-
}
|
|
698
|
-
catch (err) {
|
|
699
|
-
throw new core_common_1.IModelError(err.errorNumber, `${err.message}, ${file.path}`);
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
/**
|
|
703
|
-
* Determines if the schemas in the Db must or can be upgraded by comparing them with those included in the
|
|
704
|
-
* current version of the software.
|
|
705
|
-
* @param filePath Full name of the briefcase including path
|
|
706
|
-
* @param forReadWrite Pass true if validating for read-write scenarios - note that the schema version requirements
|
|
707
|
-
* for opening the DgnDb read-write is more stringent than when opening the database read-only
|
|
708
|
-
* @throws [[IModelError]] If the Db was in an invalid state and that causes a problem with validating schemas
|
|
709
|
-
* @see [[BriefcaseDb.upgradeSchemas]] or [[StandaloneDb.upgradeSchemas]]
|
|
710
|
-
* @see ($docs/learning/backend/IModelDb.md#upgrading-schemas-in-an-imodel)
|
|
711
|
-
*/
|
|
712
|
-
static validateSchemas(filePath, forReadWrite) {
|
|
713
|
-
const openMode = forReadWrite ? core_bentley_1.OpenMode.ReadWrite : core_bentley_1.OpenMode.Readonly;
|
|
714
|
-
const file = { path: filePath };
|
|
715
|
-
let result = core_bentley_1.DbResult.BE_SQLITE_OK;
|
|
716
|
-
try {
|
|
717
|
-
const upgradeOptions = {
|
|
718
|
-
domain: core_common_1.DomainOptions.CheckRecommendedUpgrades,
|
|
719
|
-
};
|
|
720
|
-
const nativeDb = this.openDgnDb(file, openMode, upgradeOptions);
|
|
721
|
-
nativeDb.closeIModel();
|
|
722
|
-
}
|
|
723
|
-
catch (err) {
|
|
724
|
-
result = err.errorNumber;
|
|
725
|
-
}
|
|
726
|
-
let schemaState = core_common_1.SchemaState.UpToDate;
|
|
727
|
-
switch (result) {
|
|
728
|
-
case core_bentley_1.DbResult.BE_SQLITE_OK:
|
|
729
|
-
schemaState = core_common_1.SchemaState.UpToDate;
|
|
730
|
-
break;
|
|
731
|
-
case core_bentley_1.DbResult.BE_SQLITE_ERROR_ProfileTooOld:
|
|
732
|
-
case core_bentley_1.DbResult.BE_SQLITE_ERROR_ProfileTooOldForReadWrite:
|
|
733
|
-
case core_bentley_1.DbResult.BE_SQLITE_ERROR_SchemaTooOld:
|
|
734
|
-
schemaState = core_common_1.SchemaState.TooOld;
|
|
735
|
-
break;
|
|
736
|
-
case core_bentley_1.DbResult.BE_SQLITE_ERROR_ProfileTooNew:
|
|
737
|
-
case core_bentley_1.DbResult.BE_SQLITE_ERROR_ProfileTooNewForReadWrite:
|
|
738
|
-
case core_bentley_1.DbResult.BE_SQLITE_ERROR_SchemaTooNew:
|
|
739
|
-
schemaState = core_common_1.SchemaState.TooNew;
|
|
740
|
-
break;
|
|
741
|
-
case core_bentley_1.DbResult.BE_SQLITE_ERROR_SchemaUpgradeRecommended:
|
|
742
|
-
schemaState = core_common_1.SchemaState.UpgradeRecommended;
|
|
743
|
-
break;
|
|
744
|
-
case core_bentley_1.DbResult.BE_SQLITE_ERROR_SchemaUpgradeRequired:
|
|
745
|
-
schemaState = core_common_1.SchemaState.UpgradeRequired;
|
|
746
|
-
break;
|
|
747
|
-
case core_bentley_1.DbResult.BE_SQLITE_ERROR_InvalidProfileVersion:
|
|
748
|
-
throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR_InvalidProfileVersion, "The profile of the Db is invalid. Cannot upgrade or open the Db.");
|
|
749
|
-
default:
|
|
750
|
-
throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "Error validating schemas. Cannot upgrade or open the Db.");
|
|
751
|
-
}
|
|
752
|
-
return schemaState;
|
|
753
|
-
}
|
|
754
|
-
/** Get the ClassMetaDataRegistry for this iModel.
|
|
755
|
-
* @internal
|
|
756
|
-
*/
|
|
757
|
-
get classMetaDataRegistry() {
|
|
758
|
-
if (this._classMetaDataRegistry === undefined)
|
|
759
|
-
this._classMetaDataRegistry = new ClassRegistry_1.MetaDataRegistry();
|
|
760
|
-
return this._classMetaDataRegistry;
|
|
761
|
-
}
|
|
762
|
-
/** Get the linkTableRelationships for this IModel */
|
|
763
|
-
get relationships() {
|
|
764
|
-
return this._relationships || (this._relationships = new Relationship_1.Relationships(this));
|
|
765
|
-
}
|
|
766
|
-
/** Get the CodeSpecs in this IModel. */
|
|
767
|
-
get codeSpecs() {
|
|
768
|
-
return (this._codeSpecs !== undefined) ? this._codeSpecs : (this._codeSpecs = new CodeSpecs_1.CodeSpecs(this));
|
|
769
|
-
}
|
|
770
|
-
/** @internal */
|
|
771
|
-
insertCodeSpec(codeSpec) {
|
|
772
|
-
return this.nativeDb.insertCodeSpec(codeSpec.name, codeSpec.properties); // TODO: Remove "as any" when NativeLibrary.ts is updated so "spec" isn't marked as required
|
|
773
|
-
}
|
|
774
|
-
/** Prepare an ECSQL statement.
|
|
775
|
-
* @param sql The ECSQL statement to prepare
|
|
776
|
-
* @param logErrors Determines if error will be logged if statement fail to prepare
|
|
777
|
-
* @throws [[IModelError]] if there is a problem preparing the statement.
|
|
778
|
-
*/
|
|
779
|
-
prepareStatement(sql, logErrors = true) {
|
|
780
|
-
const stmt = new ECSqlStatement_1.ECSqlStatement();
|
|
781
|
-
stmt.prepare(this.nativeDb, sql, logErrors);
|
|
782
|
-
return stmt;
|
|
783
|
-
}
|
|
784
|
-
/** Prepare an ECSQL statement.
|
|
785
|
-
* @param sql The ECSQL statement to prepare
|
|
786
|
-
* @returns `undefined` if there is a problem preparing the statement.
|
|
787
|
-
*/
|
|
788
|
-
tryPrepareStatement(sql) {
|
|
789
|
-
const statement = new ECSqlStatement_1.ECSqlStatement();
|
|
790
|
-
const result = statement.tryPrepare(this.nativeDb, sql);
|
|
791
|
-
return core_bentley_1.DbResult.BE_SQLITE_OK === result.status ? statement : undefined;
|
|
792
|
-
}
|
|
793
|
-
/** Construct an entity (Element or Model) from an iModel.
|
|
794
|
-
* @throws [[IModelError]] if the entity cannot be constructed.
|
|
795
|
-
*/
|
|
796
|
-
constructEntity(props) {
|
|
797
|
-
const jsClass = this.getJsClass(props.classFullName);
|
|
798
|
-
return new jsClass(props, this);
|
|
799
|
-
}
|
|
800
|
-
/** Get the JavaScript class that handles a given entity class. */
|
|
801
|
-
getJsClass(classFullName) {
|
|
802
|
-
try {
|
|
803
|
-
return ClassRegistry_1.ClassRegistry.getClass(classFullName, this);
|
|
804
|
-
}
|
|
805
|
-
catch (err) {
|
|
806
|
-
if (!ClassRegistry_1.ClassRegistry.isNotFoundError(err)) {
|
|
807
|
-
throw err;
|
|
808
|
-
}
|
|
809
|
-
this.loadMetaData(classFullName);
|
|
810
|
-
return ClassRegistry_1.ClassRegistry.getClass(classFullName, this);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
/** Get metadata for a class. This method will load the metadata from the iModel into the cache as a side-effect, if necessary.
|
|
814
|
-
* @throws [[IModelError]] if the metadata cannot be found nor loaded.
|
|
815
|
-
*/
|
|
816
|
-
getMetaData(classFullName) {
|
|
817
|
-
let metadata = this.classMetaDataRegistry.find(classFullName);
|
|
818
|
-
if (metadata === undefined) {
|
|
819
|
-
this.loadMetaData(classFullName);
|
|
820
|
-
metadata = this.classMetaDataRegistry.find(classFullName);
|
|
821
|
-
if (metadata === undefined)
|
|
822
|
-
throw ClassRegistry_1.ClassRegistry.makeMetaDataNotFoundError(classFullName); // do not log
|
|
823
|
-
}
|
|
824
|
-
return metadata;
|
|
825
|
-
}
|
|
826
|
-
/** Invoke a callback on each property of the specified class, optionally including superclass properties.
|
|
827
|
-
* @param iModel The IModel that contains the schema
|
|
828
|
-
* @param classFullName The full class name to load the metadata, if necessary
|
|
829
|
-
* @param wantSuper If true, superclass properties will also be processed
|
|
830
|
-
* @param func The callback to be invoked on each property
|
|
831
|
-
* @param includeCustom If true (default), include custom-handled properties in the iteration. Otherwise, skip custom-handled properties.
|
|
832
|
-
* @note Custom-handled properties are core properties that have behavior enforced by C++ handlers.
|
|
833
|
-
*/
|
|
834
|
-
static forEachMetaData(iModel, classFullName, wantSuper, func, includeCustom = true) {
|
|
835
|
-
const meta = iModel.getMetaData(classFullName); // will load if necessary
|
|
836
|
-
for (const propName in meta.properties) { // eslint-disable-line guard-for-in
|
|
837
|
-
const propMeta = meta.properties[propName];
|
|
838
|
-
if (includeCustom || !propMeta.isCustomHandled || propMeta.isCustomHandledOrphan)
|
|
839
|
-
func(propName, propMeta);
|
|
840
|
-
}
|
|
841
|
-
if (wantSuper && meta.baseClasses && meta.baseClasses.length > 0)
|
|
842
|
-
meta.baseClasses.forEach((baseClass) => this.forEachMetaData(iModel, baseClass, true, func, includeCustom));
|
|
843
|
-
}
|
|
844
|
-
/** @internal */
|
|
845
|
-
loadMetaData(classFullName) {
|
|
846
|
-
if (this.classMetaDataRegistry.find(classFullName))
|
|
847
|
-
return;
|
|
848
|
-
const className = classFullName.split(":");
|
|
849
|
-
if (className.length !== 2)
|
|
850
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadArg, `Invalid classFullName: ${classFullName}`);
|
|
851
|
-
const val = this.nativeDb.getECClassMetaData(className[0], className[1]);
|
|
852
|
-
if (val.error)
|
|
853
|
-
throw new core_common_1.IModelError(val.error.status, `Error getting class meta data for: ${classFullName}`);
|
|
854
|
-
(0, core_bentley_1.assert)(undefined !== val.result);
|
|
855
|
-
const metaData = new core_common_1.EntityMetaData(JSON.parse(val.result));
|
|
856
|
-
this.classMetaDataRegistry.add(classFullName, metaData);
|
|
857
|
-
// Recursive, to make sure that base classes are cached.
|
|
858
|
-
if (metaData.baseClasses !== undefined && metaData.baseClasses.length > 0)
|
|
859
|
-
metaData.baseClasses.forEach((baseClassName) => this.loadMetaData(baseClassName));
|
|
860
|
-
}
|
|
861
|
-
/** Returns the full schema for the input name.
|
|
862
|
-
* @param name The name of the schema e.g. 'BisCore'
|
|
863
|
-
* @returns The SchemaProps for the requested schema
|
|
864
|
-
* @throws if the schema can not be found or loaded.
|
|
865
|
-
*/
|
|
866
|
-
getSchemaProps(name) {
|
|
867
|
-
return this.nativeDb.getSchemaProps(name);
|
|
868
|
-
}
|
|
869
|
-
/** Query if this iModel contains the definition of the specified class.
|
|
870
|
-
* @param classFullName The full name of the class, for example, SomeSchema:SomeClass
|
|
871
|
-
* @returns true if the iModel contains the class definition or false if not.
|
|
872
|
-
* @see querySchemaVersion
|
|
873
|
-
* @see importSchema
|
|
874
|
-
*/
|
|
875
|
-
containsClass(classFullName) {
|
|
876
|
-
const classNameParts = classFullName.replace(".", ":").split(":");
|
|
877
|
-
return classNameParts.length === 2 && this.nativeDb.getECClassMetaData(classNameParts[0], classNameParts[1]).error === undefined;
|
|
878
|
-
}
|
|
879
|
-
/** Query for a schema of the specified name in this iModel.
|
|
880
|
-
* @returns The schema version as a semver-compatible string or `undefined` if the schema has not been imported.
|
|
881
|
-
*/
|
|
882
|
-
querySchemaVersion(schemaName) {
|
|
883
|
-
const sql = `SELECT VersionMajor,VersionWrite,VersionMinor FROM ECDbMeta.ECSchemaDef WHERE Name=:schemaName LIMIT 1`;
|
|
884
|
-
return this.withPreparedStatement(sql, (statement) => {
|
|
885
|
-
statement.bindString("schemaName", schemaName);
|
|
886
|
-
if (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
887
|
-
const versionMajor = statement.getValue(0).getInteger(); // ECSchemaDef.VersionMajor --> semver.major
|
|
888
|
-
const versionWrite = statement.getValue(1).getInteger(); // ECSchemaDef.VersionWrite --> semver.minor
|
|
889
|
-
const versionMinor = statement.getValue(2).getInteger(); // ECSchemaDef.VersionMinor --> semver.patch
|
|
890
|
-
return `${versionMajor}.${versionWrite}.${versionMinor}`;
|
|
891
|
-
}
|
|
892
|
-
return undefined;
|
|
893
|
-
});
|
|
894
|
-
}
|
|
895
|
-
/** Retrieve a named texture image from this iModel, as a TextureData.
|
|
896
|
-
* @param props the texture load properties which must include the name of the texture to load
|
|
897
|
-
* @returns the TextureData or undefined if the texture image is not present.
|
|
898
|
-
* @alpha
|
|
899
|
-
*/
|
|
900
|
-
async queryTextureData(props) {
|
|
901
|
-
return this.nativeDb.queryTextureData(props);
|
|
902
|
-
}
|
|
903
|
-
/** Query a "file property" from this iModel, as a string.
|
|
904
|
-
* @returns the property string or undefined if the property is not present.
|
|
905
|
-
*/
|
|
906
|
-
queryFilePropertyString(prop) {
|
|
907
|
-
return this.nativeDb.queryFileProperty(prop, true);
|
|
908
|
-
}
|
|
909
|
-
/** Query a "file property" from this iModel, as a blob.
|
|
910
|
-
* @returns the property blob or undefined if the property is not present.
|
|
911
|
-
*/
|
|
912
|
-
queryFilePropertyBlob(prop) {
|
|
913
|
-
return this.nativeDb.queryFileProperty(prop, false);
|
|
914
|
-
}
|
|
915
|
-
/** Save a "file property" to this iModel
|
|
916
|
-
* @param prop the FilePropertyProps that describes the new property
|
|
917
|
-
* @param value either a string or a blob to save as the file property
|
|
918
|
-
*/
|
|
919
|
-
saveFileProperty(prop, strValue, blobVal) {
|
|
920
|
-
this.nativeDb.saveFileProperty(prop, strValue, blobVal);
|
|
921
|
-
}
|
|
922
|
-
/** delete a "file property" from this iModel
|
|
923
|
-
* @param prop the FilePropertyProps that describes the property
|
|
924
|
-
*/
|
|
925
|
-
deleteFileProperty(prop) {
|
|
926
|
-
this.nativeDb.saveFileProperty(prop, undefined, undefined);
|
|
927
|
-
}
|
|
928
|
-
/** Query for the next available major id for a "file property" from this iModel.
|
|
929
|
-
* @param prop the FilePropertyProps that describes the property
|
|
930
|
-
* @returns the next available (that is, an unused) id for prop. If none are present, will return 0.
|
|
931
|
-
*/
|
|
932
|
-
queryNextAvailableFileProperty(prop) { return this.nativeDb.queryNextAvailableFileProperty(prop); }
|
|
933
|
-
/** @internal */
|
|
934
|
-
async requestSnap(sessionId, props) {
|
|
935
|
-
let request = this._snaps.get(sessionId);
|
|
936
|
-
if (undefined === request) {
|
|
937
|
-
request = new IModelHost_1.IModelHost.platform.SnapRequest();
|
|
938
|
-
this._snaps.set(sessionId, request);
|
|
939
|
-
}
|
|
940
|
-
else
|
|
941
|
-
request.cancelSnap();
|
|
942
|
-
try {
|
|
943
|
-
return await request.doSnap(this.nativeDb, core_bentley_1.JsonUtils.toObject(props));
|
|
944
|
-
}
|
|
945
|
-
finally {
|
|
946
|
-
this._snaps.delete(sessionId);
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
/** Cancel a previously requested snap.
|
|
950
|
-
* @internal
|
|
951
|
-
*/
|
|
952
|
-
cancelSnap(sessionId) {
|
|
953
|
-
const request = this._snaps.get(sessionId);
|
|
954
|
-
if (undefined !== request) {
|
|
955
|
-
request.cancelSnap();
|
|
956
|
-
this._snaps.delete(sessionId);
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
/** Get the clip containment status for the supplied elements. */
|
|
960
|
-
async getGeometryContainment(props) {
|
|
961
|
-
return this.nativeDb.getGeometryContainment(core_bentley_1.JsonUtils.toObject(props));
|
|
962
|
-
}
|
|
963
|
-
/** Get the mass properties for the supplied elements. */
|
|
964
|
-
async getMassProperties(props) {
|
|
965
|
-
return this.nativeDb.getMassProperties(core_bentley_1.JsonUtils.toObject(props));
|
|
966
|
-
}
|
|
967
|
-
/** Get the IModel coordinate corresponding to each GeoCoordinate point in the input */
|
|
968
|
-
async getIModelCoordinatesFromGeoCoordinates(props) {
|
|
969
|
-
return this.nativeDb.getIModelCoordinatesFromGeoCoordinates(props);
|
|
970
|
-
}
|
|
971
|
-
/** Get the GeoCoordinate (longitude, latitude, elevation) corresponding to each IModel Coordinate point in the input */
|
|
972
|
-
async getGeoCoordinatesFromIModelCoordinates(props) {
|
|
973
|
-
return this.nativeDb.getGeoCoordinatesFromIModelCoordinates(props);
|
|
974
|
-
}
|
|
975
|
-
/** Export meshes suitable for graphics APIs from arbitrary geometry in elements in this IModelDb.
|
|
976
|
-
* * Requests can be slow when processing many elements so it is expected that this function be used on a dedicated backend,
|
|
977
|
-
* or that shared backends export a limited number of elements at a time.
|
|
978
|
-
* * Vertices are exported in the IModelDb's world coordinate system, which is right-handed with Z pointing up.
|
|
979
|
-
* * The results of changing [ExportGraphicsOptions]($core-backend) during the [ExportGraphicsOptions.onGraphics]($core-backend) callback are not defined.
|
|
980
|
-
*
|
|
981
|
-
* Example that prints the mesh for element 1 to stdout in [OBJ format](https://en.wikipedia.org/wiki/Wavefront_.obj_file)
|
|
982
|
-
* ```ts
|
|
983
|
-
* const onGraphics: ExportGraphicsFunction = (info: ExportGraphicsInfo) => {
|
|
984
|
-
* const mesh: ExportGraphicsMesh = info.mesh;
|
|
985
|
-
* for (let i = 0; i < mesh.points.length; i += 3) {
|
|
986
|
-
* process.stdout.write(`v ${mesh.points[i]} ${mesh.points[i + 1]} ${mesh.points[i + 2]}\n`);
|
|
987
|
-
* process.stdout.write(`vn ${mesh.normals[i]} ${mesh.normals[i + 1]} ${mesh.normals[i + 2]}\n`);
|
|
988
|
-
* }
|
|
989
|
-
*
|
|
990
|
-
* for (let i = 0; i < mesh.params.length; i += 2) {
|
|
991
|
-
* process.stdout.write(`vt ${mesh.params[i]} ${mesh.params[i + 1]}\n`);
|
|
992
|
-
* }
|
|
993
|
-
*
|
|
994
|
-
* for (let i = 0; i < mesh.indices.length; i += 3) {
|
|
995
|
-
* const p1 = mesh.indices[i];
|
|
996
|
-
* const p2 = mesh.indices[i + 1];
|
|
997
|
-
* const p3 = mesh.indices[i + 2];
|
|
998
|
-
* process.stdout.write(`f ${p1}/${p1}/${p1} ${p2}/${p2}/${p2} ${p3}/${p3}/${p3}\n`);
|
|
999
|
-
* }
|
|
1000
|
-
* };
|
|
1001
|
-
*
|
|
1002
|
-
* iModel.exportGraphics(({ onGraphics, elementIdArray: ["0x1"] }));
|
|
1003
|
-
* ```
|
|
1004
|
-
* @returns 0 if successful, status otherwise
|
|
1005
|
-
* @public
|
|
1006
|
-
*/
|
|
1007
|
-
exportGraphics(exportProps) {
|
|
1008
|
-
return this.nativeDb.exportGraphics(exportProps);
|
|
1009
|
-
}
|
|
1010
|
-
/**
|
|
1011
|
-
* Exports meshes suitable for graphics APIs from a specified [GeometryPart]($core-backend)
|
|
1012
|
-
* in this IModelDb.
|
|
1013
|
-
* The expected use case is to call [IModelDb.exportGraphics]($core-backend) and supply the
|
|
1014
|
-
* optional partInstanceArray argument, then call this function for each unique GeometryPart from
|
|
1015
|
-
* that list.
|
|
1016
|
-
* * The results of changing [ExportPartGraphicsOptions]($core-backend) during the
|
|
1017
|
-
* [ExportPartGraphicsOptions.onPartGraphics]($core-backend) callback are not defined.
|
|
1018
|
-
* * See export-gltf under test-apps in the iTwin.js monorepo for a working reference.
|
|
1019
|
-
* @returns 0 is successful, status otherwise
|
|
1020
|
-
* @public
|
|
1021
|
-
*/
|
|
1022
|
-
exportPartGraphics(exportProps) {
|
|
1023
|
-
return this.nativeDb.exportPartGraphics(exportProps);
|
|
1024
|
-
}
|
|
1025
|
-
/** Request geometry stream information from an element in binary format instead of json.
|
|
1026
|
-
* @returns IModelStatus.Success if successful
|
|
1027
|
-
* @alpha
|
|
1028
|
-
*/
|
|
1029
|
-
elementGeometryRequest(requestProps) {
|
|
1030
|
-
return this.nativeDb.processGeometryStream(requestProps);
|
|
1031
|
-
}
|
|
1032
|
-
/** Create brep geometry for inclusion in an element's geometry stream.
|
|
1033
|
-
* @returns IModelStatus.Success if successful
|
|
1034
|
-
* @throws [[IModelError]] to report issues with input geometry or parameters
|
|
1035
|
-
* @alpha
|
|
1036
|
-
*/
|
|
1037
|
-
createBRepGeometry(createProps) {
|
|
1038
|
-
return this.nativeDb.createBRepGeometry(createProps);
|
|
1039
|
-
}
|
|
1040
|
-
/** Generate graphics for an element or geometry stream.
|
|
1041
|
-
* @see [readElementGraphics]($frontend) to convert the result to a [RenderGraphic]($frontend) for display.
|
|
1042
|
-
*/
|
|
1043
|
-
async generateElementGraphics(request) {
|
|
1044
|
-
return (0, ElementGraphics_1.generateElementGraphics)(request, this);
|
|
1045
|
-
}
|
|
1046
|
-
/** Save a `SettingDictionary` in this iModel that will be loaded into [[workspace.settings]] every time this iModel is opened in future sessions.
|
|
1047
|
-
* @param name The name for the SettingDictionary. If a dictionary by that name already exists in the iModel, its value is replaced.
|
|
1048
|
-
* @param dict The SettingDictionary object to stringify and save.
|
|
1049
|
-
* @note All saved `SettingDictionary`s are loaded into [[workspace.settings]] every time an iModel is opened.
|
|
1050
|
-
* @beta
|
|
1051
|
-
*/
|
|
1052
|
-
saveSettingDictionary(name, dict) {
|
|
1053
|
-
this.withSqliteStatement("REPLACE INTO be_Prop(id,SubId,TxnMode,Namespace,Name,strData) VALUES(0,0,0,?,?,?)", (stmt) => {
|
|
1054
|
-
stmt.bindString(1, IModelDb._settingPropNamespace);
|
|
1055
|
-
stmt.bindString(2, name);
|
|
1056
|
-
stmt.bindString(3, JSON.stringify(dict));
|
|
1057
|
-
const rc = stmt.step();
|
|
1058
|
-
if (rc !== core_bentley_1.DbResult.BE_SQLITE_DONE)
|
|
1059
|
-
throw new core_common_1.IModelError(rc, "cannot save setting");
|
|
1060
|
-
});
|
|
1061
|
-
this.saveChanges("add settings");
|
|
1062
|
-
}
|
|
1063
|
-
/** Delete a SettingDictionary, previously added with [[saveSettingDictionary]], from this iModel.
|
|
1064
|
-
* @param name The name of the dictionary to delete.
|
|
1065
|
-
* @beta
|
|
1066
|
-
*/
|
|
1067
|
-
deleteSettingDictionary(name) {
|
|
1068
|
-
this.withSqliteStatement("DELETE FROM be_Prop WHERE Namespace=? AND Name=?", (stmt) => {
|
|
1069
|
-
stmt.bindString(1, IModelDb._settingPropNamespace);
|
|
1070
|
-
stmt.bindString(2, name);
|
|
1071
|
-
const rc = stmt.step();
|
|
1072
|
-
if (rc !== core_bentley_1.DbResult.BE_SQLITE_DONE)
|
|
1073
|
-
throw new core_common_1.IModelError(rc, "cannot delete setting");
|
|
1074
|
-
});
|
|
1075
|
-
this.saveChanges("delete settings");
|
|
1076
|
-
}
|
|
1077
|
-
/** Load all setting dictionaries in this iModel into `this.workspace.settings` */
|
|
1078
|
-
loadSettingDictionaries() {
|
|
1079
|
-
if (!this.nativeDb.isOpen())
|
|
1080
|
-
return;
|
|
1081
|
-
this.withSqliteStatement("SELECT Name,StrData FROM be_Prop WHERE Namespace=?", (stmt) => {
|
|
1082
|
-
stmt.bindString(1, IModelDb._settingPropNamespace);
|
|
1083
|
-
while (stmt.step() === core_bentley_1.DbResult.BE_SQLITE_ROW) {
|
|
1084
|
-
try {
|
|
1085
|
-
const dict = JSON.parse(stmt.getValueString(1));
|
|
1086
|
-
this.workspace.settings.addDictionary(stmt.getValueString(0), Settings_1.SettingsPriority.iModel, dict);
|
|
1087
|
-
}
|
|
1088
|
-
catch (e) {
|
|
1089
|
-
core_bentley_1.UnexpectedErrors.handle(e);
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
});
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
exports.IModelDb = IModelDb;
|
|
1096
|
-
/** Keep track of open imodels to support `tryFind` for RPC purposes */
|
|
1097
|
-
IModelDb._openDbs = new Map();
|
|
1098
|
-
IModelDb.defaultLimit = 1000; // default limit for batching queries
|
|
1099
|
-
IModelDb.maxLimit = 10000; // maximum limit for batching queries
|
|
1100
|
-
IModelDb._settingPropNamespace = "settings";
|
|
1101
|
-
/** @public */
|
|
1102
|
-
(function (IModelDb) {
|
|
1103
|
-
/** The collection of models in an [[IModelDb]].
|
|
1104
|
-
* @public
|
|
1105
|
-
*/
|
|
1106
|
-
class Models {
|
|
1107
|
-
/** @internal */
|
|
1108
|
-
constructor(_iModel) {
|
|
1109
|
-
this._iModel = _iModel;
|
|
1110
|
-
}
|
|
1111
|
-
/** Get the ModelProps with the specified identifier.
|
|
1112
|
-
* @param modelId The Model identifier.
|
|
1113
|
-
* @throws [[IModelError]] if the model is not found or cannot be loaded.
|
|
1114
|
-
* @see tryGetModelProps
|
|
1115
|
-
*/
|
|
1116
|
-
getModelProps(id) {
|
|
1117
|
-
return this.getModelJson({ id });
|
|
1118
|
-
}
|
|
1119
|
-
/** Get the ModelProps with the specified identifier.
|
|
1120
|
-
* @param modelId The Model identifier.
|
|
1121
|
-
* @returns The ModelProps or `undefined` if the model is not found.
|
|
1122
|
-
* @throws [[IModelError]] if the model cannot be loaded.
|
|
1123
|
-
* @note Useful for cases when a model may or may not exist and throwing an `Error` would be overkill.
|
|
1124
|
-
* @see getModelProps
|
|
1125
|
-
*/
|
|
1126
|
-
tryGetModelProps(id) {
|
|
1127
|
-
return this.tryGetModelJson({ id });
|
|
1128
|
-
}
|
|
1129
|
-
/** Query for the last modified time of the specified Model.
|
|
1130
|
-
* @internal
|
|
1131
|
-
*/
|
|
1132
|
-
queryLastModifiedTime(modelId) {
|
|
1133
|
-
const sql = `SELECT LastMod FROM ${Model_1.Model.classFullName} WHERE ECInstanceId=:modelId`;
|
|
1134
|
-
return this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1135
|
-
statement.bindId("modelId", modelId);
|
|
1136
|
-
if (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1137
|
-
return statement.getValue(0).getDateTime();
|
|
1138
|
-
}
|
|
1139
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, `Can't get lastMod time for Model ${modelId}`);
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
1142
|
-
/** Get the Model with the specified identifier.
|
|
1143
|
-
* @param modelId The Model identifier.
|
|
1144
|
-
* @param modelClass Optional class to validate instance against. This parameter can accept abstract or concrete classes, but should be the same as the template (`T`) parameter.
|
|
1145
|
-
* @throws [[IModelError]] if the model is not found, cannot be loaded, or fails validation when `modelClass` is specified.
|
|
1146
|
-
* @see tryGetModel
|
|
1147
|
-
*/
|
|
1148
|
-
getModel(modelId, modelClass) {
|
|
1149
|
-
const model = this.tryGetModel(modelId, modelClass);
|
|
1150
|
-
if (undefined === model) {
|
|
1151
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `Model=${modelId}`);
|
|
1152
|
-
}
|
|
1153
|
-
return model;
|
|
1154
|
-
}
|
|
1155
|
-
/** Get the Model with the specified identifier.
|
|
1156
|
-
* @param modelId The Model identifier.
|
|
1157
|
-
* @param modelClass Optional class to validate instance against. This parameter can accept abstract or concrete classes, but should be the same as the template (`T`) parameter.
|
|
1158
|
-
* @returns The Model or `undefined` if the model is not found or fails validation when `modelClass` is specified.
|
|
1159
|
-
* @throws [[IModelError]] if the model cannot be loaded.
|
|
1160
|
-
* @note Useful for cases when a model may or may not exist and throwing an `Error` would be overkill.
|
|
1161
|
-
* @see getModel
|
|
1162
|
-
*/
|
|
1163
|
-
tryGetModel(modelId, modelClass) {
|
|
1164
|
-
const modelProps = this.tryGetModelProps(modelId);
|
|
1165
|
-
if (undefined === modelProps)
|
|
1166
|
-
return undefined; // no Model with that modelId found
|
|
1167
|
-
const model = this._iModel.constructEntity(modelProps);
|
|
1168
|
-
if (undefined === modelClass)
|
|
1169
|
-
return model; // modelClass was not specified, cannot call instanceof to validate
|
|
1170
|
-
return model instanceof modelClass ? model : undefined;
|
|
1171
|
-
}
|
|
1172
|
-
/** Read the properties for a Model as a json string.
|
|
1173
|
-
* @param modelIdArg a json string with the identity of the model to load. Must have either "id" or "code".
|
|
1174
|
-
* @returns a json string with the properties of the model.
|
|
1175
|
-
* @throws [[IModelError]] if the model is not found or cannot be loaded.
|
|
1176
|
-
* @see tryGetModelJson
|
|
1177
|
-
* @internal
|
|
1178
|
-
*/
|
|
1179
|
-
getModelJson(modelIdArg) {
|
|
1180
|
-
const modelJson = this.tryGetModelJson(modelIdArg);
|
|
1181
|
-
if (undefined === modelJson) {
|
|
1182
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `Model=${modelIdArg}`);
|
|
1183
|
-
}
|
|
1184
|
-
return modelJson;
|
|
1185
|
-
}
|
|
1186
|
-
/** Read the properties for a Model as a json string.
|
|
1187
|
-
* @param modelIdArg a json string with the identity of the model to load. Must have either "id" or "code".
|
|
1188
|
-
* @returns a json string with the properties of the model or `undefined` if the model is not found.
|
|
1189
|
-
* @see getModelJson
|
|
1190
|
-
*/
|
|
1191
|
-
tryGetModelJson(modelIdArg) {
|
|
1192
|
-
try {
|
|
1193
|
-
return this._iModel.nativeDb.getModel(modelIdArg);
|
|
1194
|
-
}
|
|
1195
|
-
catch (err) {
|
|
1196
|
-
return undefined;
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
/** Get the sub-model of the specified Element.
|
|
1200
|
-
* See [[IModelDb.Elements.queryElementIdByCode]] for more on how to find an element by Code.
|
|
1201
|
-
* @param modeledElementId Identifies the modeled element.
|
|
1202
|
-
* @param modelClass Optional class to validate instance against. This parameter can accept abstract or concrete classes, but should be the same as the template (`T`) parameter.
|
|
1203
|
-
* @throws [[IModelError]] if the sub-model is not found, cannot be loaded, or fails validation when `modelClass` is specified.
|
|
1204
|
-
* @see tryGetSubModel
|
|
1205
|
-
*/
|
|
1206
|
-
getSubModel(modeledElementId, modelClass) {
|
|
1207
|
-
const modeledElementProps = this._iModel.elements.getElementProps(modeledElementId);
|
|
1208
|
-
if (undefined === modeledElementProps.id || modeledElementProps.id === core_common_1.IModel.rootSubjectId)
|
|
1209
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, "Root subject does not have a sub-model");
|
|
1210
|
-
return this.getModel(modeledElementProps.id, modelClass);
|
|
1211
|
-
}
|
|
1212
|
-
/** Get the sub-model of the specified Element.
|
|
1213
|
-
* See [[IModelDb.Elements.queryElementIdByCode]] for more on how to find an element by Code.
|
|
1214
|
-
* @param modeledElementId Identifies the modeled element.
|
|
1215
|
-
* @param modelClass Optional class to validate instance against. This parameter can accept abstract or concrete classes, but should be the same as the template (`T`) parameter.
|
|
1216
|
-
* @returns The sub-model or `undefined` if the specified element does not have a sub-model or fails validation when `modelClass` is specified.
|
|
1217
|
-
* @see getSubModel
|
|
1218
|
-
*/
|
|
1219
|
-
tryGetSubModel(modeledElementId, modelClass) {
|
|
1220
|
-
const modeledElementProps = this._iModel.elements.tryGetElementProps(modeledElementId);
|
|
1221
|
-
if (undefined === (modeledElementProps === null || modeledElementProps === void 0 ? void 0 : modeledElementProps.id) || (core_common_1.IModel.rootSubjectId === modeledElementProps.id))
|
|
1222
|
-
return undefined;
|
|
1223
|
-
return this.tryGetModel(modeledElementProps.id, modelClass);
|
|
1224
|
-
}
|
|
1225
|
-
/** Create a new model in memory.
|
|
1226
|
-
* See the example in [[InformationPartitionElement]].
|
|
1227
|
-
* @param modelProps The properties to use when creating the model.
|
|
1228
|
-
* @throws [[IModelError]] if there is a problem creating the model.
|
|
1229
|
-
*/
|
|
1230
|
-
createModel(modelProps) { return this._iModel.constructEntity(modelProps); }
|
|
1231
|
-
/** Insert a new model.
|
|
1232
|
-
* @param props The data for the new model.
|
|
1233
|
-
* @returns The newly inserted model's Id.
|
|
1234
|
-
* @throws [[IModelError]] if unable to insert the model.
|
|
1235
|
-
*/
|
|
1236
|
-
insertModel(props) {
|
|
1237
|
-
try {
|
|
1238
|
-
return props.id = this._iModel.nativeDb.insertModel(props);
|
|
1239
|
-
}
|
|
1240
|
-
catch (err) {
|
|
1241
|
-
throw new core_common_1.IModelError(err.errorNumber, `Error inserting model [${err.message}], class=${props.classFullName}`);
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
/** Update an existing model.
|
|
1245
|
-
* @param props the properties of the model to change
|
|
1246
|
-
* @throws [[IModelError]] if unable to update the model.
|
|
1247
|
-
*/
|
|
1248
|
-
updateModel(props) {
|
|
1249
|
-
try {
|
|
1250
|
-
this._iModel.nativeDb.updateModel(props);
|
|
1251
|
-
}
|
|
1252
|
-
catch (err) {
|
|
1253
|
-
throw new core_common_1.IModelError(err.errorNumber, `error updating model [${err.message}] id=${props.id}`);
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
/** Mark the geometry of [[GeometricModel]] as having changed, by recording an indirect change to its GeometryGuid property.
|
|
1257
|
-
* Typically the GeometryGuid changes automatically when [[GeometricElement]]s within the model are modified, but
|
|
1258
|
-
* explicitly updating it is occasionally useful after modifying definition elements like line styles or materials that indirectly affect the appearance of
|
|
1259
|
-
* [[GeometricElement]]s that reference those definition elements in their geometry streams.
|
|
1260
|
-
* Cached [Tile]($frontend)s are only invalidated after the geometry guid of the model changes.
|
|
1261
|
-
* @note This will throw IModelError with [IModelStatus.VersionTooOld]($core-bentley) if a version of the BisCore schema older than 1.0.11 is present in the iModel.
|
|
1262
|
-
* @throws IModelError if unable to update the geometry guid.
|
|
1263
|
-
* @see [[TxnManager.onModelGeometryChanged]] for the event emitted in response to such a change.
|
|
1264
|
-
*/
|
|
1265
|
-
updateGeometryGuid(modelId) {
|
|
1266
|
-
const error = this._iModel.nativeDb.updateModelGeometryGuid(modelId);
|
|
1267
|
-
if (error !== core_bentley_1.IModelStatus.Success)
|
|
1268
|
-
throw new core_common_1.IModelError(error, `updating geometry guid for model ${modelId}`);
|
|
1269
|
-
}
|
|
1270
|
-
/** Delete one or more existing models.
|
|
1271
|
-
* @param ids The Ids of the models to be deleted
|
|
1272
|
-
* @throws [[IModelError]]
|
|
1273
|
-
*/
|
|
1274
|
-
deleteModel(ids) {
|
|
1275
|
-
core_bentley_1.Id64.toIdSet(ids).forEach((id) => {
|
|
1276
|
-
try {
|
|
1277
|
-
this._iModel.nativeDb.deleteModel(id);
|
|
1278
|
-
}
|
|
1279
|
-
catch (err) {
|
|
1280
|
-
throw new core_common_1.IModelError(err.errorNumber, `error deleting model [${err.message}] id ${id}`);
|
|
1281
|
-
}
|
|
1282
|
-
});
|
|
1283
|
-
}
|
|
1284
|
-
/** For each specified [[GeometricModel]], attempts to obtain the union of the volumes of all geometric elements within that model.
|
|
1285
|
-
* @param ids The Id or Ids of the [[GeometricModel]]s for which to obtain the extents.
|
|
1286
|
-
* @returns An array of results, one per supplied Id, in the order in which the Ids were supplied. If the extents could not be obtained, the
|
|
1287
|
-
* corresponding results entry's `extents` will be a "null" range (@see [Range3d.isNull]($geometry)) and its `status` will indicate
|
|
1288
|
-
* why the extents could not be obtained (e.g., because the Id did not identify a [[GeometricModel]]).
|
|
1289
|
-
* @see [[queryRange]] to obtain the union of all of the models' extents.
|
|
1290
|
-
*/
|
|
1291
|
-
async queryExtents(ids) {
|
|
1292
|
-
ids = typeof ids === "string" ? [ids] : ids;
|
|
1293
|
-
if (ids.length === 0)
|
|
1294
|
-
return [];
|
|
1295
|
-
return this._iModel.nativeDb.queryModelExtentsAsync(ids);
|
|
1296
|
-
}
|
|
1297
|
-
/** Computes the union of the volumes of all geoemtric elements within any number of [[GeometricModel]]s, specified by model Id.
|
|
1298
|
-
* @see [[queryExtents]] to obtain discrete volumes for each model.
|
|
1299
|
-
*/
|
|
1300
|
-
async queryRange(ids) {
|
|
1301
|
-
const results = await this.queryExtents(ids);
|
|
1302
|
-
const range = new core_geometry_1.Range3d();
|
|
1303
|
-
for (const result of results)
|
|
1304
|
-
range.union(core_geometry_1.Range3d.fromJSON(result.extents), range);
|
|
1305
|
-
return range;
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
IModelDb.Models = Models;
|
|
1309
|
-
/** The collection of elements in an [[IModelDb]].
|
|
1310
|
-
* @public
|
|
1311
|
-
*/
|
|
1312
|
-
class Elements {
|
|
1313
|
-
/** @internal */
|
|
1314
|
-
constructor(_iModel) {
|
|
1315
|
-
this._iModel = _iModel;
|
|
1316
|
-
}
|
|
1317
|
-
/** Read element data from the iModel as JSON
|
|
1318
|
-
* @param elementIdArg a json string with the identity of the element to load. Must have one of "id", "federationGuid", or "code".
|
|
1319
|
-
* @returns The JSON properties of the element.
|
|
1320
|
-
* @throws [[IModelError]] if the element is not found or cannot be loaded.
|
|
1321
|
-
* @see tryGetElementJson
|
|
1322
|
-
* @internal
|
|
1323
|
-
*/
|
|
1324
|
-
getElementJson(elementId) {
|
|
1325
|
-
const elementProps = this.tryGetElementJson(elementId);
|
|
1326
|
-
if (undefined === elementProps)
|
|
1327
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `reading element=${elementId}`);
|
|
1328
|
-
return elementProps;
|
|
1329
|
-
}
|
|
1330
|
-
/** Read element data from the iModel as JSON
|
|
1331
|
-
* @param loadProps - a json string with the identity of the element to load. Must have one of "id", "federationGuid", or "code".
|
|
1332
|
-
* @returns The JSON properties of the element or `undefined` if the element is not found.
|
|
1333
|
-
* @throws [[IModelError]] if the element exists, but cannot be loaded.
|
|
1334
|
-
* @see getElementJson
|
|
1335
|
-
*/
|
|
1336
|
-
tryGetElementJson(loadProps) {
|
|
1337
|
-
try {
|
|
1338
|
-
return this._iModel.nativeDb.getElement(loadProps);
|
|
1339
|
-
}
|
|
1340
|
-
catch (err) {
|
|
1341
|
-
return undefined;
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
/** Get properties of an Element by Id, FederationGuid, or Code
|
|
1345
|
-
* @throws [[IModelError]] if the element is not found or cannot be loaded.
|
|
1346
|
-
* @see tryGetElementProps
|
|
1347
|
-
*/
|
|
1348
|
-
getElementProps(props) {
|
|
1349
|
-
if (typeof props === "string") {
|
|
1350
|
-
props = core_bentley_1.Id64.isId64(props) ? { id: props } : { federationGuid: props };
|
|
1351
|
-
}
|
|
1352
|
-
else if (props instanceof core_common_1.Code) {
|
|
1353
|
-
props = { code: props };
|
|
1354
|
-
}
|
|
1355
|
-
try {
|
|
1356
|
-
return this._iModel.nativeDb.getElement(props);
|
|
1357
|
-
}
|
|
1358
|
-
catch (err) {
|
|
1359
|
-
throw new core_common_1.IModelError(err.errorNumber, err.message);
|
|
1360
|
-
}
|
|
1361
|
-
}
|
|
1362
|
-
/** Get properties of an Element by Id, FederationGuid, or Code
|
|
1363
|
-
* @returns The properties of the element or `undefined` if the element is not found.
|
|
1364
|
-
* @throws [[IModelError]] if the element exists, but cannot be loaded.
|
|
1365
|
-
* @note Useful for cases when an element may or may not exist and throwing an `Error` would be overkill.
|
|
1366
|
-
* @see getElementProps
|
|
1367
|
-
*/
|
|
1368
|
-
tryGetElementProps(elementId) {
|
|
1369
|
-
if (typeof elementId === "string") {
|
|
1370
|
-
elementId = core_bentley_1.Id64.isId64(elementId) ? { id: elementId } : { federationGuid: elementId };
|
|
1371
|
-
}
|
|
1372
|
-
else if (elementId instanceof core_common_1.Code) {
|
|
1373
|
-
elementId = { code: elementId };
|
|
1374
|
-
}
|
|
1375
|
-
return this.tryGetElementJson(elementId);
|
|
1376
|
-
}
|
|
1377
|
-
/** Get an element by Id, FederationGuid, or Code
|
|
1378
|
-
* @param elementId either the element's Id, Code, or FederationGuid, or an ElementLoadProps
|
|
1379
|
-
* @param elementClass Optional class to validate instance against. This parameter can accept abstract or concrete classes, but should be the same as the template (`T`) parameter.
|
|
1380
|
-
* @throws [[IModelError]] if the element is not found, cannot be loaded, or fails validation when `elementClass` is specified.
|
|
1381
|
-
* @see tryGetElement
|
|
1382
|
-
*/
|
|
1383
|
-
getElement(elementId, elementClass) {
|
|
1384
|
-
const element = this.tryGetElement(elementId, elementClass);
|
|
1385
|
-
if (undefined === element)
|
|
1386
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `Element=${elementId}`);
|
|
1387
|
-
return element;
|
|
1388
|
-
}
|
|
1389
|
-
/** Get an element by Id, FederationGuid, or Code
|
|
1390
|
-
* @param elementId either the element's Id, Code, or FederationGuid, or an ElementLoadProps
|
|
1391
|
-
* @param elementClass Optional class to validate instance against. This parameter can accept abstract or concrete classes, but should be the same as the template (`T`) parameter.
|
|
1392
|
-
* @returns The element or `undefined` if the element is not found or fails validation when `elementClass` is specified.
|
|
1393
|
-
* @throws [[IModelError]] if the element exists, but cannot be loaded.
|
|
1394
|
-
* @note Useful for cases when an element may or may not exist and throwing an `Error` would be overkill.
|
|
1395
|
-
* @see getElement
|
|
1396
|
-
*/
|
|
1397
|
-
tryGetElement(elementId, elementClass) {
|
|
1398
|
-
if (typeof elementId === "string")
|
|
1399
|
-
elementId = core_bentley_1.Id64.isId64(elementId) ? { id: elementId } : { federationGuid: elementId };
|
|
1400
|
-
else if (elementId instanceof core_common_1.Code)
|
|
1401
|
-
elementId = { code: elementId };
|
|
1402
|
-
else
|
|
1403
|
-
elementId.onlyBaseProperties = false; // we must load all properties to construct the element.
|
|
1404
|
-
const elementProps = this.tryGetElementJson(elementId);
|
|
1405
|
-
if (undefined === elementProps)
|
|
1406
|
-
return undefined; // no Element with that elementId found
|
|
1407
|
-
const element = this._iModel.constructEntity(elementProps);
|
|
1408
|
-
if (undefined === elementClass)
|
|
1409
|
-
return element; // elementClass was not specified, cannot call instanceof to validate
|
|
1410
|
-
return element instanceof elementClass ? element : undefined;
|
|
1411
|
-
}
|
|
1412
|
-
/** Query for the Id of the element that has a specified code.
|
|
1413
|
-
* This method is for the case where you know the element's Code.
|
|
1414
|
-
* If you only know the code *value*, then in the simplest case, you can query on that
|
|
1415
|
-
* and filter the results.
|
|
1416
|
-
* In the simple case, call [[IModelDb.queryEntityIds]], specifying the code value in the where clause of the query params.
|
|
1417
|
-
* Or, you can execute an ECSQL select statement. See
|
|
1418
|
-
* [frequently used ECSQL queries]($docs/learning/backend/ECSQL-queries.md) for an example.
|
|
1419
|
-
* @param code The code to look for
|
|
1420
|
-
* @returns The element that uses the code or undefined if the code is not used.
|
|
1421
|
-
* @throws IModelError if the code is invalid
|
|
1422
|
-
*/
|
|
1423
|
-
queryElementIdByCode(code) {
|
|
1424
|
-
if (core_bentley_1.Id64.isInvalid(code.spec))
|
|
1425
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidCodeSpec, "Invalid CodeSpec");
|
|
1426
|
-
if (code.value === undefined)
|
|
1427
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidCode, "Invalid Code");
|
|
1428
|
-
return this._iModel.withPreparedStatement("SELECT ECInstanceId FROM BisCore:Element WHERE CodeSpec.Id=? AND CodeScope.Id=? AND CodeValue=?", (stmt) => {
|
|
1429
|
-
stmt.bindId(1, code.spec);
|
|
1430
|
-
stmt.bindId(2, core_bentley_1.Id64.fromString(code.scope));
|
|
1431
|
-
stmt.bindString(3, code.value);
|
|
1432
|
-
if (core_bentley_1.DbResult.BE_SQLITE_ROW !== stmt.step())
|
|
1433
|
-
return undefined;
|
|
1434
|
-
return stmt.getValue(0).getId();
|
|
1435
|
-
});
|
|
1436
|
-
}
|
|
1437
|
-
/** Query for the last modified time of the specified element.
|
|
1438
|
-
* @internal
|
|
1439
|
-
*/
|
|
1440
|
-
queryLastModifiedTime(elementId) {
|
|
1441
|
-
const sql = "SELECT LastMod FROM BisCore:Element WHERE ECInstanceId=:elementId";
|
|
1442
|
-
return this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1443
|
-
statement.bindId("elementId", elementId);
|
|
1444
|
-
if (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step())
|
|
1445
|
-
return statement.getValue(0).getDateTime();
|
|
1446
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, `Can't get lastMod time for Element ${elementId}`);
|
|
1447
|
-
});
|
|
1448
|
-
}
|
|
1449
|
-
/** Create a new instance of an element.
|
|
1450
|
-
* @param elProps The properties of the new element.
|
|
1451
|
-
* @throws [[IModelError]] if there is a problem creating the element.
|
|
1452
|
-
*/
|
|
1453
|
-
createElement(elProps) { return this._iModel.constructEntity(elProps); }
|
|
1454
|
-
/** Insert a new element into the iModel.
|
|
1455
|
-
* @param elProps The properties of the new element.
|
|
1456
|
-
* @returns The newly inserted element's Id.
|
|
1457
|
-
* @throws [[IModelError]] if unable to insert the element.
|
|
1458
|
-
* @note For convenience, the value of `elProps.id` is updated to reflect the resultant element's id.
|
|
1459
|
-
* However when `elProps.federationGuid` is not present or undefined, a new Guid will be generated and stored on the resultant element. But
|
|
1460
|
-
* the value of `elProps.federationGuid` is *not* updated. Generally, it is best to re-read the element after inserting (e.g. via [[getElementProps]])
|
|
1461
|
-
* if you intend to continue working with it. That will ensure its values reflect the persistent state.
|
|
1462
|
-
*/
|
|
1463
|
-
insertElement(elProps) {
|
|
1464
|
-
try {
|
|
1465
|
-
return elProps.id = this._iModel.nativeDb.insertElement(elProps);
|
|
1466
|
-
}
|
|
1467
|
-
catch (err) {
|
|
1468
|
-
err.message = `error inserting element: ${err.message}`;
|
|
1469
|
-
throw err;
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
/** Update some properties of an existing element.
|
|
1473
|
-
* To support clearing a property value, every property name that is present in the `elProps` object will be updated even if the value is `undefined`.
|
|
1474
|
-
* To keep an individual element property unchanged, it should either be excluded from the `elProps` parameter or set to its current value.
|
|
1475
|
-
* @param elProps the properties of the element to update.
|
|
1476
|
-
* @note As described above, this is a special case where there is a difference between a property being excluded and a property being present in `elProps` but set to `undefined`.
|
|
1477
|
-
* @throws [[IModelError]] if unable to update the element.
|
|
1478
|
-
*/
|
|
1479
|
-
updateElement(elProps) {
|
|
1480
|
-
try {
|
|
1481
|
-
this._iModel.nativeDb.updateElement(elProps);
|
|
1482
|
-
}
|
|
1483
|
-
catch (err) {
|
|
1484
|
-
err.message = `error updating element: ${err.message}`;
|
|
1485
|
-
throw err;
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
/** Delete one or more elements from this iModel.
|
|
1489
|
-
* @param ids The set of Ids of the element(s) to be deleted
|
|
1490
|
-
* @throws [[IModelError]]
|
|
1491
|
-
* @see deleteDefinitionElements
|
|
1492
|
-
*/
|
|
1493
|
-
deleteElement(ids) {
|
|
1494
|
-
const iModel = this._iModel;
|
|
1495
|
-
core_bentley_1.Id64.toIdSet(ids).forEach((id) => {
|
|
1496
|
-
try {
|
|
1497
|
-
iModel.nativeDb.deleteElement(id);
|
|
1498
|
-
}
|
|
1499
|
-
catch (err) {
|
|
1500
|
-
err.message = `error deleting element: ${err.message}`;
|
|
1501
|
-
throw err;
|
|
1502
|
-
}
|
|
1503
|
-
});
|
|
1504
|
-
}
|
|
1505
|
-
/** DefinitionElements can only be deleted if it can be determined that they are not referenced by other Elements.
|
|
1506
|
-
* This *usage query* can be expensive since it may involve scanning the GeometryStreams of all GeometricElements.
|
|
1507
|
-
* Since [[deleteElement]] does not perform these additional checks, it fails in order to prevent potentially referenced DefinitionElements from being deleted.
|
|
1508
|
-
* This method performs those expensive checks and then calls *delete* if not referenced.
|
|
1509
|
-
* @param ids The Ids of the DefinitionElements to attempt to delete. To prevent multiple passes over the same GeometricElements, it is best to pass in the entire array of
|
|
1510
|
-
* DefinitionElements rather than calling this method separately for each one. Ids that are not valid DefinitionElements will be ignored.
|
|
1511
|
-
* @returns An IdSet of the DefinitionElements that are used and were therefore not deleted.
|
|
1512
|
-
* @see deleteElement
|
|
1513
|
-
* @beta
|
|
1514
|
-
*/
|
|
1515
|
-
deleteDefinitionElements(definitionElementIds) {
|
|
1516
|
-
const usageInfo = this._iModel.nativeDb.queryDefinitionElementUsage(definitionElementIds);
|
|
1517
|
-
if (!usageInfo) {
|
|
1518
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "Error querying for DefinitionElement usage");
|
|
1519
|
-
}
|
|
1520
|
-
const usedIdSet = usageInfo.usedIds ? core_bentley_1.Id64.toIdSet(usageInfo.usedIds) : new Set();
|
|
1521
|
-
const deleteIfUnused = (ids, used) => {
|
|
1522
|
-
if (ids) {
|
|
1523
|
-
ids.forEach((id) => {
|
|
1524
|
-
if (!used.has(id))
|
|
1525
|
-
this._iModel.elements.deleteElement(id);
|
|
1526
|
-
});
|
|
1527
|
-
}
|
|
1528
|
-
};
|
|
1529
|
-
try {
|
|
1530
|
-
this._iModel.nativeDb.beginPurgeOperation();
|
|
1531
|
-
deleteIfUnused(usageInfo.spatialCategoryIds, usedIdSet);
|
|
1532
|
-
deleteIfUnused(usageInfo.drawingCategoryIds, usedIdSet);
|
|
1533
|
-
deleteIfUnused(usageInfo.viewDefinitionIds, usedIdSet);
|
|
1534
|
-
deleteIfUnused(usageInfo.geometryPartIds, usedIdSet);
|
|
1535
|
-
deleteIfUnused(usageInfo.lineStyleIds, usedIdSet);
|
|
1536
|
-
deleteIfUnused(usageInfo.renderMaterialIds, usedIdSet);
|
|
1537
|
-
deleteIfUnused(usageInfo.subCategoryIds, usedIdSet);
|
|
1538
|
-
deleteIfUnused(usageInfo.textureIds, usedIdSet);
|
|
1539
|
-
deleteIfUnused(usageInfo.displayStyleIds, usedIdSet);
|
|
1540
|
-
deleteIfUnused(usageInfo.categorySelectorIds, usedIdSet);
|
|
1541
|
-
deleteIfUnused(usageInfo.modelSelectorIds, usedIdSet);
|
|
1542
|
-
if (usageInfo.otherDefinitionElementIds) {
|
|
1543
|
-
this._iModel.elements.deleteElement(usageInfo.otherDefinitionElementIds);
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
finally {
|
|
1547
|
-
this._iModel.nativeDb.endPurgeOperation();
|
|
1548
|
-
}
|
|
1549
|
-
if (usageInfo.viewDefinitionIds) {
|
|
1550
|
-
// take another pass in case a deleted ViewDefinition was the only usage of these view-related DefinitionElements
|
|
1551
|
-
let viewRelatedIds = [];
|
|
1552
|
-
if (usageInfo.displayStyleIds)
|
|
1553
|
-
viewRelatedIds = viewRelatedIds.concat(usageInfo.displayStyleIds.filter((id) => usedIdSet.has(id)));
|
|
1554
|
-
if (usageInfo.categorySelectorIds)
|
|
1555
|
-
viewRelatedIds = viewRelatedIds.concat(usageInfo.categorySelectorIds.filter((id) => usedIdSet.has(id)));
|
|
1556
|
-
if (usageInfo.modelSelectorIds)
|
|
1557
|
-
viewRelatedIds = viewRelatedIds.concat(usageInfo.modelSelectorIds.filter((id) => usedIdSet.has(id)));
|
|
1558
|
-
if (viewRelatedIds.length > 0) {
|
|
1559
|
-
const viewRelatedUsageInfo = this._iModel.nativeDb.queryDefinitionElementUsage(viewRelatedIds);
|
|
1560
|
-
if (viewRelatedUsageInfo) {
|
|
1561
|
-
const usedViewRelatedIdSet = viewRelatedUsageInfo.usedIds ? core_bentley_1.Id64.toIdSet(viewRelatedUsageInfo.usedIds) : new Set();
|
|
1562
|
-
try {
|
|
1563
|
-
this._iModel.nativeDb.beginPurgeOperation();
|
|
1564
|
-
deleteIfUnused(viewRelatedUsageInfo.displayStyleIds, usedViewRelatedIdSet);
|
|
1565
|
-
deleteIfUnused(viewRelatedUsageInfo.categorySelectorIds, usedViewRelatedIdSet);
|
|
1566
|
-
deleteIfUnused(viewRelatedUsageInfo.modelSelectorIds, usedViewRelatedIdSet);
|
|
1567
|
-
}
|
|
1568
|
-
finally {
|
|
1569
|
-
this._iModel.nativeDb.endPurgeOperation();
|
|
1570
|
-
}
|
|
1571
|
-
viewRelatedIds.forEach((id) => {
|
|
1572
|
-
if (!usedViewRelatedIdSet.has(id))
|
|
1573
|
-
usedIdSet.delete(id);
|
|
1574
|
-
});
|
|
1575
|
-
}
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
return usedIdSet;
|
|
1579
|
-
}
|
|
1580
|
-
/** Query for the child elements of the specified element.
|
|
1581
|
-
* @returns Returns an array of child element identifiers.
|
|
1582
|
-
* @throws [[IModelError]]
|
|
1583
|
-
*/
|
|
1584
|
-
queryChildren(elementId) {
|
|
1585
|
-
const sql = "SELECT ECInstanceId FROM BisCore:Element WHERE Parent.Id=:elementId";
|
|
1586
|
-
return this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1587
|
-
statement.bindId("elementId", elementId);
|
|
1588
|
-
const childIds = [];
|
|
1589
|
-
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1590
|
-
childIds.push(statement.getValue(0).getId());
|
|
1591
|
-
}
|
|
1592
|
-
return childIds;
|
|
1593
|
-
});
|
|
1594
|
-
}
|
|
1595
|
-
/** Query for the parent of the specified element.
|
|
1596
|
-
* @param elementId The element to check for a parent
|
|
1597
|
-
* @returns The identifier of the element's parent or undefined if the element has no parent
|
|
1598
|
-
* @throws [[IModelError]] if the element does not exist
|
|
1599
|
-
*/
|
|
1600
|
-
queryParent(elementId) {
|
|
1601
|
-
return this._iModel.withPreparedStatement(`select parent.id from ${Element_1.Element.classFullName} where ecinstanceid=?`, (stmt) => {
|
|
1602
|
-
stmt.bindId(1, elementId);
|
|
1603
|
-
if (stmt.step() !== core_bentley_1.DbResult.BE_SQLITE_ROW)
|
|
1604
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `Element=${elementId}`);
|
|
1605
|
-
const value = stmt.getValue(0);
|
|
1606
|
-
return value.isNull ? undefined : value.getId();
|
|
1607
|
-
});
|
|
1608
|
-
}
|
|
1609
|
-
/** Returns true if the specified Element has a sub-model.
|
|
1610
|
-
* @see [[IModelDb.Models.getSubModel]]
|
|
1611
|
-
*/
|
|
1612
|
-
hasSubModel(elementId) {
|
|
1613
|
-
if (core_common_1.IModel.rootSubjectId === elementId)
|
|
1614
|
-
return false; // Special case since the RepositoryModel does not sub-model the root Subject
|
|
1615
|
-
// A sub-model will have the same Id value as the element it is describing
|
|
1616
|
-
const sql = "SELECT ECInstanceId FROM BisCore:Model WHERE ECInstanceId=:elementId";
|
|
1617
|
-
return this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1618
|
-
statement.bindId("elementId", elementId);
|
|
1619
|
-
return core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step();
|
|
1620
|
-
});
|
|
1621
|
-
}
|
|
1622
|
-
/** Get the root subject element. */
|
|
1623
|
-
getRootSubject() { return this.getElement(core_common_1.IModel.rootSubjectId); }
|
|
1624
|
-
/** Query for aspects of a particular class (polymorphically) associated with this element.
|
|
1625
|
-
* @throws [[IModelError]]
|
|
1626
|
-
* @note Most cases should use the [[getAspects]] wrapper rather than calling this method directly.
|
|
1627
|
-
* @internal
|
|
1628
|
-
*/
|
|
1629
|
-
_queryAspects(elementId, fromClassFullName, excludedClassFullNames) {
|
|
1630
|
-
const sql = `SELECT ECInstanceId,ECClassId FROM ${fromClassFullName} WHERE Element.Id=:elementId ORDER BY ECClassId,ECInstanceId`; // ORDER BY to maximize statement reuse
|
|
1631
|
-
return this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1632
|
-
statement.bindId("elementId", elementId);
|
|
1633
|
-
const aspects = [];
|
|
1634
|
-
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1635
|
-
const aspectInstanceId = statement.getValue(0).getId();
|
|
1636
|
-
const aspectClassFullName = statement.getValue(1).getClassNameForClassId().replace(".", ":");
|
|
1637
|
-
if ((undefined === excludedClassFullNames) || (!excludedClassFullNames.has(aspectClassFullName))) {
|
|
1638
|
-
aspects.push(this._queryAspect(aspectInstanceId, aspectClassFullName));
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
return aspects;
|
|
1642
|
-
});
|
|
1643
|
-
}
|
|
1644
|
-
/** Query for aspect by ECInstanceId
|
|
1645
|
-
* @throws [[IModelError]]
|
|
1646
|
-
*/
|
|
1647
|
-
_queryAspect(aspectInstanceId, aspectClassName) {
|
|
1648
|
-
const sql = `SELECT * FROM ${aspectClassName} WHERE ECInstanceId=:aspectInstanceId`;
|
|
1649
|
-
const aspect = this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1650
|
-
statement.bindId("aspectInstanceId", aspectInstanceId);
|
|
1651
|
-
if (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1652
|
-
const aspectProps = statement.getRow(); // start with everything that SELECT * returned
|
|
1653
|
-
aspectProps.classFullName = aspectProps.className.replace(".", ":"); // add in property required by EntityProps
|
|
1654
|
-
aspectProps.className = undefined; // clear property from SELECT * that we don't want in the final instance
|
|
1655
|
-
return aspectProps;
|
|
1656
|
-
}
|
|
1657
|
-
return undefined;
|
|
1658
|
-
});
|
|
1659
|
-
if (undefined === aspect) {
|
|
1660
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `ElementAspect not found ${aspectInstanceId}, ${aspectClassName}`);
|
|
1661
|
-
}
|
|
1662
|
-
return this._iModel.constructEntity(aspect);
|
|
1663
|
-
}
|
|
1664
|
-
/** Get a single ElementAspect by its instance Id.
|
|
1665
|
-
* @throws [[IModelError]]
|
|
1666
|
-
*/
|
|
1667
|
-
getAspect(aspectInstanceId) {
|
|
1668
|
-
const sql = "SELECT ECClassId FROM BisCore:ElementAspect WHERE ECInstanceId=:aspectInstanceId";
|
|
1669
|
-
const aspectClassFullName = this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1670
|
-
statement.bindId("aspectInstanceId", aspectInstanceId);
|
|
1671
|
-
return (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) ? statement.getValue(0).getClassNameForClassId().replace(".", ":") : undefined;
|
|
1672
|
-
});
|
|
1673
|
-
if (undefined === aspectClassFullName) {
|
|
1674
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `ElementAspect not found ${aspectInstanceId}`);
|
|
1675
|
-
}
|
|
1676
|
-
return this._queryAspect(aspectInstanceId, aspectClassFullName);
|
|
1677
|
-
}
|
|
1678
|
-
/** Get the ElementAspect instances that are owned by the specified element.
|
|
1679
|
-
* @param elementId Get ElementAspects associated with this Element
|
|
1680
|
-
* @param aspectClassFullName Optionally filter ElementAspects polymorphically by this class name
|
|
1681
|
-
* @throws [[IModelError]]
|
|
1682
|
-
*/
|
|
1683
|
-
getAspects(elementId, aspectClassFullName) {
|
|
1684
|
-
if (undefined === aspectClassFullName) {
|
|
1685
|
-
const uniqueAspects = this._queryAspects(elementId, ElementAspect_1.ElementUniqueAspect.classFullName);
|
|
1686
|
-
const multiAspects = this._queryAspects(elementId, ElementAspect_1.ElementMultiAspect.classFullName);
|
|
1687
|
-
return uniqueAspects.concat(multiAspects);
|
|
1688
|
-
}
|
|
1689
|
-
const aspects = this._queryAspects(elementId, aspectClassFullName);
|
|
1690
|
-
return aspects;
|
|
1691
|
-
}
|
|
1692
|
-
/** Insert a new ElementAspect into the iModel.
|
|
1693
|
-
* @param aspectProps The properties of the new ElementAspect.
|
|
1694
|
-
* @throws [[IModelError]] if unable to insert the ElementAspect.
|
|
1695
|
-
* @returns the id of the newly inserted aspect.
|
|
1696
|
-
* @note Aspect Ids may collide with element Ids, so don't put both in a container like Set or Map
|
|
1697
|
-
* use [EntityReference]($common) for that instead.
|
|
1698
|
-
*/
|
|
1699
|
-
insertAspect(aspectProps) {
|
|
1700
|
-
try {
|
|
1701
|
-
return this._iModel.nativeDb.insertElementAspect(aspectProps);
|
|
1702
|
-
}
|
|
1703
|
-
catch (err) {
|
|
1704
|
-
throw new core_common_1.IModelError(err.errorNumber, `Error inserting ElementAspect [${err.message}], class: ${aspectProps.classFullName}`);
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
/** Update an exist ElementAspect within the iModel.
|
|
1708
|
-
* @param aspectProps The properties to use to update the ElementAspect.
|
|
1709
|
-
* @throws [[IModelError]] if unable to update the ElementAspect.
|
|
1710
|
-
*/
|
|
1711
|
-
updateAspect(aspectProps) {
|
|
1712
|
-
try {
|
|
1713
|
-
this._iModel.nativeDb.updateElementAspect(aspectProps);
|
|
1714
|
-
}
|
|
1715
|
-
catch (err) {
|
|
1716
|
-
throw new core_common_1.IModelError(err.errorNumber, `Error updating ElementAspect [${err.message}], id: ${aspectProps.id}`);
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
/** Delete one or more ElementAspects from this iModel.
|
|
1720
|
-
* @param aspectInstanceIds The set of instance Ids of the ElementAspect(s) to be deleted
|
|
1721
|
-
* @throws [[IModelError]] if unable to delete the ElementAspect.
|
|
1722
|
-
*/
|
|
1723
|
-
deleteAspect(aspectInstanceIds) {
|
|
1724
|
-
const iModel = this._iModel;
|
|
1725
|
-
core_bentley_1.Id64.toIdSet(aspectInstanceIds).forEach((aspectInstanceId) => {
|
|
1726
|
-
try {
|
|
1727
|
-
iModel.nativeDb.deleteElementAspect(aspectInstanceId);
|
|
1728
|
-
}
|
|
1729
|
-
catch (err) {
|
|
1730
|
-
throw new core_common_1.IModelError(err.errorNumber, `Error deleting ElementAspect [${err.message}], id: ${aspectInstanceId}`);
|
|
1731
|
-
}
|
|
1732
|
-
});
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1735
|
-
IModelDb.Elements = Elements;
|
|
1736
|
-
/** The collection of views in an [[IModelDb]].
|
|
1737
|
-
* @public
|
|
1738
|
-
*/
|
|
1739
|
-
class Views {
|
|
1740
|
-
/** @internal */
|
|
1741
|
-
constructor(_iModel) {
|
|
1742
|
-
this._iModel = _iModel;
|
|
1743
|
-
}
|
|
1744
|
-
/** Query for the array of ViewDefinitionProps of the specified class and matching the specified IsPrivate setting.
|
|
1745
|
-
* @param className Query for view definitions of this class.
|
|
1746
|
-
* @param wantPrivate If true, include private view definitions.
|
|
1747
|
-
*/
|
|
1748
|
-
queryViewDefinitionProps(className = "BisCore.ViewDefinition", limit = IModelDb.defaultLimit, offset = 0, wantPrivate = false) {
|
|
1749
|
-
const where = (wantPrivate === false) ? "IsPrivate=FALSE" : "";
|
|
1750
|
-
const ids = this._iModel.queryEntityIds({ from: className, limit, offset, where });
|
|
1751
|
-
const props = [];
|
|
1752
|
-
const imodel = this._iModel;
|
|
1753
|
-
ids.forEach((id) => {
|
|
1754
|
-
try {
|
|
1755
|
-
props.push(imodel.elements.getElementProps(id));
|
|
1756
|
-
}
|
|
1757
|
-
catch (err) { }
|
|
1758
|
-
});
|
|
1759
|
-
return props;
|
|
1760
|
-
}
|
|
1761
|
-
/** Iterate all ViewDefinitions matching the supplied query.
|
|
1762
|
-
* @param params Specifies the query by which views are selected.
|
|
1763
|
-
* @param callback Function invoked for each ViewDefinition matching the query. Return false to terminate iteration, true to continue.
|
|
1764
|
-
* @returns true if all views were iterated, false if iteration was terminated early due to callback returning false.
|
|
1765
|
-
*
|
|
1766
|
-
* **Example: Finding all views of a specific DrawingModel**
|
|
1767
|
-
* ``` ts
|
|
1768
|
-
* [[include:IModelDb.Views.iterateViews]]
|
|
1769
|
-
* ```
|
|
1770
|
-
*/
|
|
1771
|
-
iterateViews(params, callback) {
|
|
1772
|
-
const ids = this._iModel.queryEntityIds(params);
|
|
1773
|
-
let finished = true;
|
|
1774
|
-
for (const id of ids) {
|
|
1775
|
-
try {
|
|
1776
|
-
const view = this._iModel.elements.getElement(id);
|
|
1777
|
-
if (undefined !== view && view instanceof ViewDefinition_1.ViewDefinition) {
|
|
1778
|
-
finished = callback(view);
|
|
1779
|
-
if (!finished)
|
|
1780
|
-
break;
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
catch (err) { }
|
|
1784
|
-
}
|
|
1785
|
-
return finished;
|
|
1786
|
-
}
|
|
1787
|
-
loadViewStateProps(viewDefinitionElement, options, drawingExtents) {
|
|
1788
|
-
const elements = this._iModel.elements;
|
|
1789
|
-
const viewDefinitionProps = viewDefinitionElement.toJSON();
|
|
1790
|
-
const categorySelectorProps = elements.getElementProps(viewDefinitionProps.categorySelectorId);
|
|
1791
|
-
const displayStyleProps = elements.getElementProps({
|
|
1792
|
-
id: viewDefinitionProps.displayStyleId,
|
|
1793
|
-
displayStyle: options === null || options === void 0 ? void 0 : options.displayStyle,
|
|
1794
|
-
});
|
|
1795
|
-
const viewStateData = {
|
|
1796
|
-
viewDefinitionProps,
|
|
1797
|
-
displayStyleProps,
|
|
1798
|
-
categorySelectorProps,
|
|
1799
|
-
};
|
|
1800
|
-
const modelSelectorId = viewDefinitionProps.modelSelectorId;
|
|
1801
|
-
if (modelSelectorId !== undefined) {
|
|
1802
|
-
viewStateData.modelSelectorProps = elements.getElementProps(modelSelectorId);
|
|
1803
|
-
}
|
|
1804
|
-
else if (viewDefinitionElement instanceof ViewDefinition_1.SheetViewDefinition) {
|
|
1805
|
-
viewStateData.sheetProps = elements.getElementProps(viewDefinitionElement.baseModelId);
|
|
1806
|
-
viewStateData.sheetAttachments = Array.from(this._iModel.queryEntityIds({
|
|
1807
|
-
from: "BisCore.ViewAttachment",
|
|
1808
|
-
where: `Model.Id=${viewDefinitionElement.baseModelId}`,
|
|
1809
|
-
}));
|
|
1810
|
-
}
|
|
1811
|
-
else if (viewDefinitionElement instanceof ViewDefinition_1.DrawingViewDefinition) {
|
|
1812
|
-
// Ensure view has known extents
|
|
1813
|
-
if (drawingExtents && !drawingExtents.isNull)
|
|
1814
|
-
viewStateData.modelExtents = drawingExtents.toJSON();
|
|
1815
|
-
// Include information about the associated [[SectionDrawing]], if any.
|
|
1816
|
-
// NB: The SectionDrawing ECClass may not exist in the iModel's version of the BisCore ECSchema.
|
|
1817
|
-
try {
|
|
1818
|
-
const sectionDrawing = this._iModel.elements.tryGetElement(viewDefinitionElement.baseModelId);
|
|
1819
|
-
if (sectionDrawing && sectionDrawing.spatialView && core_bentley_1.Id64.isValidId64(sectionDrawing.spatialView.id)) {
|
|
1820
|
-
viewStateData.sectionDrawing = {
|
|
1821
|
-
spatialView: sectionDrawing.spatialView.id,
|
|
1822
|
-
displaySpatialView: true === sectionDrawing.jsonProperties.displaySpatialView,
|
|
1823
|
-
drawingToSpatialTransform: sectionDrawing.jsonProperties.drawingToSpatialTransform,
|
|
1824
|
-
};
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
catch {
|
|
1828
|
-
//
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
return viewStateData;
|
|
1832
|
-
}
|
|
1833
|
-
/** @deprecated in 3.x. use [[getViewStateProps]]. */
|
|
1834
|
-
getViewStateData(viewDefinitionId, options) {
|
|
1835
|
-
const view = this._iModel.elements.getElement(viewDefinitionId);
|
|
1836
|
-
let drawingExtents;
|
|
1837
|
-
if (view instanceof ViewDefinition_1.DrawingViewDefinition) {
|
|
1838
|
-
try {
|
|
1839
|
-
drawingExtents = core_geometry_1.Range3d.fromJSON(this._iModel.nativeDb.queryModelExtents({ id: view.baseModelId }).modelExtents);
|
|
1840
|
-
}
|
|
1841
|
-
catch {
|
|
1842
|
-
//
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
return this.loadViewStateProps(view, options, drawingExtents);
|
|
1846
|
-
}
|
|
1847
|
-
/** Obtain a [ViewStateProps]($common) for a [[ViewDefinition]] specified by element Id. */
|
|
1848
|
-
async getViewStateProps(viewDefinitionId, options) {
|
|
1849
|
-
const view = this._iModel.elements.getElement(viewDefinitionId);
|
|
1850
|
-
let drawingExtents;
|
|
1851
|
-
if (view instanceof ViewDefinition_1.DrawingViewDefinition)
|
|
1852
|
-
drawingExtents = (await this._iModel.models.queryRange(view.baseModelId));
|
|
1853
|
-
return this.loadViewStateProps(view, options, drawingExtents);
|
|
1854
|
-
}
|
|
1855
|
-
getViewThumbnailArg(viewDefinitionId) {
|
|
1856
|
-
return { namespace: "dgn_View", name: "Thumbnail", id: viewDefinitionId };
|
|
1857
|
-
}
|
|
1858
|
-
/** Get the thumbnail for a view.
|
|
1859
|
-
* @param viewDefinitionId The Id of the view for thumbnail
|
|
1860
|
-
* @returns the ThumbnailProps, or undefined if no thumbnail exists.
|
|
1861
|
-
*/
|
|
1862
|
-
getThumbnail(viewDefinitionId) {
|
|
1863
|
-
const viewArg = this.getViewThumbnailArg(viewDefinitionId);
|
|
1864
|
-
const sizeProps = this._iModel.nativeDb.queryFileProperty(viewArg, true);
|
|
1865
|
-
if (undefined === sizeProps)
|
|
1866
|
-
return undefined;
|
|
1867
|
-
const out = JSON.parse(sizeProps);
|
|
1868
|
-
out.image = this._iModel.nativeDb.queryFileProperty(viewArg, false);
|
|
1869
|
-
return out;
|
|
1870
|
-
}
|
|
1871
|
-
/** Save a thumbnail for a view.
|
|
1872
|
-
* @param viewDefinitionId The Id of the view for thumbnail
|
|
1873
|
-
* @param thumbnail The thumbnail data.
|
|
1874
|
-
* @returns 0 if successful
|
|
1875
|
-
*/
|
|
1876
|
-
saveThumbnail(viewDefinitionId, thumbnail) {
|
|
1877
|
-
const viewArg = this.getViewThumbnailArg(viewDefinitionId);
|
|
1878
|
-
const props = { format: thumbnail.format, height: thumbnail.height, width: thumbnail.width };
|
|
1879
|
-
this._iModel.nativeDb.saveFileProperty(viewArg, JSON.stringify(props), thumbnail.image);
|
|
1880
|
-
return 0;
|
|
1881
|
-
}
|
|
1882
|
-
/** Set the default view property the iModel
|
|
1883
|
-
* @param viewId The Id of the ViewDefinition to use as the default
|
|
1884
|
-
*/
|
|
1885
|
-
setDefaultViewId(viewId) {
|
|
1886
|
-
const spec = { namespace: "dgn_View", name: "DefaultView" };
|
|
1887
|
-
const blob32 = new Uint32Array(2);
|
|
1888
|
-
blob32[0] = core_bentley_1.Id64.getLowerUint32(viewId);
|
|
1889
|
-
blob32[1] = core_bentley_1.Id64.getUpperUint32(viewId);
|
|
1890
|
-
const blob8 = new Uint8Array(blob32.buffer);
|
|
1891
|
-
this._iModel.saveFileProperty(spec, undefined, blob8);
|
|
1892
|
-
}
|
|
1893
|
-
}
|
|
1894
|
-
/** Default parameters for iterating/querying ViewDefinitions. Includes all subclasses of ViewDefinition, excluding only those marked 'private'. */
|
|
1895
|
-
Views.defaultQueryParams = { from: "BisCore.ViewDefinition", where: "IsPrivate=FALSE" };
|
|
1896
|
-
IModelDb.Views = Views;
|
|
1897
|
-
/** Represents the current state of a pollable tile content request.
|
|
1898
|
-
* Note: lack of a "completed" state because polling a completed request returns the content as a Uint8Array.
|
|
1899
|
-
* @internal
|
|
1900
|
-
*/
|
|
1901
|
-
let TileContentState;
|
|
1902
|
-
(function (TileContentState) {
|
|
1903
|
-
TileContentState[TileContentState["New"] = 0] = "New";
|
|
1904
|
-
TileContentState[TileContentState["Pending"] = 1] = "Pending";
|
|
1905
|
-
TileContentState[TileContentState["Loading"] = 2] = "Loading";
|
|
1906
|
-
})(TileContentState = IModelDb.TileContentState || (IModelDb.TileContentState = {}));
|
|
1907
|
-
/** @internal */
|
|
1908
|
-
class Tiles {
|
|
1909
|
-
/** @internal */
|
|
1910
|
-
constructor(_iModel) {
|
|
1911
|
-
this._iModel = _iModel;
|
|
1912
|
-
}
|
|
1913
|
-
/** @internal */
|
|
1914
|
-
async requestTileTreeProps(id) {
|
|
1915
|
-
return new Promise((resolve, reject) => {
|
|
1916
|
-
this._iModel.nativeDb.getTileTree(id, (ret) => {
|
|
1917
|
-
if (undefined !== ret.error)
|
|
1918
|
-
reject(new core_common_1.IModelError(ret.error.status, `TreeId=${id}`));
|
|
1919
|
-
else
|
|
1920
|
-
resolve(ret.result);
|
|
1921
|
-
});
|
|
1922
|
-
});
|
|
1923
|
-
}
|
|
1924
|
-
pollTileContent(resolve, reject, treeId, tileId) {
|
|
1925
|
-
let ret;
|
|
1926
|
-
try {
|
|
1927
|
-
ret = this._iModel.nativeDb.pollTileContent(treeId, tileId);
|
|
1928
|
-
}
|
|
1929
|
-
catch (err) {
|
|
1930
|
-
// Typically "imodel not open".
|
|
1931
|
-
reject(err);
|
|
1932
|
-
return;
|
|
1933
|
-
}
|
|
1934
|
-
if (ret.error) {
|
|
1935
|
-
reject(new core_common_1.IModelError(ret.error.status, `TreeId=${treeId} TileId=${tileId}`));
|
|
1936
|
-
}
|
|
1937
|
-
else if (ret.result && typeof ret.result !== "number") { // if type is not a number, it's the TileContent interface
|
|
1938
|
-
const res = ret.result;
|
|
1939
|
-
const iModelId = this._iModel.iModelId;
|
|
1940
|
-
const tileSizeThreshold = IModelHost_1.IModelHost.logTileSizeThreshold;
|
|
1941
|
-
const tileSize = res.content.length;
|
|
1942
|
-
if (tileSize > tileSizeThreshold) {
|
|
1943
|
-
core_bentley_1.Logger.logWarning(loggerCategory, "Tile size (in bytes) larger than specified threshold", () => ({ tileSize, tileSizeThreshold, treeId, tileId, iModelId }));
|
|
1944
|
-
}
|
|
1945
|
-
const loadTimeThreshold = IModelHost_1.IModelHost.logTileLoadTimeThreshold;
|
|
1946
|
-
const loadTime = res.elapsedSeconds;
|
|
1947
|
-
if (loadTime > loadTimeThreshold) {
|
|
1948
|
-
core_bentley_1.Logger.logWarning(loggerCategory, "Tile load time (in seconds) greater than specified threshold", () => ({ loadTime, loadTimeThreshold, treeId, tileId, iModelId }));
|
|
1949
|
-
}
|
|
1950
|
-
resolve(res);
|
|
1951
|
-
}
|
|
1952
|
-
else { // if the type is a number, it's the TileContentState enum
|
|
1953
|
-
// ###TODO: Decide appropriate timeout interval. May want to switch on state (new vs loading vs pending)
|
|
1954
|
-
setTimeout(() => this.pollTileContent(resolve, reject, treeId, tileId), 10);
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
1957
|
-
/** @internal */
|
|
1958
|
-
async requestTileContent(treeId, tileId) {
|
|
1959
|
-
return new Promise((resolve, reject) => {
|
|
1960
|
-
this.pollTileContent(resolve, reject, treeId, tileId);
|
|
1961
|
-
});
|
|
1962
|
-
}
|
|
1963
|
-
/** @internal */
|
|
1964
|
-
async getTileContent(treeId, tileId) {
|
|
1965
|
-
const ret = await new Promise((resolve) => {
|
|
1966
|
-
this._iModel.nativeDb.getTileContent(treeId, tileId, resolve);
|
|
1967
|
-
});
|
|
1968
|
-
if (undefined !== ret.error) {
|
|
1969
|
-
throw new core_common_1.IModelError(ret.error.status, `TreeId=${treeId} TileId=${tileId}`);
|
|
1970
|
-
}
|
|
1971
|
-
(0, core_bentley_1.assert)(undefined !== ret.result);
|
|
1972
|
-
return ret.result;
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
IModelDb.Tiles = Tiles;
|
|
1976
|
-
})(IModelDb = exports.IModelDb || (exports.IModelDb = {}));
|
|
1977
|
-
/**
|
|
1978
|
-
* A local copy of an iModel from iModelHub that can pull and potentially push changesets.
|
|
1979
|
-
* BriefcaseDb raises a set of events to allow apps and subsystems to track its object life cycle, including [[onOpen]] and [[onOpened]].
|
|
1980
|
-
* @public
|
|
1981
|
-
*/
|
|
1982
|
-
class BriefcaseDb extends IModelDb {
|
|
1983
|
-
constructor(args) {
|
|
1984
|
-
super({ ...args, changeset: args.nativeDb.getCurrentChangeset() });
|
|
1985
|
-
/** Manages local changes to this briefcase. */
|
|
1986
|
-
this.txns = new TxnManager_1.TxnManager(this);
|
|
1987
|
-
this._openMode = args.openMode;
|
|
1988
|
-
this.briefcaseId = args.briefcaseId;
|
|
1989
|
-
if (this.useLockServer) // if the iModel uses a lock server, create a ServerBasedLocks LockControl for this BriefcaseDb.
|
|
1990
|
-
this._locks = new ServerBasedLocks_1.ServerBasedLocks(this);
|
|
1991
|
-
}
|
|
1992
|
-
/** override superclass method */
|
|
1993
|
-
get isBriefcase() { return true; }
|
|
1994
|
-
static findByKey(key) {
|
|
1995
|
-
return super.findByKey(key);
|
|
1996
|
-
}
|
|
1997
|
-
static tryFindByKey(key) {
|
|
1998
|
-
const db = super.tryFindByKey(key);
|
|
1999
|
-
return (db === null || db === void 0 ? void 0 : db.isBriefcaseDb()) ? db : undefined;
|
|
2000
|
-
}
|
|
2001
|
-
/**
|
|
2002
|
-
* The Guid that identifies the *context* that owns this iModel.
|
|
2003
|
-
* GuidString | undefined for the superclass, but required for BriefcaseDb
|
|
2004
|
-
* */
|
|
2005
|
-
get iTwinId() { return super.iTwinId; } // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
2006
|
-
/**
|
|
2007
|
-
* Determine whether this BriefcaseDb should use a lock server.
|
|
2008
|
-
* All must be true:
|
|
2009
|
-
* - file is open for write
|
|
2010
|
-
* - has an assigned briefcaseId
|
|
2011
|
-
* - the "no locking" flag is not present. This is a property of an iModel, established when the iModel is created in IModelHub.
|
|
2012
|
-
*/
|
|
2013
|
-
get useLockServer() {
|
|
2014
|
-
return !this.isReadonly && (this.briefcaseId !== core_common_1.BriefcaseIdValue.Unassigned) && (undefined === this.nativeDb.queryLocalValue(BriefcaseLocalValue.NoLocking));
|
|
2015
|
-
}
|
|
2016
|
-
/** Upgrades the profile or domain schemas. File must be closed before this call and is always left closed. */
|
|
2017
|
-
static async doUpgrade(briefcase, upgradeOptions, description) {
|
|
2018
|
-
const nativeDb = this.openDgnDb({ path: briefcase.fileName }, core_bentley_1.OpenMode.ReadWrite, upgradeOptions); // performs the upgrade
|
|
2019
|
-
const wasChanges = nativeDb.hasPendingTxns();
|
|
2020
|
-
nativeDb.closeIModel();
|
|
2021
|
-
if (wasChanges)
|
|
2022
|
-
await withBriefcaseDb(briefcase, async (db) => db.pushChanges({ ...briefcase, description, retainLocks: true }));
|
|
2023
|
-
}
|
|
2024
|
-
/** Upgrades the schemas in the iModel based on the current version of the software. Follows a sequence of operations -
|
|
2025
|
-
* * Acquires a schema lock to prevent other users from making any other changes while upgrade is happening
|
|
2026
|
-
* * Updates the local briefcase with the schema changes.
|
|
2027
|
-
* * Pushes the resulting changeset(s) to iModelHub.
|
|
2028
|
-
* Note that the upgrade requires that the local briefcase be closed, and may result in one or two change sets depending on whether both
|
|
2029
|
-
* profile and domain schemas need to get upgraded.
|
|
2030
|
-
* @see ($docs/learning/backend/IModelDb.md#upgrading-schemas-in-an-imodel)
|
|
2031
|
-
*/
|
|
2032
|
-
static async upgradeSchemas(briefcase) {
|
|
2033
|
-
// upgrading schemas involves closing and reopening the file repeatedly. That's because the process of upgrading
|
|
2034
|
-
// happens on a file open. We have to open-and-close the file at *each* of these steps:
|
|
2035
|
-
// - acquire schema lock
|
|
2036
|
-
// - upgrade profile
|
|
2037
|
-
// - push changes
|
|
2038
|
-
// - upgrade domain
|
|
2039
|
-
// - push changes
|
|
2040
|
-
// - release schema lock
|
|
2041
|
-
// good thing computers are fast. Fortunately upgrading should be rare (and the push time will dominate anyway.) Don't try to optimize any of this away.
|
|
2042
|
-
await withBriefcaseDb(briefcase, async (db) => db.acquireSchemaLock()); // may not really acquire lock if iModel uses "noLocks" mode.
|
|
2043
|
-
try {
|
|
2044
|
-
await this.doUpgrade(briefcase, { profile: core_common_1.ProfileOptions.Upgrade, schemaLockHeld: true }, "Upgraded profile");
|
|
2045
|
-
await this.doUpgrade(briefcase, { domain: core_common_1.DomainOptions.Upgrade, schemaLockHeld: true }, "Upgraded domain schemas");
|
|
2046
|
-
}
|
|
2047
|
-
finally {
|
|
2048
|
-
await withBriefcaseDb(briefcase, async (db) => db.locks.releaseAllLocks());
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
/** Open a briefcase file and return a new BriefcaseDb to interact with it.
|
|
2052
|
-
* @param args parameters that specify the file name, and options for opening the briefcase file
|
|
2053
|
-
*/
|
|
2054
|
-
static async open(args) {
|
|
2055
|
-
var _a;
|
|
2056
|
-
this.onOpen.raiseEvent(args);
|
|
2057
|
-
const file = { path: args.fileName, key: args.key };
|
|
2058
|
-
const openMode = args.readonly ? core_bentley_1.OpenMode.Readonly : core_bentley_1.OpenMode.ReadWrite;
|
|
2059
|
-
const nativeDb = this.openDgnDb(file, openMode, undefined, args);
|
|
2060
|
-
const briefcaseDb = new BriefcaseDb({ nativeDb, key: (_a = file.key) !== null && _a !== void 0 ? _a : core_bentley_1.Guid.createValue(), openMode, briefcaseId: nativeDb.getBriefcaseId() });
|
|
2061
|
-
if (openMode === core_bentley_1.OpenMode.ReadWrite && CodeService_1.CodeService.createForIModel) {
|
|
2062
|
-
try {
|
|
2063
|
-
const codeService = CodeService_1.CodeService.createForIModel(briefcaseDb);
|
|
2064
|
-
briefcaseDb._codeService = codeService;
|
|
2065
|
-
this.onCodeServiceCreated.raiseEvent(codeService);
|
|
2066
|
-
}
|
|
2067
|
-
catch (e) {
|
|
2068
|
-
if (e.errorId !== "NoCodeIndex") // no code index means iModel isn't enforcing codes.
|
|
2069
|
-
throw e;
|
|
2070
|
-
}
|
|
2071
|
-
}
|
|
2072
|
-
BriefcaseManager_1.BriefcaseManager.logUsage(briefcaseDb);
|
|
2073
|
-
this.onOpened.raiseEvent(briefcaseDb, args);
|
|
2074
|
-
return briefcaseDb;
|
|
2075
|
-
}
|
|
2076
|
-
closeAndReopen(openMode) {
|
|
2077
|
-
const fileName = this.pathName;
|
|
2078
|
-
this.nativeDb.closeIModel();
|
|
2079
|
-
this.nativeDb.openIModel(fileName, openMode);
|
|
2080
|
-
}
|
|
2081
|
-
/** Pull and apply changesets from iModelHub */
|
|
2082
|
-
async pullChanges(arg) {
|
|
2083
|
-
if (this.isReadonly) // we allow pulling changes into a briefcase that is readonly - close and reopen it writeable
|
|
2084
|
-
this.closeAndReopen(core_bentley_1.OpenMode.ReadWrite);
|
|
2085
|
-
try {
|
|
2086
|
-
await BriefcaseManager_1.BriefcaseManager.pullAndApplyChangesets(this, arg !== null && arg !== void 0 ? arg : {});
|
|
2087
|
-
this.initializeIModelDb();
|
|
2088
|
-
}
|
|
2089
|
-
finally {
|
|
2090
|
-
if (this.isReadonly) // if the briefcase was opened readonly - close and reopen it readonly
|
|
2091
|
-
this.closeAndReopen(core_bentley_1.OpenMode.Readonly);
|
|
2092
|
-
}
|
|
2093
|
-
IpcHost_1.IpcHost.notifyTxns(this, "notifyPulledChanges", this.changeset);
|
|
2094
|
-
}
|
|
2095
|
-
/** Push changes to iModelHub. */
|
|
2096
|
-
async pushChanges(arg) {
|
|
2097
|
-
if (this.briefcaseId === core_common_1.BriefcaseIdValue.Unassigned)
|
|
2098
|
-
return;
|
|
2099
|
-
if (this.nativeDb.hasUnsavedChanges())
|
|
2100
|
-
throw new core_common_1.IModelError(core_bentley_1.ChangeSetStatus.HasUncommittedChanges, "Cannot push with unsaved changes");
|
|
2101
|
-
if (!this.nativeDb.hasPendingTxns())
|
|
2102
|
-
return; // nothing to push
|
|
2103
|
-
await BriefcaseManager_1.BriefcaseManager.pullMergePush(this, arg);
|
|
2104
|
-
this.initializeIModelDb();
|
|
2105
|
-
const changeset = this.changeset;
|
|
2106
|
-
IpcHost_1.IpcHost.notifyTxns(this, "notifyPushedChanges", changeset);
|
|
2107
|
-
}
|
|
2108
|
-
}
|
|
2109
|
-
exports.BriefcaseDb = BriefcaseDb;
|
|
2110
|
-
/**
|
|
2111
|
-
* Event raised just before a BriefcaseDb is opened. Supplies the arguments that will be used to open the BriefcaseDb.
|
|
2112
|
-
* Throw an exception to stop the open.
|
|
2113
|
-
*
|
|
2114
|
-
* **Example:**
|
|
2115
|
-
* ``` ts
|
|
2116
|
-
* [[include:BriefcaseDb.onOpen]]
|
|
2117
|
-
* ```
|
|
2118
|
-
*/
|
|
2119
|
-
BriefcaseDb.onOpen = new core_bentley_1.BeEvent();
|
|
2120
|
-
/**
|
|
2121
|
-
* Event raised just after a BriefcaseDb is opened. Supplies the newly opened BriefcaseDb and the arguments that were used to open it.
|
|
2122
|
-
*
|
|
2123
|
-
* **Example:**
|
|
2124
|
-
* ``` ts
|
|
2125
|
-
* [[include:BriefcaseDb.onOpened]]
|
|
2126
|
-
* ```
|
|
2127
|
-
*/
|
|
2128
|
-
BriefcaseDb.onOpened = new core_bentley_1.BeEvent();
|
|
2129
|
-
/** @alpha */
|
|
2130
|
-
BriefcaseDb.onCodeServiceCreated = new core_bentley_1.BeEvent();
|
|
2131
|
-
/** Used to reattach Daemon from a user's accessToken for V2 checkpoints.
|
|
2132
|
-
* @note Reattach only happens if the previous access token either has expired or is about to expire within an application-supplied safety duration.
|
|
2133
|
-
*/
|
|
2134
|
-
class RefreshV2CheckpointSas {
|
|
2135
|
-
constructor(sasToken, safetySeconds) {
|
|
2136
|
-
/** the time at which the current token should be refreshed (its expiry minus safetySeconds) */
|
|
2137
|
-
this._timestamp = 0;
|
|
2138
|
-
this._safetySeconds = safetySeconds !== null && safetySeconds !== void 0 ? safetySeconds : 60 * 60; // default to 1 hour
|
|
2139
|
-
this.setTimestamp(sasToken);
|
|
2140
|
-
}
|
|
2141
|
-
async performRefresh(accessToken, iModel) {
|
|
2142
|
-
this._timestamp = 0; // everyone needs to wait until token is valid
|
|
2143
|
-
// we're going to request that the checkpoint manager use this user's accessToken to obtain a new access token for this checkpoint's storage account.
|
|
2144
|
-
core_bentley_1.Logger.logInfo(BackendLoggerCategory_1.BackendLoggerCategory.Authorization, "attempting to refresh sasToken for checkpoint");
|
|
2145
|
-
try {
|
|
2146
|
-
// this exchanges the supplied user accessToken for an expiring blob-store token to read the checkpoint.
|
|
2147
|
-
const container = iModel.nativeDb.cloudContainer;
|
|
2148
|
-
if (!container)
|
|
2149
|
-
throw new Error("checkpoint is not from a cloud container");
|
|
2150
|
-
(0, core_bentley_1.assert)(undefined !== iModel.iTwinId);
|
|
2151
|
-
const props = await IModelHost_1.IModelHost.hubAccess.queryV2Checkpoint({ accessToken, iTwinId: iModel.iTwinId, iModelId: iModel.iModelId, changeset: iModel.changeset });
|
|
2152
|
-
if (!props)
|
|
2153
|
-
throw new Error("can't reset checkpoint sas token");
|
|
2154
|
-
container.accessToken = props.sasToken;
|
|
2155
|
-
this.setTimestamp(props.sasToken);
|
|
2156
|
-
core_bentley_1.Logger.logInfo(BackendLoggerCategory_1.BackendLoggerCategory.Authorization, "refreshed checkpoint sasToken successfully");
|
|
2157
|
-
}
|
|
2158
|
-
finally {
|
|
2159
|
-
this._promise = undefined;
|
|
2160
|
-
}
|
|
2161
|
-
}
|
|
2162
|
-
setTimestamp(sasToken) {
|
|
2163
|
-
const exp = new URLSearchParams(sasToken).get("se");
|
|
2164
|
-
const sasTokenExpiry = exp ? Date.parse(exp) : 0;
|
|
2165
|
-
this._timestamp = sasTokenExpiry - (this._safetySeconds * 1000);
|
|
2166
|
-
if (this._timestamp < Date.now())
|
|
2167
|
-
core_bentley_1.Logger.logError(BackendLoggerCategory_1.BackendLoggerCategory.Authorization, "attached with timestamp that expires before safety interval");
|
|
2168
|
-
}
|
|
2169
|
-
async refreshSas(accessToken, iModel) {
|
|
2170
|
-
if (this._timestamp > Date.now())
|
|
2171
|
-
return; // current token is fine
|
|
2172
|
-
if (undefined === this._promise) // has reattach already begun?
|
|
2173
|
-
this._promise = this.performRefresh(accessToken, iModel); // no, start it
|
|
2174
|
-
return this._promise;
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
/** A *snapshot* iModel database file that is used for archival and data transfer purposes.
|
|
2178
|
-
* @see [Snapshot iModels]($docs/learning/backend/AccessingIModels.md#snapshot-imodels)
|
|
2179
|
-
* @see [About IModelDb]($docs/learning/backend/IModelDb.md)
|
|
2180
|
-
* @public
|
|
2181
|
-
*/
|
|
2182
|
-
class SnapshotDb extends IModelDb {
|
|
2183
|
-
constructor(nativeDb, key) {
|
|
2184
|
-
super({ nativeDb, key, changeset: nativeDb.getCurrentChangeset() });
|
|
2185
|
-
this._openMode = nativeDb.isReadonly() ? core_bentley_1.OpenMode.Readonly : core_bentley_1.OpenMode.ReadWrite;
|
|
2186
|
-
}
|
|
2187
|
-
get isSnapshot() { return true; }
|
|
2188
|
-
static findByKey(key) {
|
|
2189
|
-
return super.findByKey(key);
|
|
2190
|
-
}
|
|
2191
|
-
static tryFindByKey(key) {
|
|
2192
|
-
const db = super.tryFindByKey(key);
|
|
2193
|
-
return (db === null || db === void 0 ? void 0 : db.isSnapshotDb()) ? db : undefined;
|
|
2194
|
-
}
|
|
2195
|
-
/** Create an *empty* local [Snapshot]($docs/learning/backend/AccessingIModels.md#snapshot-imodels) iModel file.
|
|
2196
|
-
* Snapshots are not synchronized with iModelHub, so do not have a change timeline.
|
|
2197
|
-
* > Note: A *snapshot* cannot be modified after [[close]] is called.
|
|
2198
|
-
* @param filePath The file that will contain the new iModel *snapshot*
|
|
2199
|
-
* @param options The parameters that define the new iModel *snapshot*
|
|
2200
|
-
* @returns A writeable SnapshotDb
|
|
2201
|
-
* @see [Snapshot iModels]($docs/learning/backend/AccessingIModels.md#snapshot-imodels)
|
|
2202
|
-
*/
|
|
2203
|
-
static createEmpty(filePath, options) {
|
|
2204
|
-
const nativeDb = new IModelHost_1.IModelHost.platform.DgnDb();
|
|
2205
|
-
nativeDb.createIModel(filePath, options);
|
|
2206
|
-
nativeDb.resetBriefcaseId(core_common_1.BriefcaseIdValue.Unassigned);
|
|
2207
|
-
const snapshotDb = new SnapshotDb(nativeDb, core_bentley_1.Guid.createValue());
|
|
2208
|
-
if (options.createClassViews)
|
|
2209
|
-
snapshotDb._createClassViewsOnClose = true; // save flag that will be checked when close() is called
|
|
2210
|
-
return snapshotDb;
|
|
2211
|
-
}
|
|
2212
|
-
/** Create a local [Snapshot]($docs/learning/backend/AccessingIModels.md#snapshot-imodels) iModel file, using this iModel as a *seed* or starting point.
|
|
2213
|
-
* Snapshots are not synchronized with iModelHub, so do not have a change timeline.
|
|
2214
|
-
* > Note: A *snapshot* cannot be modified after [[close]] is called.
|
|
2215
|
-
* @param iModelDb The snapshot will be initialized from the current contents of this iModelDb
|
|
2216
|
-
* @param snapshotFile The file that will contain the new iModel *snapshot*
|
|
2217
|
-
* @param options Optional properties that determine how the snapshot iModel is created.
|
|
2218
|
-
* @returns A writeable SnapshotDb
|
|
2219
|
-
* @see [Snapshot iModels]($docs/learning/backend/AccessingIModels.md#snapshot-imodels)
|
|
2220
|
-
*/
|
|
2221
|
-
static createFrom(iModelDb, snapshotFile, options) {
|
|
2222
|
-
iModelDb.performCheckpoint();
|
|
2223
|
-
IModelJsFs_1.IModelJsFs.copySync(iModelDb.pathName, snapshotFile);
|
|
2224
|
-
const nativeDb = new IModelHost_1.IModelHost.platform.DgnDb();
|
|
2225
|
-
nativeDb.openIModel(snapshotFile, core_bentley_1.OpenMode.ReadWrite, undefined, options);
|
|
2226
|
-
nativeDb.vacuum();
|
|
2227
|
-
// Replace iModelId if seedFile is a snapshot, preserve iModelId if seedFile is an iModelHub-managed briefcase
|
|
2228
|
-
if (!BriefcaseManager_1.BriefcaseManager.isValidBriefcaseId(nativeDb.getBriefcaseId()))
|
|
2229
|
-
nativeDb.setIModelId(core_bentley_1.Guid.createValue());
|
|
2230
|
-
nativeDb.deleteLocalValue(BriefcaseLocalValue.StandaloneEdit);
|
|
2231
|
-
nativeDb.saveChanges();
|
|
2232
|
-
nativeDb.deleteAllTxns();
|
|
2233
|
-
nativeDb.resetBriefcaseId(core_common_1.BriefcaseIdValue.Unassigned);
|
|
2234
|
-
const snapshotDb = new SnapshotDb(nativeDb, core_bentley_1.Guid.createValue());
|
|
2235
|
-
if (options === null || options === void 0 ? void 0 : options.createClassViews)
|
|
2236
|
-
snapshotDb._createClassViewsOnClose = true; // save flag that will be checked when close() is called
|
|
2237
|
-
return snapshotDb;
|
|
2238
|
-
}
|
|
2239
|
-
/** open this SnapshotDb read/write, strictly to apply incoming changesets. Used for creating new checkpoints.
|
|
2240
|
-
* @internal
|
|
2241
|
-
*/
|
|
2242
|
-
static openForApplyChangesets(path, props) {
|
|
2243
|
-
const file = { path, key: props === null || props === void 0 ? void 0 : props.key };
|
|
2244
|
-
const nativeDb = this.openDgnDb(file, core_bentley_1.OpenMode.ReadWrite, undefined, props);
|
|
2245
|
-
(0, core_bentley_1.assert)(undefined !== file.key);
|
|
2246
|
-
return new SnapshotDb(nativeDb, file.key);
|
|
2247
|
-
}
|
|
2248
|
-
/** Open a read-only iModel *snapshot*.
|
|
2249
|
-
* @param path the full path of the snapshot iModel file to open.
|
|
2250
|
-
* @param props options for opening snapshot
|
|
2251
|
-
* @see [[close]]
|
|
2252
|
-
* @throws [[IModelError]] If the file is not found or is not a valid *snapshot*.
|
|
2253
|
-
*/
|
|
2254
|
-
static openFile(path, opts) {
|
|
2255
|
-
const file = { path, key: opts === null || opts === void 0 ? void 0 : opts.key };
|
|
2256
|
-
const nativeDb = this.openDgnDb(file, core_bentley_1.OpenMode.Readonly, undefined, opts);
|
|
2257
|
-
(0, core_bentley_1.assert)(undefined !== file.key);
|
|
2258
|
-
return new SnapshotDb(nativeDb, file.key);
|
|
2259
|
-
}
|
|
2260
|
-
/** Open a previously downloaded V1 checkpoint file.
|
|
2261
|
-
* @note The key is generated by this call is predictable and is formed from the IModelId and ChangeSetId.
|
|
2262
|
-
* This is so every backend working on the same checkpoint will use the same key, to permit multiple backends
|
|
2263
|
-
* servicing the same checkpoint.
|
|
2264
|
-
* @internal
|
|
2265
|
-
*/
|
|
2266
|
-
static openCheckpointV1(fileName, checkpoint) {
|
|
2267
|
-
const snapshot = this.openFile(fileName, { key: CheckpointManager_1.CheckpointManager.getKey(checkpoint) });
|
|
2268
|
-
snapshot._iTwinId = checkpoint.iTwinId;
|
|
2269
|
-
return snapshot;
|
|
2270
|
-
}
|
|
2271
|
-
/** Open a V2 *checkpoint*, a special form of snapshot iModel that represents a read-only snapshot of an iModel from iModelHub at a particular point in time.
|
|
2272
|
-
* > Note: The checkpoint daemon must already be running and a checkpoint must already exist in iModelHub's storage *before* this function is called.
|
|
2273
|
-
* @param checkpoint The checkpoint to open
|
|
2274
|
-
* @note The key generated by this call is predictable and is formed from the IModelId and ChangeSetId.
|
|
2275
|
-
* This is so every backend working on the same checkpoint will use the same key, to permit multiple backends
|
|
2276
|
-
* servicing the same checkpoint.
|
|
2277
|
-
* @throws [[IModelError]] If the checkpoint is not found in iModelHub or the checkpoint daemon is not supported in the current environment.
|
|
2278
|
-
* @internal
|
|
2279
|
-
*/
|
|
2280
|
-
static async openCheckpointV2(checkpoint) {
|
|
2281
|
-
const { dbName, container } = await CheckpointManager_1.V2CheckpointManager.attach(checkpoint);
|
|
2282
|
-
const key = CheckpointManager_1.CheckpointManager.getKey(checkpoint);
|
|
2283
|
-
const tempFileBase = (0, path_1.join)(IModelHost_1.IModelHost.cacheDir, `${checkpoint.iModelId}\$${checkpoint.changeset.id}`); // temp files for this checkpoint should go in the cacheDir.
|
|
2284
|
-
const snapshot = SnapshotDb.openFile(dbName, { key, tempFileBase, container });
|
|
2285
|
-
snapshot._iTwinId = checkpoint.iTwinId;
|
|
2286
|
-
try {
|
|
2287
|
-
CheckpointManager_1.CheckpointManager.validateCheckpointGuids(checkpoint, snapshot);
|
|
2288
|
-
}
|
|
2289
|
-
catch (err) {
|
|
2290
|
-
snapshot.close();
|
|
2291
|
-
throw err;
|
|
2292
|
-
}
|
|
2293
|
-
snapshot._refreshSas = new RefreshV2CheckpointSas(container.accessToken, checkpoint.reattachSafetySeconds);
|
|
2294
|
-
return snapshot;
|
|
2295
|
-
}
|
|
2296
|
-
/** Used to refresh the container sasToken using the current user's accessToken
|
|
2297
|
-
* @internal
|
|
2298
|
-
*/
|
|
2299
|
-
async refreshContainerSas(userAccessToken) {
|
|
2300
|
-
var _a;
|
|
2301
|
-
return (_a = this._refreshSas) === null || _a === void 0 ? void 0 : _a.refreshSas(userAccessToken, this);
|
|
2302
|
-
}
|
|
2303
|
-
/** @internal */
|
|
2304
|
-
beforeClose() {
|
|
2305
|
-
super.beforeClose();
|
|
2306
|
-
if (this._createClassViewsOnClose) { // check for flag set during create
|
|
2307
|
-
if (core_bentley_1.BentleyStatus.SUCCESS !== this.nativeDb.createClassViewsInDb()) {
|
|
2308
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.SQLiteError, "Error creating class views");
|
|
2309
|
-
}
|
|
2310
|
-
else {
|
|
2311
|
-
this.saveChanges();
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
exports.SnapshotDb = SnapshotDb;
|
|
2317
|
-
/**
|
|
2318
|
-
* Standalone iModels are read/write files that are not associated with an iTwin or managed by iModelHub.
|
|
2319
|
-
* They are relevant only for testing, or for small-scale single-user scenarios.
|
|
2320
|
-
* Standalone iModels are designed such that the API for Standalone iModels and Briefcase
|
|
2321
|
-
* iModels (those synchronized with iModelHub) are as similar and consistent as possible.
|
|
2322
|
-
* This leads to a straightforward process where the a user starts with StandaloneDb and can
|
|
2323
|
-
* optionally choose to upgrade to an iTwin.
|
|
2324
|
-
*
|
|
2325
|
-
* Some additional details. Standalone iModels:
|
|
2326
|
-
* - always have [Guid.empty]($bentley) for their iTwinId (they are "unassociated" files)
|
|
2327
|
-
* - always have BriefcaseId === [BriefcaseIdValue.Unassigned]($common)
|
|
2328
|
-
* - are connected to the frontend via [BriefcaseConnection.openStandalone]($frontend)
|
|
2329
|
-
* - may be opened without supplying any user credentials
|
|
2330
|
-
* - may be opened read/write
|
|
2331
|
-
* - cannot apply a changeset to nor generate a changesets (since there is no timeline from which to get/push changesets)
|
|
2332
|
-
* @public
|
|
2333
|
-
*/
|
|
2334
|
-
class StandaloneDb extends BriefcaseDb {
|
|
2335
|
-
get isStandalone() { return true; }
|
|
2336
|
-
get useLockServer() { return false; } // standalone iModels have no lock server
|
|
2337
|
-
static findByKey(key) {
|
|
2338
|
-
return super.findByKey(key);
|
|
2339
|
-
}
|
|
2340
|
-
static tryFindByKey(key) {
|
|
2341
|
-
const db = super.tryFindByKey(key);
|
|
2342
|
-
return (db === null || db === void 0 ? void 0 : db.isStandaloneDb()) ? db : undefined;
|
|
2343
|
-
}
|
|
2344
|
-
/** Create an *empty* standalone iModel.
|
|
2345
|
-
* @param filePath The file path for the iModel
|
|
2346
|
-
* @param args The parameters that define the new iModel
|
|
2347
|
-
*/
|
|
2348
|
-
static createEmpty(filePath, args) {
|
|
2349
|
-
const nativeDb = new IModelHost_1.IModelHost.platform.DgnDb();
|
|
2350
|
-
nativeDb.createIModel(filePath, args);
|
|
2351
|
-
nativeDb.saveLocalValue(BriefcaseLocalValue.StandaloneEdit, args.allowEdit);
|
|
2352
|
-
nativeDb.setITwinId(core_bentley_1.Guid.empty);
|
|
2353
|
-
nativeDb.resetBriefcaseId(core_common_1.BriefcaseIdValue.Unassigned);
|
|
2354
|
-
nativeDb.saveChanges();
|
|
2355
|
-
return new StandaloneDb({ nativeDb, key: core_bentley_1.Guid.createValue(), briefcaseId: core_common_1.BriefcaseIdValue.Unassigned, openMode: core_bentley_1.OpenMode.ReadWrite });
|
|
2356
|
-
}
|
|
2357
|
-
/**
|
|
2358
|
-
* Upgrades the schemas in the standalone iModel file.
|
|
2359
|
-
* Note that the upgrade requires that the file be closed, and will leave it back in the closed state.
|
|
2360
|
-
* @param filePath Full path name of the standalone iModel file.
|
|
2361
|
-
* @see ($docs/learning/backend/IModelDb.md#upgrading-schemas-in-an-imodel)
|
|
2362
|
-
* @see [[StandaloneDb.validateSchemas]]
|
|
2363
|
-
*/
|
|
2364
|
-
static upgradeStandaloneSchemas(filePath) {
|
|
2365
|
-
let nativeDb = this.openDgnDb({ path: filePath }, core_bentley_1.OpenMode.ReadWrite, { profile: core_common_1.ProfileOptions.Upgrade, schemaLockHeld: true });
|
|
2366
|
-
nativeDb.closeIModel();
|
|
2367
|
-
nativeDb = this.openDgnDb({ path: filePath }, core_bentley_1.OpenMode.ReadWrite, { domain: core_common_1.DomainOptions.Upgrade, schemaLockHeld: true });
|
|
2368
|
-
nativeDb.closeIModel();
|
|
2369
|
-
}
|
|
2370
|
-
/** Open a standalone iModel file.
|
|
2371
|
-
* @param filePath The path of the standalone iModel file.
|
|
2372
|
-
* @param openMode Optional open mode for the standalone iModel. The default is read/write.
|
|
2373
|
-
* @throws [[IModelError]] if the file is not a standalone iModel.
|
|
2374
|
-
* @see [BriefcaseConnection.openStandalone]($frontend) to open a StandaloneDb from the frontend
|
|
2375
|
-
*/
|
|
2376
|
-
static openFile(filePath, openMode = core_bentley_1.OpenMode.ReadWrite, options) {
|
|
2377
|
-
const file = { path: filePath, key: options === null || options === void 0 ? void 0 : options.key };
|
|
2378
|
-
const nativeDb = this.openDgnDb(file, openMode, undefined, options);
|
|
2379
|
-
try {
|
|
2380
|
-
const iTwinId = nativeDb.getITwinId();
|
|
2381
|
-
if (iTwinId !== core_bentley_1.Guid.empty) // a "standalone" iModel means it is not associated with an iTwin
|
|
2382
|
-
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.WrongIModel, `${filePath} is not a Standalone iModel. iTwinId=${iTwinId}`);
|
|
2383
|
-
(0, core_bentley_1.assert)(undefined !== file.key);
|
|
2384
|
-
return new StandaloneDb({ nativeDb, key: file.key, openMode, briefcaseId: core_common_1.BriefcaseIdValue.Unassigned });
|
|
2385
|
-
}
|
|
2386
|
-
catch (error) {
|
|
2387
|
-
nativeDb.closeIModel();
|
|
2388
|
-
throw error;
|
|
2389
|
-
}
|
|
2390
|
-
}
|
|
2391
|
-
}
|
|
2392
|
-
exports.StandaloneDb = StandaloneDb;
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
/** @packageDocumentation
|
|
7
|
+
* @module iModels
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.StandaloneDb = exports.SnapshotDb = exports.BriefcaseDb = exports.IModelDb = exports.BriefcaseLocalValue = void 0;
|
|
11
|
+
const path_1 = require("path");
|
|
12
|
+
const core_bentley_1 = require("@itwin/core-bentley");
|
|
13
|
+
const core_common_1 = require("@itwin/core-common");
|
|
14
|
+
const core_geometry_1 = require("@itwin/core-geometry");
|
|
15
|
+
const BackendLoggerCategory_1 = require("./BackendLoggerCategory");
|
|
16
|
+
const BriefcaseManager_1 = require("./BriefcaseManager");
|
|
17
|
+
const CheckpointManager_1 = require("./CheckpointManager");
|
|
18
|
+
const ClassRegistry_1 = require("./ClassRegistry");
|
|
19
|
+
const CodeService_1 = require("./CodeService");
|
|
20
|
+
const CodeSpecs_1 = require("./CodeSpecs");
|
|
21
|
+
const ConcurrentQuery_1 = require("./ConcurrentQuery");
|
|
22
|
+
const ECSqlStatement_1 = require("./ECSqlStatement");
|
|
23
|
+
const Element_1 = require("./Element");
|
|
24
|
+
const ElementAspect_1 = require("./ElementAspect");
|
|
25
|
+
const ElementGraphics_1 = require("./ElementGraphics");
|
|
26
|
+
const GeoCoordConfig_1 = require("./GeoCoordConfig");
|
|
27
|
+
const IModelHost_1 = require("./IModelHost");
|
|
28
|
+
const IModelJsFs_1 = require("./IModelJsFs");
|
|
29
|
+
const IpcHost_1 = require("./IpcHost");
|
|
30
|
+
const Model_1 = require("./Model");
|
|
31
|
+
const Relationship_1 = require("./Relationship");
|
|
32
|
+
const ServerBasedLocks_1 = require("./ServerBasedLocks");
|
|
33
|
+
const SqliteStatement_1 = require("./SqliteStatement");
|
|
34
|
+
const TxnManager_1 = require("./TxnManager");
|
|
35
|
+
const ViewDefinition_1 = require("./ViewDefinition");
|
|
36
|
+
const Settings_1 = require("./workspace/Settings");
|
|
37
|
+
const Workspace_1 = require("./workspace/Workspace");
|
|
38
|
+
// spell:ignore fontid fontmap
|
|
39
|
+
const loggerCategory = BackendLoggerCategory_1.BackendLoggerCategory.IModelDb;
|
|
40
|
+
/** A null-implementation of LockControl that does not attempt to limit access between briefcases. This relies on change-merging to resolve conflicts. */
|
|
41
|
+
class NoLocks {
|
|
42
|
+
get isServerBased() { return false; }
|
|
43
|
+
close() { }
|
|
44
|
+
clearAllLocks() { }
|
|
45
|
+
holdsExclusiveLock() { return false; }
|
|
46
|
+
holdsSharedLock() { return false; }
|
|
47
|
+
checkExclusiveLock() { }
|
|
48
|
+
checkSharedLock() { }
|
|
49
|
+
elementWasCreated() { }
|
|
50
|
+
async acquireLocks() { }
|
|
51
|
+
async releaseAllLocks() { }
|
|
52
|
+
}
|
|
53
|
+
/** @internal */
|
|
54
|
+
var BriefcaseLocalValue;
|
|
55
|
+
(function (BriefcaseLocalValue) {
|
|
56
|
+
BriefcaseLocalValue["StandaloneEdit"] = "StandaloneEdit";
|
|
57
|
+
BriefcaseLocalValue["NoLocking"] = "NoLocking";
|
|
58
|
+
})(BriefcaseLocalValue = exports.BriefcaseLocalValue || (exports.BriefcaseLocalValue = {}));
|
|
59
|
+
// function to open an briefcaseDb, perform an operation, and then close it.
|
|
60
|
+
const withBriefcaseDb = async (briefcase, fn) => {
|
|
61
|
+
const db = await BriefcaseDb.open(briefcase);
|
|
62
|
+
try {
|
|
63
|
+
return await fn(db);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
db.close();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Settings for an individual iModel. May only include settings priority for iModel, iTwin and organization.
|
|
71
|
+
* @note if there is more than one iModel for an iTwin or organization, they will *each* hold a copy of the settings for those priorities.
|
|
72
|
+
*/
|
|
73
|
+
class IModelSettings extends Settings_1.BaseSettings {
|
|
74
|
+
verifyPriority(priority) {
|
|
75
|
+
if (priority <= Settings_1.SettingsPriority.application)
|
|
76
|
+
throw new Error("Use IModelHost.appSettings");
|
|
77
|
+
}
|
|
78
|
+
// attempt to resolve a setting from this iModel's settings, otherwise use appWorkspace's settings, otherwise defaultValue.
|
|
79
|
+
resolveSetting(name, resolver, defaultValue) {
|
|
80
|
+
var _a;
|
|
81
|
+
return (_a = super.resolveSetting(name, resolver)) !== null && _a !== void 0 ? _a : IModelHost_1.IModelHost.appWorkspace.settings.resolveSetting(name, resolver, defaultValue);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** An iModel database file. The database file can either be a briefcase or a snapshot.
|
|
85
|
+
* @see [Accessing iModels]($docs/learning/backend/AccessingIModels.md)
|
|
86
|
+
* @see [About IModelDb]($docs/learning/backend/IModelDb.md)
|
|
87
|
+
* @public
|
|
88
|
+
*/
|
|
89
|
+
class IModelDb extends core_common_1.IModel {
|
|
90
|
+
/** @internal */
|
|
91
|
+
constructor(args) {
|
|
92
|
+
super({ ...args, iTwinId: args.nativeDb.getITwinId(), iModelId: args.nativeDb.getIModelId() });
|
|
93
|
+
this._initialized = false;
|
|
94
|
+
this.models = new IModelDb.Models(this);
|
|
95
|
+
this.elements = new IModelDb.Elements(this);
|
|
96
|
+
this.views = new IModelDb.Views(this);
|
|
97
|
+
this.tiles = new IModelDb.Tiles(this);
|
|
98
|
+
this._statementCache = new SqliteStatement_1.StatementCache();
|
|
99
|
+
this._sqliteStatementCache = new SqliteStatement_1.StatementCache();
|
|
100
|
+
this._snaps = new Map();
|
|
101
|
+
/** @internal */
|
|
102
|
+
this._locks = new NoLocks();
|
|
103
|
+
/** Event called after a changeset is applied to this IModelDb. */
|
|
104
|
+
this.onChangesetApplied = new core_bentley_1.BeEvent();
|
|
105
|
+
/** Event called when the iModel is about to be closed. */
|
|
106
|
+
this.onBeforeClose = new core_bentley_1.BeEvent();
|
|
107
|
+
this._nativeDb = args.nativeDb;
|
|
108
|
+
this.nativeDb.setIModelDb(this);
|
|
109
|
+
this.loadSettingDictionaries();
|
|
110
|
+
GeoCoordConfig_1.GeoCoordConfig.loadForImodel(this.workspace.settings); // load gcs data specified by iModel's settings dictionaries, must be done before calling initializeIModelDb
|
|
111
|
+
this.initializeIModelDb();
|
|
112
|
+
IModelDb._openDbs.set(this._fileKey, this);
|
|
113
|
+
if (undefined === IModelDb._shutdownListener) { // the first time we create an IModelDb, add a listener to close any orphan files at shutdown.
|
|
114
|
+
IModelDb._shutdownListener = IModelHost_1.IModelHost.onBeforeShutdown.addListener(() => {
|
|
115
|
+
IModelDb._openDbs.forEach((db) => {
|
|
116
|
+
try {
|
|
117
|
+
db.abandonChanges();
|
|
118
|
+
db.close();
|
|
119
|
+
}
|
|
120
|
+
catch { }
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** @alpha */
|
|
126
|
+
get codeService() { return this._codeService; }
|
|
127
|
+
/**
|
|
128
|
+
* Get the [[LockControl]] for this iModel.
|
|
129
|
+
* @beta
|
|
130
|
+
*/
|
|
131
|
+
get locks() { return this._locks; } // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
132
|
+
/**
|
|
133
|
+
* Get the [[Workspace]] for this iModel.
|
|
134
|
+
* @beta
|
|
135
|
+
*/
|
|
136
|
+
get workspace() {
|
|
137
|
+
if (undefined === this._workspace)
|
|
138
|
+
this._workspace = new Workspace_1.ITwinWorkspace(new IModelSettings());
|
|
139
|
+
return this._workspace;
|
|
140
|
+
}
|
|
141
|
+
/** Acquire the exclusive schema lock on this iModel.
|
|
142
|
+
* > Note: To acquire the schema lock, all other briefcases must first release *all* their locks. No other briefcases
|
|
143
|
+
* will be able to acquire *any* locks while the schema lock is held.
|
|
144
|
+
*/
|
|
145
|
+
async acquireSchemaLock() {
|
|
146
|
+
return this.locks.acquireLocks({ exclusive: core_common_1.IModel.repositoryModelId });
|
|
147
|
+
}
|
|
148
|
+
/** determine whether the schema lock is currently held for this iModel. */
|
|
149
|
+
get holdsSchemaLock() {
|
|
150
|
+
return this.locks.holdsExclusiveLock(core_common_1.IModel.repositoryModelId);
|
|
151
|
+
}
|
|
152
|
+
/** @internal */
|
|
153
|
+
notifyChangesetApplied() {
|
|
154
|
+
this.changeset = this.nativeDb.getCurrentChangeset();
|
|
155
|
+
this.onChangesetApplied.raiseEvent();
|
|
156
|
+
}
|
|
157
|
+
get fontMap() {
|
|
158
|
+
var _a;
|
|
159
|
+
return (_a = this._fontMap) !== null && _a !== void 0 ? _a : (this._fontMap = new core_common_1.FontMap(this.nativeDb.readFontMap()));
|
|
160
|
+
}
|
|
161
|
+
/** @internal */
|
|
162
|
+
clearFontMap() {
|
|
163
|
+
this._fontMap = undefined;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Add a new font name/type to the FontMap for this iModel and return its FontId.
|
|
167
|
+
* @param name The name of the font to add
|
|
168
|
+
* @param type The type of the font. Default is TrueType.
|
|
169
|
+
* @returns The FontId for the newly added font. If a font by that name/type already exists, this method does not fail, it returns the existing Id.
|
|
170
|
+
* @see [FontId and FontMap]($docs/learning/backend/Fonts.md#fontid-and-fontmap)
|
|
171
|
+
* @beta
|
|
172
|
+
*/
|
|
173
|
+
addNewFont(name, type) {
|
|
174
|
+
this.locks.checkExclusiveLock(core_common_1.IModel.repositoryModelId, "schema", "addNewFont");
|
|
175
|
+
this.clearFontMap();
|
|
176
|
+
return this.nativeDb.addNewFont({ name, type: type !== null && type !== void 0 ? type : core_common_1.FontType.TrueType });
|
|
177
|
+
}
|
|
178
|
+
/** Check if this iModel has been opened read-only or not. */
|
|
179
|
+
get isReadonly() { return this.openMode === core_bentley_1.OpenMode.Readonly; }
|
|
180
|
+
/** The Guid that identifies this iModel. */
|
|
181
|
+
get iModelId() {
|
|
182
|
+
(0, core_bentley_1.assert)(undefined !== super.iModelId);
|
|
183
|
+
return super.iModelId;
|
|
184
|
+
} // GuidString | undefined for the IModel superclass, but required for all IModelDb subclasses
|
|
185
|
+
/** @internal*/
|
|
186
|
+
get nativeDb() { return this._nativeDb; } // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
187
|
+
/** Get the full path fileName of this iModelDb
|
|
188
|
+
* @note this member is only valid while the iModel is opened.
|
|
189
|
+
*/
|
|
190
|
+
get pathName() { return this.nativeDb.getFilePath(); }
|
|
191
|
+
/** Close this IModel, if it is currently open. */
|
|
192
|
+
close() {
|
|
193
|
+
var _a, _b;
|
|
194
|
+
if (!this.isOpen)
|
|
195
|
+
return; // don't continue if already closed
|
|
196
|
+
this.beforeClose();
|
|
197
|
+
IModelDb._openDbs.delete(this._fileKey);
|
|
198
|
+
(_a = this._workspace) === null || _a === void 0 ? void 0 : _a.close();
|
|
199
|
+
this.locks.close();
|
|
200
|
+
this._locks = undefined;
|
|
201
|
+
(_b = this._codeService) === null || _b === void 0 ? void 0 : _b.close();
|
|
202
|
+
this._codeService = undefined;
|
|
203
|
+
this.nativeDb.closeIModel();
|
|
204
|
+
this._nativeDb = undefined; // the underlying nativeDb has been freed by closeIModel
|
|
205
|
+
}
|
|
206
|
+
/** @internal */
|
|
207
|
+
async refreshContainerSas(_userAccessToken) { }
|
|
208
|
+
/**
|
|
209
|
+
* Called by derived classes before closing the connection
|
|
210
|
+
* @internal
|
|
211
|
+
*/
|
|
212
|
+
beforeClose() {
|
|
213
|
+
this.onBeforeClose.raiseEvent();
|
|
214
|
+
this.clearCaches();
|
|
215
|
+
}
|
|
216
|
+
/** @internal */
|
|
217
|
+
initializeIModelDb() {
|
|
218
|
+
const props = this.nativeDb.getIModelProps();
|
|
219
|
+
super.initialize(props.rootSubject.name, props);
|
|
220
|
+
if (this._initialized)
|
|
221
|
+
return;
|
|
222
|
+
this._initialized = true;
|
|
223
|
+
const db = this.isBriefcaseDb() ? this : undefined;
|
|
224
|
+
if (!db || !IpcHost_1.IpcHost.isValid)
|
|
225
|
+
return;
|
|
226
|
+
db.onNameChanged.addListener(() => IpcHost_1.IpcHost.notifyTxns(db, "notifyIModelNameChanged", db.name));
|
|
227
|
+
db.onRootSubjectChanged.addListener(() => IpcHost_1.IpcHost.notifyTxns(db, "notifyRootSubjectChanged", db.rootSubject));
|
|
228
|
+
db.onProjectExtentsChanged.addListener(() => IpcHost_1.IpcHost.notifyTxns(db, "notifyProjectExtentsChanged", db.projectExtents.toJSON()));
|
|
229
|
+
db.onGlobalOriginChanged.addListener(() => IpcHost_1.IpcHost.notifyTxns(db, "notifyGlobalOriginChanged", db.globalOrigin.toJSON()));
|
|
230
|
+
db.onEcefLocationChanged.addListener(() => { var _a; return IpcHost_1.IpcHost.notifyTxns(db, "notifyEcefLocationChanged", (_a = db.ecefLocation) === null || _a === void 0 ? void 0 : _a.toJSON()); });
|
|
231
|
+
db.onGeographicCoordinateSystemChanged.addListener(() => { var _a; return IpcHost_1.IpcHost.notifyTxns(db, "notifyGeographicCoordinateSystemChanged", (_a = db.geographicCoordinateSystem) === null || _a === void 0 ? void 0 : _a.toJSON()); });
|
|
232
|
+
}
|
|
233
|
+
/** Returns true if this is a BriefcaseDb
|
|
234
|
+
* @see [[BriefcaseDb.open]]
|
|
235
|
+
*/
|
|
236
|
+
get isBriefcase() { return false; }
|
|
237
|
+
/** Type guard for instanceof [[BriefcaseDb]] */
|
|
238
|
+
isBriefcaseDb() { return this.isBriefcase; }
|
|
239
|
+
/** Returns true if this is a SnapshotDb
|
|
240
|
+
* @see [[SnapshotDb.open]]
|
|
241
|
+
*/
|
|
242
|
+
get isSnapshot() { return false; }
|
|
243
|
+
/** Type guard for instanceof [[SnapshotDb]] */
|
|
244
|
+
isSnapshotDb() { return this.isSnapshot; }
|
|
245
|
+
/** Returns true if this is a *standalone* iModel
|
|
246
|
+
* @see [[StandaloneDb.open]]
|
|
247
|
+
* @internal
|
|
248
|
+
*/
|
|
249
|
+
get isStandalone() { return false; }
|
|
250
|
+
/** Type guard for instanceof [[StandaloneDb]]
|
|
251
|
+
* @internal
|
|
252
|
+
*/
|
|
253
|
+
isStandaloneDb() { return this.isStandalone; }
|
|
254
|
+
/** Return `true` if the underlying nativeDb is open and valid.
|
|
255
|
+
* @internal
|
|
256
|
+
*/
|
|
257
|
+
get isOpen() { return undefined !== this.nativeDb; }
|
|
258
|
+
/** Get the briefcase Id of this iModel */
|
|
259
|
+
getBriefcaseId() { return this.isOpen ? this.nativeDb.getBriefcaseId() : core_common_1.BriefcaseIdValue.Illegal; }
|
|
260
|
+
/**
|
|
261
|
+
* Use a prepared ECSQL statement, potentially from the statement cache. If the requested statement doesn't exist
|
|
262
|
+
* in the statement cache, a new statement is prepared. After the callback completes, the statement is reset and saved
|
|
263
|
+
* in the statement cache so it can be reused in the future. Use this method for ECSQL statements that will be
|
|
264
|
+
* reused often and are expensive to prepare. The statement cache holds the most recently used statements, discarding
|
|
265
|
+
* the oldest statements as it fills. For statements you don't intend to reuse, instead use [[withStatement]].
|
|
266
|
+
* @param sql The SQLite SQL statement to execute
|
|
267
|
+
* @param callback the callback to invoke on the prepared statement
|
|
268
|
+
* @param logErrors Determines if error will be logged if statement fail to prepare
|
|
269
|
+
* @returns the value returned by `callback`.
|
|
270
|
+
* @see [[withStatement]]
|
|
271
|
+
* @public
|
|
272
|
+
*/
|
|
273
|
+
withPreparedStatement(ecsql, callback, logErrors = true) {
|
|
274
|
+
var _a;
|
|
275
|
+
const stmt = (_a = this._statementCache.findAndRemove(ecsql)) !== null && _a !== void 0 ? _a : this.prepareStatement(ecsql, logErrors);
|
|
276
|
+
const release = () => this._statementCache.addOrDispose(stmt);
|
|
277
|
+
try {
|
|
278
|
+
const val = callback(stmt);
|
|
279
|
+
if (val instanceof Promise) {
|
|
280
|
+
val.then(release, release);
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
release();
|
|
284
|
+
}
|
|
285
|
+
return val;
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
release();
|
|
289
|
+
throw err;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Prepared and execute a callback on an ECSQL statement. After the callback completes the statement is disposed.
|
|
294
|
+
* Use this method for ECSQL statements are either not expected to be reused, or are not expensive to prepare.
|
|
295
|
+
* For statements that will be reused often, instead use [[withPreparedStatement]].
|
|
296
|
+
* @param sql The SQLite SQL statement to execute
|
|
297
|
+
* @param callback the callback to invoke on the prepared statement
|
|
298
|
+
* @param logErrors Determines if error will be logged if statement fail to prepare
|
|
299
|
+
* @returns the value returned by `callback`.
|
|
300
|
+
* @see [[withPreparedStatement]]
|
|
301
|
+
* @public
|
|
302
|
+
*/
|
|
303
|
+
withStatement(ecsql, callback, logErrors = true) {
|
|
304
|
+
const stmt = this.prepareStatement(ecsql, logErrors);
|
|
305
|
+
const release = () => stmt.dispose();
|
|
306
|
+
try {
|
|
307
|
+
const val = callback(stmt);
|
|
308
|
+
if (val instanceof Promise) {
|
|
309
|
+
val.then(release, release);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
release();
|
|
313
|
+
}
|
|
314
|
+
return val;
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
release();
|
|
318
|
+
throw err;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/** Allow to execute query and read results along with meta data. The result are streamed.
|
|
322
|
+
* @param params The values to bind to the parameters (if the ECSQL has any).
|
|
323
|
+
* @param config Allow to specify certain flags which control how query is executed.
|
|
324
|
+
* @returns Returns an [ECSqlReader]($common) which helps iterate over the result set and also give access to metadata.
|
|
325
|
+
* @beta
|
|
326
|
+
* */
|
|
327
|
+
createQueryReader(ecsql, params, config) {
|
|
328
|
+
if (!this._nativeDb || !this._nativeDb.isOpen()) {
|
|
329
|
+
throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "db not open");
|
|
330
|
+
}
|
|
331
|
+
const executor = {
|
|
332
|
+
execute: async (request) => {
|
|
333
|
+
return ConcurrentQuery_1.ConcurrentQuery.executeQueryRequest(this.nativeDb, request);
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
return new core_common_1.ECSqlReader(executor, ecsql, params, config);
|
|
337
|
+
}
|
|
338
|
+
/** Execute a query and stream its results
|
|
339
|
+
* The result of the query is async iterator over the rows. The iterator will get next page automatically once rows in current page has been read.
|
|
340
|
+
* [ECSQL row]($docs/learning/ECSQLRowFormat).
|
|
341
|
+
*
|
|
342
|
+
* See also:
|
|
343
|
+
* - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL)
|
|
344
|
+
* - [Code Examples]($docs/learning/backend/ECSQLCodeExamples)
|
|
345
|
+
*
|
|
346
|
+
* @param ecsql The ECSQL statement to execute
|
|
347
|
+
* @param params The values to bind to the parameters (if the ECSQL has any).
|
|
348
|
+
* @param options Allow to specify certain flags which control how query is executed.
|
|
349
|
+
* @returns Returns the query result as an *AsyncIterableIterator<any>* which lazy load result as needed. The row format is determined by *rowFormat* parameter.
|
|
350
|
+
* See [ECSQL row format]($docs/learning/ECSQLRowFormat) for details about the format of the returned rows.
|
|
351
|
+
* @throws [IModelError]($common) If there was any error while submitting, preparing or stepping into query
|
|
352
|
+
* @deprecated in 3.7. Use [[createQueryReader]] instead; it accepts the same parameters.
|
|
353
|
+
*/
|
|
354
|
+
async *query(ecsql, params, options) {
|
|
355
|
+
const builder = new core_common_1.QueryOptionsBuilder(options);
|
|
356
|
+
const reader = this.createQueryReader(ecsql, params, builder.getOptions());
|
|
357
|
+
while (await reader.step())
|
|
358
|
+
yield reader.formatCurrentRow();
|
|
359
|
+
}
|
|
360
|
+
/** Compute number of rows that would be returned by the ECSQL.
|
|
361
|
+
*
|
|
362
|
+
* See also:
|
|
363
|
+
* - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL)
|
|
364
|
+
* - [Code Examples]($docs/learning/backend/ECSQLCodeExamples)
|
|
365
|
+
*
|
|
366
|
+
* @param ecsql The ECSQL statement to execute
|
|
367
|
+
* @param params The values to bind to the parameters (if the ECSQL has any).
|
|
368
|
+
* See "[iTwin.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" for details.
|
|
369
|
+
* @returns Return row count.
|
|
370
|
+
* @throws [IModelError]($common) If the statement is invalid
|
|
371
|
+
* @deprecated in 3.7. Count the number of results using `count(*)` where the original query is a subquery instead. E.g., `SELECT count(*) FROM (<query-whose-rows-to-count>)`.
|
|
372
|
+
*/
|
|
373
|
+
async queryRowCount(ecsql, params) {
|
|
374
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
375
|
+
for await (const row of this.query(`select count(*) from (${ecsql})`, params)) {
|
|
376
|
+
return row[0];
|
|
377
|
+
}
|
|
378
|
+
throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "Failed to get row count");
|
|
379
|
+
}
|
|
380
|
+
/** Cancel any previous query with same token and run execute the current specified query.
|
|
381
|
+
* The result of the query is async iterator over the rows. The iterator will get next page automatically once rows in current page has been read.
|
|
382
|
+
* [ECSQL row]($docs/learning/ECSQLRowFormat).
|
|
383
|
+
*
|
|
384
|
+
* See also:
|
|
385
|
+
* - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL)
|
|
386
|
+
* - [Code Examples]($docs/learning/backend/ECSQLCodeExamples)
|
|
387
|
+
*
|
|
388
|
+
* @param ecsql The ECSQL statement to execute
|
|
389
|
+
* @param token None empty restart token. The previous query with same token would be cancelled. This would cause
|
|
390
|
+
* exception which user code must handle.
|
|
391
|
+
* @param params The values to bind to the parameters (if the ECSQL has any).
|
|
392
|
+
* @param options Allow to specify certain flags which control how query is executed.
|
|
393
|
+
* @returns Returns the query result as an *AsyncIterableIterator<any>* which lazy load result as needed. The row format is determined by *rowFormat* parameter.
|
|
394
|
+
* See [ECSQL row format]($docs/learning/ECSQLRowFormat) for details about the format of the returned rows.
|
|
395
|
+
* @throws [IModelError]($common) If there was any error while submitting, preparing or stepping into query
|
|
396
|
+
* @deprecated in 3.7. Use [[createQueryReader]] instead. Pass in the restart token as part of the `config` argument; e.g., `{ restartToken: myToken }` or `new QueryOptionsBuilder().setRestartToken(myToken).getOptions()`.
|
|
397
|
+
*/
|
|
398
|
+
async *restartQuery(token, ecsql, params, options) {
|
|
399
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
400
|
+
for await (const row of this.query(ecsql, params, new core_common_1.QueryOptionsBuilder(options).setRestartToken(token).getOptions())) {
|
|
401
|
+
yield row;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Use a prepared SQL statement, potentially from the statement cache. If the requested statement doesn't exist
|
|
406
|
+
* in the statement cache, a new statement is prepared. After the callback completes, the statement is reset and saved
|
|
407
|
+
* in the statement cache so it can be reused in the future. Use this method for SQL statements that will be
|
|
408
|
+
* reused often and are expensive to prepare. The statement cache holds the most recently used statements, discarding
|
|
409
|
+
* the oldest statements as it fills. For statements you don't intend to reuse, instead use [[withSqliteStatement]].
|
|
410
|
+
* @param sql The SQLite SQL statement to execute
|
|
411
|
+
* @param callback the callback to invoke on the prepared statement
|
|
412
|
+
* @param logErrors Determine if errors are logged or not
|
|
413
|
+
* @returns the value returned by `callback`.
|
|
414
|
+
* @see [[withPreparedStatement]]
|
|
415
|
+
* @public
|
|
416
|
+
*/
|
|
417
|
+
withPreparedSqliteStatement(sql, callback, logErrors = true) {
|
|
418
|
+
var _a;
|
|
419
|
+
const stmt = (_a = this._sqliteStatementCache.findAndRemove(sql)) !== null && _a !== void 0 ? _a : this.prepareSqliteStatement(sql, logErrors);
|
|
420
|
+
const release = () => this._sqliteStatementCache.addOrDispose(stmt);
|
|
421
|
+
try {
|
|
422
|
+
const val = callback(stmt);
|
|
423
|
+
if (val instanceof Promise) {
|
|
424
|
+
val.then(release, release);
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
release();
|
|
428
|
+
}
|
|
429
|
+
return val;
|
|
430
|
+
}
|
|
431
|
+
catch (err) {
|
|
432
|
+
release();
|
|
433
|
+
throw err;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Prepared and execute a callback on a SQL statement. After the callback completes the statement is disposed.
|
|
438
|
+
* Use this method for SQL statements are either not expected to be reused, or are not expensive to prepare.
|
|
439
|
+
* For statements that will be reused often, instead use [[withPreparedSqliteStatement]].
|
|
440
|
+
* @param sql The SQLite SQL statement to execute
|
|
441
|
+
* @param callback the callback to invoke on the prepared statement
|
|
442
|
+
* @param logErrors Determine if errors are logged or not
|
|
443
|
+
* @returns the value returned by `callback`.
|
|
444
|
+
* @public
|
|
445
|
+
*/
|
|
446
|
+
withSqliteStatement(sql, callback, logErrors = true) {
|
|
447
|
+
const stmt = this.prepareSqliteStatement(sql, logErrors);
|
|
448
|
+
const release = () => stmt.dispose();
|
|
449
|
+
try {
|
|
450
|
+
const val = callback(stmt);
|
|
451
|
+
if (val instanceof Promise) {
|
|
452
|
+
val.then(release, release);
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
release();
|
|
456
|
+
}
|
|
457
|
+
return val;
|
|
458
|
+
}
|
|
459
|
+
catch (err) {
|
|
460
|
+
release();
|
|
461
|
+
throw err;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/** Prepare an SQL statement.
|
|
465
|
+
* @param sql The SQL statement to prepare
|
|
466
|
+
* @throws [[IModelError]] if there is a problem preparing the statement.
|
|
467
|
+
* @internal
|
|
468
|
+
*/
|
|
469
|
+
prepareSqliteStatement(sql, logErrors = true) {
|
|
470
|
+
const stmt = new SqliteStatement_1.SqliteStatement(sql);
|
|
471
|
+
stmt.prepare(this.nativeDb, logErrors);
|
|
472
|
+
return stmt;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* queries the BisCore.SubCategory table for the entries that are children of the passed categoryIds
|
|
476
|
+
* @param categoryIds categoryIds to query
|
|
477
|
+
* @returns array of SubCategoryResultRow
|
|
478
|
+
* @internal
|
|
479
|
+
*/
|
|
480
|
+
async querySubCategories(categoryIds) {
|
|
481
|
+
const result = [];
|
|
482
|
+
const where = [...categoryIds].join(",");
|
|
483
|
+
const query = `SELECT ECInstanceId as id, Parent.Id as parentId, Properties as appearance FROM BisCore.SubCategory WHERE Parent.Id IN (${where})`;
|
|
484
|
+
try {
|
|
485
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
486
|
+
for await (const row of this.query(query, undefined, { rowFormat: core_common_1.QueryRowFormat.UseJsPropertyNames })) {
|
|
487
|
+
result.push(row);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
// We can ignore the error here, and just return whatever we were able to query.
|
|
492
|
+
}
|
|
493
|
+
return result;
|
|
494
|
+
}
|
|
495
|
+
/** Query for a set of entity ids, given an EntityQueryParams
|
|
496
|
+
* @param params The query parameters. The `limit` and `offset` members should be used to page results.
|
|
497
|
+
* @returns an Id64Set with results of query
|
|
498
|
+
* @throws [[IModelError]] if the generated statement is invalid or [IModelDb.maxLimit]($backend) exceeded when collecting ids.
|
|
499
|
+
*
|
|
500
|
+
* *Example:*
|
|
501
|
+
* ``` ts
|
|
502
|
+
* [[include:ECSQL-backend-queries.select-element-by-code-value-using-queryEntityIds]]
|
|
503
|
+
* ```
|
|
504
|
+
*/
|
|
505
|
+
queryEntityIds(params) {
|
|
506
|
+
let sql = "SELECT ECInstanceId FROM ";
|
|
507
|
+
if (params.only)
|
|
508
|
+
sql += "ONLY ";
|
|
509
|
+
sql += params.from;
|
|
510
|
+
if (params.where)
|
|
511
|
+
sql += ` WHERE ${params.where}`;
|
|
512
|
+
if (params.orderBy)
|
|
513
|
+
sql += ` ORDER BY ${params.orderBy}`;
|
|
514
|
+
if (typeof params.limit === "number" && params.limit > 0)
|
|
515
|
+
sql += ` LIMIT ${params.limit}`;
|
|
516
|
+
if (typeof params.offset === "number" && params.offset > 0)
|
|
517
|
+
sql += ` OFFSET ${params.offset}`;
|
|
518
|
+
const ids = new Set();
|
|
519
|
+
this.withPreparedStatement(sql, (stmt) => {
|
|
520
|
+
if (params.bindings)
|
|
521
|
+
stmt.bindValues(params.bindings);
|
|
522
|
+
for (const row of stmt) {
|
|
523
|
+
if (row.id !== undefined) {
|
|
524
|
+
ids.add(row.id);
|
|
525
|
+
if (ids.size > IModelDb.maxLimit) {
|
|
526
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "Max LIMIT exceeded in SELECT statement");
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
return ids;
|
|
532
|
+
}
|
|
533
|
+
/** Clear all in-memory caches held in this IModelDb. */
|
|
534
|
+
clearCaches() {
|
|
535
|
+
this._statementCache.clear();
|
|
536
|
+
this._sqliteStatementCache.clear();
|
|
537
|
+
}
|
|
538
|
+
/** Update the project extents for this iModel.
|
|
539
|
+
* <p><em>Example:</em>
|
|
540
|
+
* ``` ts
|
|
541
|
+
* [[include:IModelDb.updateProjectExtents]]
|
|
542
|
+
* ```
|
|
543
|
+
*/
|
|
544
|
+
updateProjectExtents(newExtents) {
|
|
545
|
+
this.projectExtents = newExtents;
|
|
546
|
+
this.updateIModelProps();
|
|
547
|
+
}
|
|
548
|
+
/** Compute an appropriate project extents for this iModel based on the ranges of all spatial elements.
|
|
549
|
+
* Typically, the result is simply the union of the ranges of all spatial elements. However, the algorithm also detects "outlier elements",
|
|
550
|
+
* whose placements locate them so far from the rest of the spatial geometry that they are considered statistically insignificant. The
|
|
551
|
+
* range of an outlier element does not contribute to the computed extents.
|
|
552
|
+
* @param options Specifies the level of detail desired in the return value.
|
|
553
|
+
* @returns the computed extents.
|
|
554
|
+
* @note This method does not modify the IModel's stored project extents. @see [[updateProjectExtents]].
|
|
555
|
+
*/
|
|
556
|
+
computeProjectExtents(options) {
|
|
557
|
+
const wantFullExtents = true === (options === null || options === void 0 ? void 0 : options.reportExtentsWithOutliers);
|
|
558
|
+
const wantOutliers = true === (options === null || options === void 0 ? void 0 : options.reportOutliers);
|
|
559
|
+
const result = this.nativeDb.computeProjectExtents(wantFullExtents, wantOutliers);
|
|
560
|
+
return {
|
|
561
|
+
extents: core_geometry_1.Range3d.fromJSON(result.extents),
|
|
562
|
+
extentsWithOutliers: result.fullExtents ? core_geometry_1.Range3d.fromJSON(result.fullExtents) : undefined,
|
|
563
|
+
outliers: result.outliers,
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
/** Update the [EcefLocation]($docs/learning/glossary#eceflocation) of this iModel. */
|
|
567
|
+
updateEcefLocation(ecef) {
|
|
568
|
+
this.setEcefLocation(ecef);
|
|
569
|
+
this.updateIModelProps();
|
|
570
|
+
}
|
|
571
|
+
/** Update the IModelProps of this iModel in the database. */
|
|
572
|
+
updateIModelProps() {
|
|
573
|
+
this.nativeDb.updateIModelProps(this.toJSON());
|
|
574
|
+
}
|
|
575
|
+
/** Commit pending changes to this iModel.
|
|
576
|
+
* @param description Optional description of the changes
|
|
577
|
+
* @throws [[IModelError]] if there is a problem saving changes or if there are pending, un-processed lock or code requests.
|
|
578
|
+
*/
|
|
579
|
+
saveChanges(description) {
|
|
580
|
+
if (this.openMode === core_bentley_1.OpenMode.Readonly)
|
|
581
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.ReadOnly, "IModelDb was opened read-only");
|
|
582
|
+
const stat = this.nativeDb.saveChanges(description);
|
|
583
|
+
if (core_bentley_1.DbResult.BE_SQLITE_OK !== stat)
|
|
584
|
+
throw new core_common_1.IModelError(stat, `Could not save changes (${description})`);
|
|
585
|
+
}
|
|
586
|
+
/** Abandon pending changes in this iModel. */
|
|
587
|
+
abandonChanges() {
|
|
588
|
+
this.nativeDb.abandonChanges();
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Save all changes and perform a [checkpoint](https://www.sqlite.org/c3ref/wal_checkpoint_v2.html) on this IModelDb.
|
|
592
|
+
* This ensures that all changes to the database since it was opened are saved to its file and the WAL file is truncated.
|
|
593
|
+
* @note Checkpoint automatically happens when IModelDbs are closed. However, the checkpoint
|
|
594
|
+
* operation itself can take some time. It may be useful to call this method prior to closing so that the checkpoint "penalty" is paid earlier.
|
|
595
|
+
* @note Another use for this function is to permit the file to be copied while it is open for write. iModel files should
|
|
596
|
+
* rarely be copied, and even less so while they're opened. But this scenario is sometimes encountered for tests.
|
|
597
|
+
*/
|
|
598
|
+
performCheckpoint() {
|
|
599
|
+
if (!this.isReadonly) {
|
|
600
|
+
this.saveChanges();
|
|
601
|
+
this.nativeDb.performCheckpoint();
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
/** @internal */
|
|
605
|
+
reverseTxns(numOperations) {
|
|
606
|
+
return this.nativeDb.reverseTxns(numOperations);
|
|
607
|
+
}
|
|
608
|
+
/** @internal */
|
|
609
|
+
reinstateTxn() {
|
|
610
|
+
return this.nativeDb.reinstateTxn();
|
|
611
|
+
}
|
|
612
|
+
/** @internal */
|
|
613
|
+
restartTxnSession() {
|
|
614
|
+
return this.nativeDb.restartTxnSession();
|
|
615
|
+
}
|
|
616
|
+
/** Import an ECSchema. On success, the schema definition is stored in the iModel.
|
|
617
|
+
* This method is asynchronous (must be awaited) because, in the case where this IModelDb is a briefcase, this method first obtains the schema lock from the iModel server.
|
|
618
|
+
* You must import a schema into an iModel before you can insert instances of the classes in that schema. See [[Element]]
|
|
619
|
+
* @param schemaFileName array of Full paths to ECSchema.xml files to be imported.
|
|
620
|
+
* @param {SchemaImportOptions} options - options during schema import.
|
|
621
|
+
* @throws [[IModelError]] if the schema lock cannot be obtained or there is a problem importing the schema.
|
|
622
|
+
* @note Changes are saved if importSchemas is successful and abandoned if not successful.
|
|
623
|
+
* @see querySchemaVersion
|
|
624
|
+
*/
|
|
625
|
+
async importSchemas(schemaFileNames, options) {
|
|
626
|
+
var _a;
|
|
627
|
+
if (this.nativeDb.getITwinId() !== core_bentley_1.Guid.empty) // if this iModel is associated with an iTwin, importing schema requires the schema lock
|
|
628
|
+
await this.acquireSchemaLock();
|
|
629
|
+
const maybeCustomNativeContext = (_a = options === null || options === void 0 ? void 0 : options.ecSchemaXmlContext) === null || _a === void 0 ? void 0 : _a.nativeContext;
|
|
630
|
+
const nativeImportOptions = {
|
|
631
|
+
schemaLockHeld: true,
|
|
632
|
+
ecSchemaXmlContext: maybeCustomNativeContext,
|
|
633
|
+
};
|
|
634
|
+
const stat = this.nativeDb.importSchemas(schemaFileNames, nativeImportOptions);
|
|
635
|
+
if (core_bentley_1.DbResult.BE_SQLITE_OK !== stat) {
|
|
636
|
+
throw new core_common_1.IModelError(stat, "Error importing schema");
|
|
637
|
+
}
|
|
638
|
+
this.clearCaches();
|
|
639
|
+
}
|
|
640
|
+
/** Import ECSchema(s) serialized to XML. On success, the schema definition is stored in the iModel.
|
|
641
|
+
* This method is asynchronous (must be awaited) because, in the case where this IModelDb is a briefcase, this method first obtains the schema lock from the iModel server.
|
|
642
|
+
* You must import a schema into an iModel before you can insert instances of the classes in that schema. See [[Element]]
|
|
643
|
+
* @param serializedXmlSchemas The xml string(s) created from a serialized ECSchema.
|
|
644
|
+
* @throws [[IModelError]] if the schema lock cannot be obtained or there is a problem importing the schema.
|
|
645
|
+
* @note Changes are saved if importSchemaStrings is successful and abandoned if not successful.
|
|
646
|
+
* @see querySchemaVersion
|
|
647
|
+
* @alpha
|
|
648
|
+
*/
|
|
649
|
+
async importSchemaStrings(serializedXmlSchemas) {
|
|
650
|
+
if (this.iTwinId && this.iTwinId !== core_bentley_1.Guid.empty) // if this iModel is associated with an iTwin, importing schema requires the schema lock
|
|
651
|
+
await this.acquireSchemaLock();
|
|
652
|
+
const stat = this.nativeDb.importXmlSchemas(serializedXmlSchemas, { schemaLockHeld: true });
|
|
653
|
+
if (core_bentley_1.DbResult.BE_SQLITE_OK !== stat)
|
|
654
|
+
throw new core_common_1.IModelError(stat, "Error importing schema");
|
|
655
|
+
this.clearCaches();
|
|
656
|
+
}
|
|
657
|
+
/** Find an opened instance of any subclass of IModelDb, by filename
|
|
658
|
+
* @note this method returns an IModelDb if the filename is open for *any* subclass of IModelDb
|
|
659
|
+
*/
|
|
660
|
+
static findByFilename(fileName) {
|
|
661
|
+
for (const entry of this._openDbs) {
|
|
662
|
+
if (entry[1].pathName === fileName)
|
|
663
|
+
return entry[1];
|
|
664
|
+
}
|
|
665
|
+
return undefined;
|
|
666
|
+
}
|
|
667
|
+
/** Find an open IModelDb by its key.
|
|
668
|
+
* @note This method is mainly for use by RPC implementations.
|
|
669
|
+
* @throws [[IModelNotFoundResponse]] if an open IModelDb matching the key is not found.
|
|
670
|
+
* @see [IModel.key]($common)
|
|
671
|
+
*/
|
|
672
|
+
static findByKey(key) {
|
|
673
|
+
const iModelDb = this.tryFindByKey(key);
|
|
674
|
+
if (undefined === iModelDb)
|
|
675
|
+
throw new core_common_1.IModelNotFoundResponse(); // a very specific status for the RpcManager
|
|
676
|
+
return iModelDb;
|
|
677
|
+
}
|
|
678
|
+
/** Attempt to find an open IModelDb by key.
|
|
679
|
+
* @returns The matching IModelDb or `undefined`.
|
|
680
|
+
*/
|
|
681
|
+
static tryFindByKey(key) {
|
|
682
|
+
return this._openDbs.get(key);
|
|
683
|
+
}
|
|
684
|
+
/** @internal */
|
|
685
|
+
static openDgnDb(file, openMode, upgradeOptions, props) {
|
|
686
|
+
var _a;
|
|
687
|
+
file.key = (_a = file.key) !== null && _a !== void 0 ? _a : core_bentley_1.Guid.createValue();
|
|
688
|
+
if (this.tryFindByKey(file.key))
|
|
689
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.AlreadyOpen, `key [${file.key}] for file [${file.path}] is already in use`);
|
|
690
|
+
const isUpgradeRequested = (upgradeOptions === null || upgradeOptions === void 0 ? void 0 : upgradeOptions.domain) === core_common_1.DomainOptions.Upgrade || (upgradeOptions === null || upgradeOptions === void 0 ? void 0 : upgradeOptions.profile) === core_common_1.ProfileOptions.Upgrade;
|
|
691
|
+
if (isUpgradeRequested && openMode !== core_bentley_1.OpenMode.ReadWrite)
|
|
692
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.UpgradeFailed, "Cannot upgrade a Readonly Db");
|
|
693
|
+
try {
|
|
694
|
+
const nativeDb = new IModelHost_1.IModelHost.platform.DgnDb();
|
|
695
|
+
nativeDb.openIModel(file.path, openMode, upgradeOptions, props, props === null || props === void 0 ? void 0 : props.container);
|
|
696
|
+
return nativeDb;
|
|
697
|
+
}
|
|
698
|
+
catch (err) {
|
|
699
|
+
throw new core_common_1.IModelError(err.errorNumber, `${err.message}, ${file.path}`);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Determines if the schemas in the Db must or can be upgraded by comparing them with those included in the
|
|
704
|
+
* current version of the software.
|
|
705
|
+
* @param filePath Full name of the briefcase including path
|
|
706
|
+
* @param forReadWrite Pass true if validating for read-write scenarios - note that the schema version requirements
|
|
707
|
+
* for opening the DgnDb read-write is more stringent than when opening the database read-only
|
|
708
|
+
* @throws [[IModelError]] If the Db was in an invalid state and that causes a problem with validating schemas
|
|
709
|
+
* @see [[BriefcaseDb.upgradeSchemas]] or [[StandaloneDb.upgradeSchemas]]
|
|
710
|
+
* @see ($docs/learning/backend/IModelDb.md#upgrading-schemas-in-an-imodel)
|
|
711
|
+
*/
|
|
712
|
+
static validateSchemas(filePath, forReadWrite) {
|
|
713
|
+
const openMode = forReadWrite ? core_bentley_1.OpenMode.ReadWrite : core_bentley_1.OpenMode.Readonly;
|
|
714
|
+
const file = { path: filePath };
|
|
715
|
+
let result = core_bentley_1.DbResult.BE_SQLITE_OK;
|
|
716
|
+
try {
|
|
717
|
+
const upgradeOptions = {
|
|
718
|
+
domain: core_common_1.DomainOptions.CheckRecommendedUpgrades,
|
|
719
|
+
};
|
|
720
|
+
const nativeDb = this.openDgnDb(file, openMode, upgradeOptions);
|
|
721
|
+
nativeDb.closeIModel();
|
|
722
|
+
}
|
|
723
|
+
catch (err) {
|
|
724
|
+
result = err.errorNumber;
|
|
725
|
+
}
|
|
726
|
+
let schemaState = core_common_1.SchemaState.UpToDate;
|
|
727
|
+
switch (result) {
|
|
728
|
+
case core_bentley_1.DbResult.BE_SQLITE_OK:
|
|
729
|
+
schemaState = core_common_1.SchemaState.UpToDate;
|
|
730
|
+
break;
|
|
731
|
+
case core_bentley_1.DbResult.BE_SQLITE_ERROR_ProfileTooOld:
|
|
732
|
+
case core_bentley_1.DbResult.BE_SQLITE_ERROR_ProfileTooOldForReadWrite:
|
|
733
|
+
case core_bentley_1.DbResult.BE_SQLITE_ERROR_SchemaTooOld:
|
|
734
|
+
schemaState = core_common_1.SchemaState.TooOld;
|
|
735
|
+
break;
|
|
736
|
+
case core_bentley_1.DbResult.BE_SQLITE_ERROR_ProfileTooNew:
|
|
737
|
+
case core_bentley_1.DbResult.BE_SQLITE_ERROR_ProfileTooNewForReadWrite:
|
|
738
|
+
case core_bentley_1.DbResult.BE_SQLITE_ERROR_SchemaTooNew:
|
|
739
|
+
schemaState = core_common_1.SchemaState.TooNew;
|
|
740
|
+
break;
|
|
741
|
+
case core_bentley_1.DbResult.BE_SQLITE_ERROR_SchemaUpgradeRecommended:
|
|
742
|
+
schemaState = core_common_1.SchemaState.UpgradeRecommended;
|
|
743
|
+
break;
|
|
744
|
+
case core_bentley_1.DbResult.BE_SQLITE_ERROR_SchemaUpgradeRequired:
|
|
745
|
+
schemaState = core_common_1.SchemaState.UpgradeRequired;
|
|
746
|
+
break;
|
|
747
|
+
case core_bentley_1.DbResult.BE_SQLITE_ERROR_InvalidProfileVersion:
|
|
748
|
+
throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR_InvalidProfileVersion, "The profile of the Db is invalid. Cannot upgrade or open the Db.");
|
|
749
|
+
default:
|
|
750
|
+
throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "Error validating schemas. Cannot upgrade or open the Db.");
|
|
751
|
+
}
|
|
752
|
+
return schemaState;
|
|
753
|
+
}
|
|
754
|
+
/** Get the ClassMetaDataRegistry for this iModel.
|
|
755
|
+
* @internal
|
|
756
|
+
*/
|
|
757
|
+
get classMetaDataRegistry() {
|
|
758
|
+
if (this._classMetaDataRegistry === undefined)
|
|
759
|
+
this._classMetaDataRegistry = new ClassRegistry_1.MetaDataRegistry();
|
|
760
|
+
return this._classMetaDataRegistry;
|
|
761
|
+
}
|
|
762
|
+
/** Get the linkTableRelationships for this IModel */
|
|
763
|
+
get relationships() {
|
|
764
|
+
return this._relationships || (this._relationships = new Relationship_1.Relationships(this));
|
|
765
|
+
}
|
|
766
|
+
/** Get the CodeSpecs in this IModel. */
|
|
767
|
+
get codeSpecs() {
|
|
768
|
+
return (this._codeSpecs !== undefined) ? this._codeSpecs : (this._codeSpecs = new CodeSpecs_1.CodeSpecs(this));
|
|
769
|
+
}
|
|
770
|
+
/** @internal */
|
|
771
|
+
insertCodeSpec(codeSpec) {
|
|
772
|
+
return this.nativeDb.insertCodeSpec(codeSpec.name, codeSpec.properties); // TODO: Remove "as any" when NativeLibrary.ts is updated so "spec" isn't marked as required
|
|
773
|
+
}
|
|
774
|
+
/** Prepare an ECSQL statement.
|
|
775
|
+
* @param sql The ECSQL statement to prepare
|
|
776
|
+
* @param logErrors Determines if error will be logged if statement fail to prepare
|
|
777
|
+
* @throws [[IModelError]] if there is a problem preparing the statement.
|
|
778
|
+
*/
|
|
779
|
+
prepareStatement(sql, logErrors = true) {
|
|
780
|
+
const stmt = new ECSqlStatement_1.ECSqlStatement();
|
|
781
|
+
stmt.prepare(this.nativeDb, sql, logErrors);
|
|
782
|
+
return stmt;
|
|
783
|
+
}
|
|
784
|
+
/** Prepare an ECSQL statement.
|
|
785
|
+
* @param sql The ECSQL statement to prepare
|
|
786
|
+
* @returns `undefined` if there is a problem preparing the statement.
|
|
787
|
+
*/
|
|
788
|
+
tryPrepareStatement(sql) {
|
|
789
|
+
const statement = new ECSqlStatement_1.ECSqlStatement();
|
|
790
|
+
const result = statement.tryPrepare(this.nativeDb, sql);
|
|
791
|
+
return core_bentley_1.DbResult.BE_SQLITE_OK === result.status ? statement : undefined;
|
|
792
|
+
}
|
|
793
|
+
/** Construct an entity (Element or Model) from an iModel.
|
|
794
|
+
* @throws [[IModelError]] if the entity cannot be constructed.
|
|
795
|
+
*/
|
|
796
|
+
constructEntity(props) {
|
|
797
|
+
const jsClass = this.getJsClass(props.classFullName);
|
|
798
|
+
return new jsClass(props, this);
|
|
799
|
+
}
|
|
800
|
+
/** Get the JavaScript class that handles a given entity class. */
|
|
801
|
+
getJsClass(classFullName) {
|
|
802
|
+
try {
|
|
803
|
+
return ClassRegistry_1.ClassRegistry.getClass(classFullName, this);
|
|
804
|
+
}
|
|
805
|
+
catch (err) {
|
|
806
|
+
if (!ClassRegistry_1.ClassRegistry.isNotFoundError(err)) {
|
|
807
|
+
throw err;
|
|
808
|
+
}
|
|
809
|
+
this.loadMetaData(classFullName);
|
|
810
|
+
return ClassRegistry_1.ClassRegistry.getClass(classFullName, this);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
/** Get metadata for a class. This method will load the metadata from the iModel into the cache as a side-effect, if necessary.
|
|
814
|
+
* @throws [[IModelError]] if the metadata cannot be found nor loaded.
|
|
815
|
+
*/
|
|
816
|
+
getMetaData(classFullName) {
|
|
817
|
+
let metadata = this.classMetaDataRegistry.find(classFullName);
|
|
818
|
+
if (metadata === undefined) {
|
|
819
|
+
this.loadMetaData(classFullName);
|
|
820
|
+
metadata = this.classMetaDataRegistry.find(classFullName);
|
|
821
|
+
if (metadata === undefined)
|
|
822
|
+
throw ClassRegistry_1.ClassRegistry.makeMetaDataNotFoundError(classFullName); // do not log
|
|
823
|
+
}
|
|
824
|
+
return metadata;
|
|
825
|
+
}
|
|
826
|
+
/** Invoke a callback on each property of the specified class, optionally including superclass properties.
|
|
827
|
+
* @param iModel The IModel that contains the schema
|
|
828
|
+
* @param classFullName The full class name to load the metadata, if necessary
|
|
829
|
+
* @param wantSuper If true, superclass properties will also be processed
|
|
830
|
+
* @param func The callback to be invoked on each property
|
|
831
|
+
* @param includeCustom If true (default), include custom-handled properties in the iteration. Otherwise, skip custom-handled properties.
|
|
832
|
+
* @note Custom-handled properties are core properties that have behavior enforced by C++ handlers.
|
|
833
|
+
*/
|
|
834
|
+
static forEachMetaData(iModel, classFullName, wantSuper, func, includeCustom = true) {
|
|
835
|
+
const meta = iModel.getMetaData(classFullName); // will load if necessary
|
|
836
|
+
for (const propName in meta.properties) { // eslint-disable-line guard-for-in
|
|
837
|
+
const propMeta = meta.properties[propName];
|
|
838
|
+
if (includeCustom || !propMeta.isCustomHandled || propMeta.isCustomHandledOrphan)
|
|
839
|
+
func(propName, propMeta);
|
|
840
|
+
}
|
|
841
|
+
if (wantSuper && meta.baseClasses && meta.baseClasses.length > 0)
|
|
842
|
+
meta.baseClasses.forEach((baseClass) => this.forEachMetaData(iModel, baseClass, true, func, includeCustom));
|
|
843
|
+
}
|
|
844
|
+
/** @internal */
|
|
845
|
+
loadMetaData(classFullName) {
|
|
846
|
+
if (this.classMetaDataRegistry.find(classFullName))
|
|
847
|
+
return;
|
|
848
|
+
const className = classFullName.split(":");
|
|
849
|
+
if (className.length !== 2)
|
|
850
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadArg, `Invalid classFullName: ${classFullName}`);
|
|
851
|
+
const val = this.nativeDb.getECClassMetaData(className[0], className[1]);
|
|
852
|
+
if (val.error)
|
|
853
|
+
throw new core_common_1.IModelError(val.error.status, `Error getting class meta data for: ${classFullName}`);
|
|
854
|
+
(0, core_bentley_1.assert)(undefined !== val.result);
|
|
855
|
+
const metaData = new core_common_1.EntityMetaData(JSON.parse(val.result));
|
|
856
|
+
this.classMetaDataRegistry.add(classFullName, metaData);
|
|
857
|
+
// Recursive, to make sure that base classes are cached.
|
|
858
|
+
if (metaData.baseClasses !== undefined && metaData.baseClasses.length > 0)
|
|
859
|
+
metaData.baseClasses.forEach((baseClassName) => this.loadMetaData(baseClassName));
|
|
860
|
+
}
|
|
861
|
+
/** Returns the full schema for the input name.
|
|
862
|
+
* @param name The name of the schema e.g. 'BisCore'
|
|
863
|
+
* @returns The SchemaProps for the requested schema
|
|
864
|
+
* @throws if the schema can not be found or loaded.
|
|
865
|
+
*/
|
|
866
|
+
getSchemaProps(name) {
|
|
867
|
+
return this.nativeDb.getSchemaProps(name);
|
|
868
|
+
}
|
|
869
|
+
/** Query if this iModel contains the definition of the specified class.
|
|
870
|
+
* @param classFullName The full name of the class, for example, SomeSchema:SomeClass
|
|
871
|
+
* @returns true if the iModel contains the class definition or false if not.
|
|
872
|
+
* @see querySchemaVersion
|
|
873
|
+
* @see importSchema
|
|
874
|
+
*/
|
|
875
|
+
containsClass(classFullName) {
|
|
876
|
+
const classNameParts = classFullName.replace(".", ":").split(":");
|
|
877
|
+
return classNameParts.length === 2 && this.nativeDb.getECClassMetaData(classNameParts[0], classNameParts[1]).error === undefined;
|
|
878
|
+
}
|
|
879
|
+
/** Query for a schema of the specified name in this iModel.
|
|
880
|
+
* @returns The schema version as a semver-compatible string or `undefined` if the schema has not been imported.
|
|
881
|
+
*/
|
|
882
|
+
querySchemaVersion(schemaName) {
|
|
883
|
+
const sql = `SELECT VersionMajor,VersionWrite,VersionMinor FROM ECDbMeta.ECSchemaDef WHERE Name=:schemaName LIMIT 1`;
|
|
884
|
+
return this.withPreparedStatement(sql, (statement) => {
|
|
885
|
+
statement.bindString("schemaName", schemaName);
|
|
886
|
+
if (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
887
|
+
const versionMajor = statement.getValue(0).getInteger(); // ECSchemaDef.VersionMajor --> semver.major
|
|
888
|
+
const versionWrite = statement.getValue(1).getInteger(); // ECSchemaDef.VersionWrite --> semver.minor
|
|
889
|
+
const versionMinor = statement.getValue(2).getInteger(); // ECSchemaDef.VersionMinor --> semver.patch
|
|
890
|
+
return `${versionMajor}.${versionWrite}.${versionMinor}`;
|
|
891
|
+
}
|
|
892
|
+
return undefined;
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
/** Retrieve a named texture image from this iModel, as a TextureData.
|
|
896
|
+
* @param props the texture load properties which must include the name of the texture to load
|
|
897
|
+
* @returns the TextureData or undefined if the texture image is not present.
|
|
898
|
+
* @alpha
|
|
899
|
+
*/
|
|
900
|
+
async queryTextureData(props) {
|
|
901
|
+
return this.nativeDb.queryTextureData(props);
|
|
902
|
+
}
|
|
903
|
+
/** Query a "file property" from this iModel, as a string.
|
|
904
|
+
* @returns the property string or undefined if the property is not present.
|
|
905
|
+
*/
|
|
906
|
+
queryFilePropertyString(prop) {
|
|
907
|
+
return this.nativeDb.queryFileProperty(prop, true);
|
|
908
|
+
}
|
|
909
|
+
/** Query a "file property" from this iModel, as a blob.
|
|
910
|
+
* @returns the property blob or undefined if the property is not present.
|
|
911
|
+
*/
|
|
912
|
+
queryFilePropertyBlob(prop) {
|
|
913
|
+
return this.nativeDb.queryFileProperty(prop, false);
|
|
914
|
+
}
|
|
915
|
+
/** Save a "file property" to this iModel
|
|
916
|
+
* @param prop the FilePropertyProps that describes the new property
|
|
917
|
+
* @param value either a string or a blob to save as the file property
|
|
918
|
+
*/
|
|
919
|
+
saveFileProperty(prop, strValue, blobVal) {
|
|
920
|
+
this.nativeDb.saveFileProperty(prop, strValue, blobVal);
|
|
921
|
+
}
|
|
922
|
+
/** delete a "file property" from this iModel
|
|
923
|
+
* @param prop the FilePropertyProps that describes the property
|
|
924
|
+
*/
|
|
925
|
+
deleteFileProperty(prop) {
|
|
926
|
+
this.nativeDb.saveFileProperty(prop, undefined, undefined);
|
|
927
|
+
}
|
|
928
|
+
/** Query for the next available major id for a "file property" from this iModel.
|
|
929
|
+
* @param prop the FilePropertyProps that describes the property
|
|
930
|
+
* @returns the next available (that is, an unused) id for prop. If none are present, will return 0.
|
|
931
|
+
*/
|
|
932
|
+
queryNextAvailableFileProperty(prop) { return this.nativeDb.queryNextAvailableFileProperty(prop); }
|
|
933
|
+
/** @internal */
|
|
934
|
+
async requestSnap(sessionId, props) {
|
|
935
|
+
let request = this._snaps.get(sessionId);
|
|
936
|
+
if (undefined === request) {
|
|
937
|
+
request = new IModelHost_1.IModelHost.platform.SnapRequest();
|
|
938
|
+
this._snaps.set(sessionId, request);
|
|
939
|
+
}
|
|
940
|
+
else
|
|
941
|
+
request.cancelSnap();
|
|
942
|
+
try {
|
|
943
|
+
return await request.doSnap(this.nativeDb, core_bentley_1.JsonUtils.toObject(props));
|
|
944
|
+
}
|
|
945
|
+
finally {
|
|
946
|
+
this._snaps.delete(sessionId);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
/** Cancel a previously requested snap.
|
|
950
|
+
* @internal
|
|
951
|
+
*/
|
|
952
|
+
cancelSnap(sessionId) {
|
|
953
|
+
const request = this._snaps.get(sessionId);
|
|
954
|
+
if (undefined !== request) {
|
|
955
|
+
request.cancelSnap();
|
|
956
|
+
this._snaps.delete(sessionId);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
/** Get the clip containment status for the supplied elements. */
|
|
960
|
+
async getGeometryContainment(props) {
|
|
961
|
+
return this.nativeDb.getGeometryContainment(core_bentley_1.JsonUtils.toObject(props));
|
|
962
|
+
}
|
|
963
|
+
/** Get the mass properties for the supplied elements. */
|
|
964
|
+
async getMassProperties(props) {
|
|
965
|
+
return this.nativeDb.getMassProperties(core_bentley_1.JsonUtils.toObject(props));
|
|
966
|
+
}
|
|
967
|
+
/** Get the IModel coordinate corresponding to each GeoCoordinate point in the input */
|
|
968
|
+
async getIModelCoordinatesFromGeoCoordinates(props) {
|
|
969
|
+
return this.nativeDb.getIModelCoordinatesFromGeoCoordinates(props);
|
|
970
|
+
}
|
|
971
|
+
/** Get the GeoCoordinate (longitude, latitude, elevation) corresponding to each IModel Coordinate point in the input */
|
|
972
|
+
async getGeoCoordinatesFromIModelCoordinates(props) {
|
|
973
|
+
return this.nativeDb.getGeoCoordinatesFromIModelCoordinates(props);
|
|
974
|
+
}
|
|
975
|
+
/** Export meshes suitable for graphics APIs from arbitrary geometry in elements in this IModelDb.
|
|
976
|
+
* * Requests can be slow when processing many elements so it is expected that this function be used on a dedicated backend,
|
|
977
|
+
* or that shared backends export a limited number of elements at a time.
|
|
978
|
+
* * Vertices are exported in the IModelDb's world coordinate system, which is right-handed with Z pointing up.
|
|
979
|
+
* * The results of changing [ExportGraphicsOptions]($core-backend) during the [ExportGraphicsOptions.onGraphics]($core-backend) callback are not defined.
|
|
980
|
+
*
|
|
981
|
+
* Example that prints the mesh for element 1 to stdout in [OBJ format](https://en.wikipedia.org/wiki/Wavefront_.obj_file)
|
|
982
|
+
* ```ts
|
|
983
|
+
* const onGraphics: ExportGraphicsFunction = (info: ExportGraphicsInfo) => {
|
|
984
|
+
* const mesh: ExportGraphicsMesh = info.mesh;
|
|
985
|
+
* for (let i = 0; i < mesh.points.length; i += 3) {
|
|
986
|
+
* process.stdout.write(`v ${mesh.points[i]} ${mesh.points[i + 1]} ${mesh.points[i + 2]}\n`);
|
|
987
|
+
* process.stdout.write(`vn ${mesh.normals[i]} ${mesh.normals[i + 1]} ${mesh.normals[i + 2]}\n`);
|
|
988
|
+
* }
|
|
989
|
+
*
|
|
990
|
+
* for (let i = 0; i < mesh.params.length; i += 2) {
|
|
991
|
+
* process.stdout.write(`vt ${mesh.params[i]} ${mesh.params[i + 1]}\n`);
|
|
992
|
+
* }
|
|
993
|
+
*
|
|
994
|
+
* for (let i = 0; i < mesh.indices.length; i += 3) {
|
|
995
|
+
* const p1 = mesh.indices[i];
|
|
996
|
+
* const p2 = mesh.indices[i + 1];
|
|
997
|
+
* const p3 = mesh.indices[i + 2];
|
|
998
|
+
* process.stdout.write(`f ${p1}/${p1}/${p1} ${p2}/${p2}/${p2} ${p3}/${p3}/${p3}\n`);
|
|
999
|
+
* }
|
|
1000
|
+
* };
|
|
1001
|
+
*
|
|
1002
|
+
* iModel.exportGraphics(({ onGraphics, elementIdArray: ["0x1"] }));
|
|
1003
|
+
* ```
|
|
1004
|
+
* @returns 0 if successful, status otherwise
|
|
1005
|
+
* @public
|
|
1006
|
+
*/
|
|
1007
|
+
exportGraphics(exportProps) {
|
|
1008
|
+
return this.nativeDb.exportGraphics(exportProps);
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Exports meshes suitable for graphics APIs from a specified [GeometryPart]($core-backend)
|
|
1012
|
+
* in this IModelDb.
|
|
1013
|
+
* The expected use case is to call [IModelDb.exportGraphics]($core-backend) and supply the
|
|
1014
|
+
* optional partInstanceArray argument, then call this function for each unique GeometryPart from
|
|
1015
|
+
* that list.
|
|
1016
|
+
* * The results of changing [ExportPartGraphicsOptions]($core-backend) during the
|
|
1017
|
+
* [ExportPartGraphicsOptions.onPartGraphics]($core-backend) callback are not defined.
|
|
1018
|
+
* * See export-gltf under test-apps in the iTwin.js monorepo for a working reference.
|
|
1019
|
+
* @returns 0 is successful, status otherwise
|
|
1020
|
+
* @public
|
|
1021
|
+
*/
|
|
1022
|
+
exportPartGraphics(exportProps) {
|
|
1023
|
+
return this.nativeDb.exportPartGraphics(exportProps);
|
|
1024
|
+
}
|
|
1025
|
+
/** Request geometry stream information from an element in binary format instead of json.
|
|
1026
|
+
* @returns IModelStatus.Success if successful
|
|
1027
|
+
* @alpha
|
|
1028
|
+
*/
|
|
1029
|
+
elementGeometryRequest(requestProps) {
|
|
1030
|
+
return this.nativeDb.processGeometryStream(requestProps);
|
|
1031
|
+
}
|
|
1032
|
+
/** Create brep geometry for inclusion in an element's geometry stream.
|
|
1033
|
+
* @returns IModelStatus.Success if successful
|
|
1034
|
+
* @throws [[IModelError]] to report issues with input geometry or parameters
|
|
1035
|
+
* @alpha
|
|
1036
|
+
*/
|
|
1037
|
+
createBRepGeometry(createProps) {
|
|
1038
|
+
return this.nativeDb.createBRepGeometry(createProps);
|
|
1039
|
+
}
|
|
1040
|
+
/** Generate graphics for an element or geometry stream.
|
|
1041
|
+
* @see [readElementGraphics]($frontend) to convert the result to a [RenderGraphic]($frontend) for display.
|
|
1042
|
+
*/
|
|
1043
|
+
async generateElementGraphics(request) {
|
|
1044
|
+
return (0, ElementGraphics_1.generateElementGraphics)(request, this);
|
|
1045
|
+
}
|
|
1046
|
+
/** Save a `SettingDictionary` in this iModel that will be loaded into [[workspace.settings]] every time this iModel is opened in future sessions.
|
|
1047
|
+
* @param name The name for the SettingDictionary. If a dictionary by that name already exists in the iModel, its value is replaced.
|
|
1048
|
+
* @param dict The SettingDictionary object to stringify and save.
|
|
1049
|
+
* @note All saved `SettingDictionary`s are loaded into [[workspace.settings]] every time an iModel is opened.
|
|
1050
|
+
* @beta
|
|
1051
|
+
*/
|
|
1052
|
+
saveSettingDictionary(name, dict) {
|
|
1053
|
+
this.withSqliteStatement("REPLACE INTO be_Prop(id,SubId,TxnMode,Namespace,Name,strData) VALUES(0,0,0,?,?,?)", (stmt) => {
|
|
1054
|
+
stmt.bindString(1, IModelDb._settingPropNamespace);
|
|
1055
|
+
stmt.bindString(2, name);
|
|
1056
|
+
stmt.bindString(3, JSON.stringify(dict));
|
|
1057
|
+
const rc = stmt.step();
|
|
1058
|
+
if (rc !== core_bentley_1.DbResult.BE_SQLITE_DONE)
|
|
1059
|
+
throw new core_common_1.IModelError(rc, "cannot save setting");
|
|
1060
|
+
});
|
|
1061
|
+
this.saveChanges("add settings");
|
|
1062
|
+
}
|
|
1063
|
+
/** Delete a SettingDictionary, previously added with [[saveSettingDictionary]], from this iModel.
|
|
1064
|
+
* @param name The name of the dictionary to delete.
|
|
1065
|
+
* @beta
|
|
1066
|
+
*/
|
|
1067
|
+
deleteSettingDictionary(name) {
|
|
1068
|
+
this.withSqliteStatement("DELETE FROM be_Prop WHERE Namespace=? AND Name=?", (stmt) => {
|
|
1069
|
+
stmt.bindString(1, IModelDb._settingPropNamespace);
|
|
1070
|
+
stmt.bindString(2, name);
|
|
1071
|
+
const rc = stmt.step();
|
|
1072
|
+
if (rc !== core_bentley_1.DbResult.BE_SQLITE_DONE)
|
|
1073
|
+
throw new core_common_1.IModelError(rc, "cannot delete setting");
|
|
1074
|
+
});
|
|
1075
|
+
this.saveChanges("delete settings");
|
|
1076
|
+
}
|
|
1077
|
+
/** Load all setting dictionaries in this iModel into `this.workspace.settings` */
|
|
1078
|
+
loadSettingDictionaries() {
|
|
1079
|
+
if (!this.nativeDb.isOpen())
|
|
1080
|
+
return;
|
|
1081
|
+
this.withSqliteStatement("SELECT Name,StrData FROM be_Prop WHERE Namespace=?", (stmt) => {
|
|
1082
|
+
stmt.bindString(1, IModelDb._settingPropNamespace);
|
|
1083
|
+
while (stmt.step() === core_bentley_1.DbResult.BE_SQLITE_ROW) {
|
|
1084
|
+
try {
|
|
1085
|
+
const dict = JSON.parse(stmt.getValueString(1));
|
|
1086
|
+
this.workspace.settings.addDictionary(stmt.getValueString(0), Settings_1.SettingsPriority.iModel, dict);
|
|
1087
|
+
}
|
|
1088
|
+
catch (e) {
|
|
1089
|
+
core_bentley_1.UnexpectedErrors.handle(e);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
exports.IModelDb = IModelDb;
|
|
1096
|
+
/** Keep track of open imodels to support `tryFind` for RPC purposes */
|
|
1097
|
+
IModelDb._openDbs = new Map();
|
|
1098
|
+
IModelDb.defaultLimit = 1000; // default limit for batching queries
|
|
1099
|
+
IModelDb.maxLimit = 10000; // maximum limit for batching queries
|
|
1100
|
+
IModelDb._settingPropNamespace = "settings";
|
|
1101
|
+
/** @public */
|
|
1102
|
+
(function (IModelDb) {
|
|
1103
|
+
/** The collection of models in an [[IModelDb]].
|
|
1104
|
+
* @public
|
|
1105
|
+
*/
|
|
1106
|
+
class Models {
|
|
1107
|
+
/** @internal */
|
|
1108
|
+
constructor(_iModel) {
|
|
1109
|
+
this._iModel = _iModel;
|
|
1110
|
+
}
|
|
1111
|
+
/** Get the ModelProps with the specified identifier.
|
|
1112
|
+
* @param modelId The Model identifier.
|
|
1113
|
+
* @throws [[IModelError]] if the model is not found or cannot be loaded.
|
|
1114
|
+
* @see tryGetModelProps
|
|
1115
|
+
*/
|
|
1116
|
+
getModelProps(id) {
|
|
1117
|
+
return this.getModelJson({ id });
|
|
1118
|
+
}
|
|
1119
|
+
/** Get the ModelProps with the specified identifier.
|
|
1120
|
+
* @param modelId The Model identifier.
|
|
1121
|
+
* @returns The ModelProps or `undefined` if the model is not found.
|
|
1122
|
+
* @throws [[IModelError]] if the model cannot be loaded.
|
|
1123
|
+
* @note Useful for cases when a model may or may not exist and throwing an `Error` would be overkill.
|
|
1124
|
+
* @see getModelProps
|
|
1125
|
+
*/
|
|
1126
|
+
tryGetModelProps(id) {
|
|
1127
|
+
return this.tryGetModelJson({ id });
|
|
1128
|
+
}
|
|
1129
|
+
/** Query for the last modified time of the specified Model.
|
|
1130
|
+
* @internal
|
|
1131
|
+
*/
|
|
1132
|
+
queryLastModifiedTime(modelId) {
|
|
1133
|
+
const sql = `SELECT LastMod FROM ${Model_1.Model.classFullName} WHERE ECInstanceId=:modelId`;
|
|
1134
|
+
return this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1135
|
+
statement.bindId("modelId", modelId);
|
|
1136
|
+
if (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1137
|
+
return statement.getValue(0).getDateTime();
|
|
1138
|
+
}
|
|
1139
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, `Can't get lastMod time for Model ${modelId}`);
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
/** Get the Model with the specified identifier.
|
|
1143
|
+
* @param modelId The Model identifier.
|
|
1144
|
+
* @param modelClass Optional class to validate instance against. This parameter can accept abstract or concrete classes, but should be the same as the template (`T`) parameter.
|
|
1145
|
+
* @throws [[IModelError]] if the model is not found, cannot be loaded, or fails validation when `modelClass` is specified.
|
|
1146
|
+
* @see tryGetModel
|
|
1147
|
+
*/
|
|
1148
|
+
getModel(modelId, modelClass) {
|
|
1149
|
+
const model = this.tryGetModel(modelId, modelClass);
|
|
1150
|
+
if (undefined === model) {
|
|
1151
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `Model=${modelId}`);
|
|
1152
|
+
}
|
|
1153
|
+
return model;
|
|
1154
|
+
}
|
|
1155
|
+
/** Get the Model with the specified identifier.
|
|
1156
|
+
* @param modelId The Model identifier.
|
|
1157
|
+
* @param modelClass Optional class to validate instance against. This parameter can accept abstract or concrete classes, but should be the same as the template (`T`) parameter.
|
|
1158
|
+
* @returns The Model or `undefined` if the model is not found or fails validation when `modelClass` is specified.
|
|
1159
|
+
* @throws [[IModelError]] if the model cannot be loaded.
|
|
1160
|
+
* @note Useful for cases when a model may or may not exist and throwing an `Error` would be overkill.
|
|
1161
|
+
* @see getModel
|
|
1162
|
+
*/
|
|
1163
|
+
tryGetModel(modelId, modelClass) {
|
|
1164
|
+
const modelProps = this.tryGetModelProps(modelId);
|
|
1165
|
+
if (undefined === modelProps)
|
|
1166
|
+
return undefined; // no Model with that modelId found
|
|
1167
|
+
const model = this._iModel.constructEntity(modelProps);
|
|
1168
|
+
if (undefined === modelClass)
|
|
1169
|
+
return model; // modelClass was not specified, cannot call instanceof to validate
|
|
1170
|
+
return model instanceof modelClass ? model : undefined;
|
|
1171
|
+
}
|
|
1172
|
+
/** Read the properties for a Model as a json string.
|
|
1173
|
+
* @param modelIdArg a json string with the identity of the model to load. Must have either "id" or "code".
|
|
1174
|
+
* @returns a json string with the properties of the model.
|
|
1175
|
+
* @throws [[IModelError]] if the model is not found or cannot be loaded.
|
|
1176
|
+
* @see tryGetModelJson
|
|
1177
|
+
* @internal
|
|
1178
|
+
*/
|
|
1179
|
+
getModelJson(modelIdArg) {
|
|
1180
|
+
const modelJson = this.tryGetModelJson(modelIdArg);
|
|
1181
|
+
if (undefined === modelJson) {
|
|
1182
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `Model=${modelIdArg}`);
|
|
1183
|
+
}
|
|
1184
|
+
return modelJson;
|
|
1185
|
+
}
|
|
1186
|
+
/** Read the properties for a Model as a json string.
|
|
1187
|
+
* @param modelIdArg a json string with the identity of the model to load. Must have either "id" or "code".
|
|
1188
|
+
* @returns a json string with the properties of the model or `undefined` if the model is not found.
|
|
1189
|
+
* @see getModelJson
|
|
1190
|
+
*/
|
|
1191
|
+
tryGetModelJson(modelIdArg) {
|
|
1192
|
+
try {
|
|
1193
|
+
return this._iModel.nativeDb.getModel(modelIdArg);
|
|
1194
|
+
}
|
|
1195
|
+
catch (err) {
|
|
1196
|
+
return undefined;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
/** Get the sub-model of the specified Element.
|
|
1200
|
+
* See [[IModelDb.Elements.queryElementIdByCode]] for more on how to find an element by Code.
|
|
1201
|
+
* @param modeledElementId Identifies the modeled element.
|
|
1202
|
+
* @param modelClass Optional class to validate instance against. This parameter can accept abstract or concrete classes, but should be the same as the template (`T`) parameter.
|
|
1203
|
+
* @throws [[IModelError]] if the sub-model is not found, cannot be loaded, or fails validation when `modelClass` is specified.
|
|
1204
|
+
* @see tryGetSubModel
|
|
1205
|
+
*/
|
|
1206
|
+
getSubModel(modeledElementId, modelClass) {
|
|
1207
|
+
const modeledElementProps = this._iModel.elements.getElementProps(modeledElementId);
|
|
1208
|
+
if (undefined === modeledElementProps.id || modeledElementProps.id === core_common_1.IModel.rootSubjectId)
|
|
1209
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, "Root subject does not have a sub-model");
|
|
1210
|
+
return this.getModel(modeledElementProps.id, modelClass);
|
|
1211
|
+
}
|
|
1212
|
+
/** Get the sub-model of the specified Element.
|
|
1213
|
+
* See [[IModelDb.Elements.queryElementIdByCode]] for more on how to find an element by Code.
|
|
1214
|
+
* @param modeledElementId Identifies the modeled element.
|
|
1215
|
+
* @param modelClass Optional class to validate instance against. This parameter can accept abstract or concrete classes, but should be the same as the template (`T`) parameter.
|
|
1216
|
+
* @returns The sub-model or `undefined` if the specified element does not have a sub-model or fails validation when `modelClass` is specified.
|
|
1217
|
+
* @see getSubModel
|
|
1218
|
+
*/
|
|
1219
|
+
tryGetSubModel(modeledElementId, modelClass) {
|
|
1220
|
+
const modeledElementProps = this._iModel.elements.tryGetElementProps(modeledElementId);
|
|
1221
|
+
if (undefined === (modeledElementProps === null || modeledElementProps === void 0 ? void 0 : modeledElementProps.id) || (core_common_1.IModel.rootSubjectId === modeledElementProps.id))
|
|
1222
|
+
return undefined;
|
|
1223
|
+
return this.tryGetModel(modeledElementProps.id, modelClass);
|
|
1224
|
+
}
|
|
1225
|
+
/** Create a new model in memory.
|
|
1226
|
+
* See the example in [[InformationPartitionElement]].
|
|
1227
|
+
* @param modelProps The properties to use when creating the model.
|
|
1228
|
+
* @throws [[IModelError]] if there is a problem creating the model.
|
|
1229
|
+
*/
|
|
1230
|
+
createModel(modelProps) { return this._iModel.constructEntity(modelProps); }
|
|
1231
|
+
/** Insert a new model.
|
|
1232
|
+
* @param props The data for the new model.
|
|
1233
|
+
* @returns The newly inserted model's Id.
|
|
1234
|
+
* @throws [[IModelError]] if unable to insert the model.
|
|
1235
|
+
*/
|
|
1236
|
+
insertModel(props) {
|
|
1237
|
+
try {
|
|
1238
|
+
return props.id = this._iModel.nativeDb.insertModel(props);
|
|
1239
|
+
}
|
|
1240
|
+
catch (err) {
|
|
1241
|
+
throw new core_common_1.IModelError(err.errorNumber, `Error inserting model [${err.message}], class=${props.classFullName}`);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
/** Update an existing model.
|
|
1245
|
+
* @param props the properties of the model to change
|
|
1246
|
+
* @throws [[IModelError]] if unable to update the model.
|
|
1247
|
+
*/
|
|
1248
|
+
updateModel(props) {
|
|
1249
|
+
try {
|
|
1250
|
+
this._iModel.nativeDb.updateModel(props);
|
|
1251
|
+
}
|
|
1252
|
+
catch (err) {
|
|
1253
|
+
throw new core_common_1.IModelError(err.errorNumber, `error updating model [${err.message}] id=${props.id}`);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
/** Mark the geometry of [[GeometricModel]] as having changed, by recording an indirect change to its GeometryGuid property.
|
|
1257
|
+
* Typically the GeometryGuid changes automatically when [[GeometricElement]]s within the model are modified, but
|
|
1258
|
+
* explicitly updating it is occasionally useful after modifying definition elements like line styles or materials that indirectly affect the appearance of
|
|
1259
|
+
* [[GeometricElement]]s that reference those definition elements in their geometry streams.
|
|
1260
|
+
* Cached [Tile]($frontend)s are only invalidated after the geometry guid of the model changes.
|
|
1261
|
+
* @note This will throw IModelError with [IModelStatus.VersionTooOld]($core-bentley) if a version of the BisCore schema older than 1.0.11 is present in the iModel.
|
|
1262
|
+
* @throws IModelError if unable to update the geometry guid.
|
|
1263
|
+
* @see [[TxnManager.onModelGeometryChanged]] for the event emitted in response to such a change.
|
|
1264
|
+
*/
|
|
1265
|
+
updateGeometryGuid(modelId) {
|
|
1266
|
+
const error = this._iModel.nativeDb.updateModelGeometryGuid(modelId);
|
|
1267
|
+
if (error !== core_bentley_1.IModelStatus.Success)
|
|
1268
|
+
throw new core_common_1.IModelError(error, `updating geometry guid for model ${modelId}`);
|
|
1269
|
+
}
|
|
1270
|
+
/** Delete one or more existing models.
|
|
1271
|
+
* @param ids The Ids of the models to be deleted
|
|
1272
|
+
* @throws [[IModelError]]
|
|
1273
|
+
*/
|
|
1274
|
+
deleteModel(ids) {
|
|
1275
|
+
core_bentley_1.Id64.toIdSet(ids).forEach((id) => {
|
|
1276
|
+
try {
|
|
1277
|
+
this._iModel.nativeDb.deleteModel(id);
|
|
1278
|
+
}
|
|
1279
|
+
catch (err) {
|
|
1280
|
+
throw new core_common_1.IModelError(err.errorNumber, `error deleting model [${err.message}] id ${id}`);
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
/** For each specified [[GeometricModel]], attempts to obtain the union of the volumes of all geometric elements within that model.
|
|
1285
|
+
* @param ids The Id or Ids of the [[GeometricModel]]s for which to obtain the extents.
|
|
1286
|
+
* @returns An array of results, one per supplied Id, in the order in which the Ids were supplied. If the extents could not be obtained, the
|
|
1287
|
+
* corresponding results entry's `extents` will be a "null" range (@see [Range3d.isNull]($geometry)) and its `status` will indicate
|
|
1288
|
+
* why the extents could not be obtained (e.g., because the Id did not identify a [[GeometricModel]]).
|
|
1289
|
+
* @see [[queryRange]] to obtain the union of all of the models' extents.
|
|
1290
|
+
*/
|
|
1291
|
+
async queryExtents(ids) {
|
|
1292
|
+
ids = typeof ids === "string" ? [ids] : ids;
|
|
1293
|
+
if (ids.length === 0)
|
|
1294
|
+
return [];
|
|
1295
|
+
return this._iModel.nativeDb.queryModelExtentsAsync(ids);
|
|
1296
|
+
}
|
|
1297
|
+
/** Computes the union of the volumes of all geoemtric elements within any number of [[GeometricModel]]s, specified by model Id.
|
|
1298
|
+
* @see [[queryExtents]] to obtain discrete volumes for each model.
|
|
1299
|
+
*/
|
|
1300
|
+
async queryRange(ids) {
|
|
1301
|
+
const results = await this.queryExtents(ids);
|
|
1302
|
+
const range = new core_geometry_1.Range3d();
|
|
1303
|
+
for (const result of results)
|
|
1304
|
+
range.union(core_geometry_1.Range3d.fromJSON(result.extents), range);
|
|
1305
|
+
return range;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
IModelDb.Models = Models;
|
|
1309
|
+
/** The collection of elements in an [[IModelDb]].
|
|
1310
|
+
* @public
|
|
1311
|
+
*/
|
|
1312
|
+
class Elements {
|
|
1313
|
+
/** @internal */
|
|
1314
|
+
constructor(_iModel) {
|
|
1315
|
+
this._iModel = _iModel;
|
|
1316
|
+
}
|
|
1317
|
+
/** Read element data from the iModel as JSON
|
|
1318
|
+
* @param elementIdArg a json string with the identity of the element to load. Must have one of "id", "federationGuid", or "code".
|
|
1319
|
+
* @returns The JSON properties of the element.
|
|
1320
|
+
* @throws [[IModelError]] if the element is not found or cannot be loaded.
|
|
1321
|
+
* @see tryGetElementJson
|
|
1322
|
+
* @internal
|
|
1323
|
+
*/
|
|
1324
|
+
getElementJson(elementId) {
|
|
1325
|
+
const elementProps = this.tryGetElementJson(elementId);
|
|
1326
|
+
if (undefined === elementProps)
|
|
1327
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `reading element=${elementId}`);
|
|
1328
|
+
return elementProps;
|
|
1329
|
+
}
|
|
1330
|
+
/** Read element data from the iModel as JSON
|
|
1331
|
+
* @param loadProps - a json string with the identity of the element to load. Must have one of "id", "federationGuid", or "code".
|
|
1332
|
+
* @returns The JSON properties of the element or `undefined` if the element is not found.
|
|
1333
|
+
* @throws [[IModelError]] if the element exists, but cannot be loaded.
|
|
1334
|
+
* @see getElementJson
|
|
1335
|
+
*/
|
|
1336
|
+
tryGetElementJson(loadProps) {
|
|
1337
|
+
try {
|
|
1338
|
+
return this._iModel.nativeDb.getElement(loadProps);
|
|
1339
|
+
}
|
|
1340
|
+
catch (err) {
|
|
1341
|
+
return undefined;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
/** Get properties of an Element by Id, FederationGuid, or Code
|
|
1345
|
+
* @throws [[IModelError]] if the element is not found or cannot be loaded.
|
|
1346
|
+
* @see tryGetElementProps
|
|
1347
|
+
*/
|
|
1348
|
+
getElementProps(props) {
|
|
1349
|
+
if (typeof props === "string") {
|
|
1350
|
+
props = core_bentley_1.Id64.isId64(props) ? { id: props } : { federationGuid: props };
|
|
1351
|
+
}
|
|
1352
|
+
else if (props instanceof core_common_1.Code) {
|
|
1353
|
+
props = { code: props };
|
|
1354
|
+
}
|
|
1355
|
+
try {
|
|
1356
|
+
return this._iModel.nativeDb.getElement(props);
|
|
1357
|
+
}
|
|
1358
|
+
catch (err) {
|
|
1359
|
+
throw new core_common_1.IModelError(err.errorNumber, err.message);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
/** Get properties of an Element by Id, FederationGuid, or Code
|
|
1363
|
+
* @returns The properties of the element or `undefined` if the element is not found.
|
|
1364
|
+
* @throws [[IModelError]] if the element exists, but cannot be loaded.
|
|
1365
|
+
* @note Useful for cases when an element may or may not exist and throwing an `Error` would be overkill.
|
|
1366
|
+
* @see getElementProps
|
|
1367
|
+
*/
|
|
1368
|
+
tryGetElementProps(elementId) {
|
|
1369
|
+
if (typeof elementId === "string") {
|
|
1370
|
+
elementId = core_bentley_1.Id64.isId64(elementId) ? { id: elementId } : { federationGuid: elementId };
|
|
1371
|
+
}
|
|
1372
|
+
else if (elementId instanceof core_common_1.Code) {
|
|
1373
|
+
elementId = { code: elementId };
|
|
1374
|
+
}
|
|
1375
|
+
return this.tryGetElementJson(elementId);
|
|
1376
|
+
}
|
|
1377
|
+
/** Get an element by Id, FederationGuid, or Code
|
|
1378
|
+
* @param elementId either the element's Id, Code, or FederationGuid, or an ElementLoadProps
|
|
1379
|
+
* @param elementClass Optional class to validate instance against. This parameter can accept abstract or concrete classes, but should be the same as the template (`T`) parameter.
|
|
1380
|
+
* @throws [[IModelError]] if the element is not found, cannot be loaded, or fails validation when `elementClass` is specified.
|
|
1381
|
+
* @see tryGetElement
|
|
1382
|
+
*/
|
|
1383
|
+
getElement(elementId, elementClass) {
|
|
1384
|
+
const element = this.tryGetElement(elementId, elementClass);
|
|
1385
|
+
if (undefined === element)
|
|
1386
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `Element=${elementId}`);
|
|
1387
|
+
return element;
|
|
1388
|
+
}
|
|
1389
|
+
/** Get an element by Id, FederationGuid, or Code
|
|
1390
|
+
* @param elementId either the element's Id, Code, or FederationGuid, or an ElementLoadProps
|
|
1391
|
+
* @param elementClass Optional class to validate instance against. This parameter can accept abstract or concrete classes, but should be the same as the template (`T`) parameter.
|
|
1392
|
+
* @returns The element or `undefined` if the element is not found or fails validation when `elementClass` is specified.
|
|
1393
|
+
* @throws [[IModelError]] if the element exists, but cannot be loaded.
|
|
1394
|
+
* @note Useful for cases when an element may or may not exist and throwing an `Error` would be overkill.
|
|
1395
|
+
* @see getElement
|
|
1396
|
+
*/
|
|
1397
|
+
tryGetElement(elementId, elementClass) {
|
|
1398
|
+
if (typeof elementId === "string")
|
|
1399
|
+
elementId = core_bentley_1.Id64.isId64(elementId) ? { id: elementId } : { federationGuid: elementId };
|
|
1400
|
+
else if (elementId instanceof core_common_1.Code)
|
|
1401
|
+
elementId = { code: elementId };
|
|
1402
|
+
else
|
|
1403
|
+
elementId.onlyBaseProperties = false; // we must load all properties to construct the element.
|
|
1404
|
+
const elementProps = this.tryGetElementJson(elementId);
|
|
1405
|
+
if (undefined === elementProps)
|
|
1406
|
+
return undefined; // no Element with that elementId found
|
|
1407
|
+
const element = this._iModel.constructEntity(elementProps);
|
|
1408
|
+
if (undefined === elementClass)
|
|
1409
|
+
return element; // elementClass was not specified, cannot call instanceof to validate
|
|
1410
|
+
return element instanceof elementClass ? element : undefined;
|
|
1411
|
+
}
|
|
1412
|
+
/** Query for the Id of the element that has a specified code.
|
|
1413
|
+
* This method is for the case where you know the element's Code.
|
|
1414
|
+
* If you only know the code *value*, then in the simplest case, you can query on that
|
|
1415
|
+
* and filter the results.
|
|
1416
|
+
* In the simple case, call [[IModelDb.queryEntityIds]], specifying the code value in the where clause of the query params.
|
|
1417
|
+
* Or, you can execute an ECSQL select statement. See
|
|
1418
|
+
* [frequently used ECSQL queries]($docs/learning/backend/ECSQL-queries.md) for an example.
|
|
1419
|
+
* @param code The code to look for
|
|
1420
|
+
* @returns The element that uses the code or undefined if the code is not used.
|
|
1421
|
+
* @throws IModelError if the code is invalid
|
|
1422
|
+
*/
|
|
1423
|
+
queryElementIdByCode(code) {
|
|
1424
|
+
if (core_bentley_1.Id64.isInvalid(code.spec))
|
|
1425
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidCodeSpec, "Invalid CodeSpec");
|
|
1426
|
+
if (code.value === undefined)
|
|
1427
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidCode, "Invalid Code");
|
|
1428
|
+
return this._iModel.withPreparedStatement("SELECT ECInstanceId FROM BisCore:Element WHERE CodeSpec.Id=? AND CodeScope.Id=? AND CodeValue=?", (stmt) => {
|
|
1429
|
+
stmt.bindId(1, code.spec);
|
|
1430
|
+
stmt.bindId(2, core_bentley_1.Id64.fromString(code.scope));
|
|
1431
|
+
stmt.bindString(3, code.value);
|
|
1432
|
+
if (core_bentley_1.DbResult.BE_SQLITE_ROW !== stmt.step())
|
|
1433
|
+
return undefined;
|
|
1434
|
+
return stmt.getValue(0).getId();
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
/** Query for the last modified time of the specified element.
|
|
1438
|
+
* @internal
|
|
1439
|
+
*/
|
|
1440
|
+
queryLastModifiedTime(elementId) {
|
|
1441
|
+
const sql = "SELECT LastMod FROM BisCore:Element WHERE ECInstanceId=:elementId";
|
|
1442
|
+
return this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1443
|
+
statement.bindId("elementId", elementId);
|
|
1444
|
+
if (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step())
|
|
1445
|
+
return statement.getValue(0).getDateTime();
|
|
1446
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, `Can't get lastMod time for Element ${elementId}`);
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
/** Create a new instance of an element.
|
|
1450
|
+
* @param elProps The properties of the new element.
|
|
1451
|
+
* @throws [[IModelError]] if there is a problem creating the element.
|
|
1452
|
+
*/
|
|
1453
|
+
createElement(elProps) { return this._iModel.constructEntity(elProps); }
|
|
1454
|
+
/** Insert a new element into the iModel.
|
|
1455
|
+
* @param elProps The properties of the new element.
|
|
1456
|
+
* @returns The newly inserted element's Id.
|
|
1457
|
+
* @throws [[IModelError]] if unable to insert the element.
|
|
1458
|
+
* @note For convenience, the value of `elProps.id` is updated to reflect the resultant element's id.
|
|
1459
|
+
* However when `elProps.federationGuid` is not present or undefined, a new Guid will be generated and stored on the resultant element. But
|
|
1460
|
+
* the value of `elProps.federationGuid` is *not* updated. Generally, it is best to re-read the element after inserting (e.g. via [[getElementProps]])
|
|
1461
|
+
* if you intend to continue working with it. That will ensure its values reflect the persistent state.
|
|
1462
|
+
*/
|
|
1463
|
+
insertElement(elProps) {
|
|
1464
|
+
try {
|
|
1465
|
+
return elProps.id = this._iModel.nativeDb.insertElement(elProps);
|
|
1466
|
+
}
|
|
1467
|
+
catch (err) {
|
|
1468
|
+
err.message = `error inserting element: ${err.message}`;
|
|
1469
|
+
throw err;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
/** Update some properties of an existing element.
|
|
1473
|
+
* To support clearing a property value, every property name that is present in the `elProps` object will be updated even if the value is `undefined`.
|
|
1474
|
+
* To keep an individual element property unchanged, it should either be excluded from the `elProps` parameter or set to its current value.
|
|
1475
|
+
* @param elProps the properties of the element to update.
|
|
1476
|
+
* @note As described above, this is a special case where there is a difference between a property being excluded and a property being present in `elProps` but set to `undefined`.
|
|
1477
|
+
* @throws [[IModelError]] if unable to update the element.
|
|
1478
|
+
*/
|
|
1479
|
+
updateElement(elProps) {
|
|
1480
|
+
try {
|
|
1481
|
+
this._iModel.nativeDb.updateElement(elProps);
|
|
1482
|
+
}
|
|
1483
|
+
catch (err) {
|
|
1484
|
+
err.message = `error updating element: ${err.message}`;
|
|
1485
|
+
throw err;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
/** Delete one or more elements from this iModel.
|
|
1489
|
+
* @param ids The set of Ids of the element(s) to be deleted
|
|
1490
|
+
* @throws [[IModelError]]
|
|
1491
|
+
* @see deleteDefinitionElements
|
|
1492
|
+
*/
|
|
1493
|
+
deleteElement(ids) {
|
|
1494
|
+
const iModel = this._iModel;
|
|
1495
|
+
core_bentley_1.Id64.toIdSet(ids).forEach((id) => {
|
|
1496
|
+
try {
|
|
1497
|
+
iModel.nativeDb.deleteElement(id);
|
|
1498
|
+
}
|
|
1499
|
+
catch (err) {
|
|
1500
|
+
err.message = `error deleting element: ${err.message}`;
|
|
1501
|
+
throw err;
|
|
1502
|
+
}
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
/** DefinitionElements can only be deleted if it can be determined that they are not referenced by other Elements.
|
|
1506
|
+
* This *usage query* can be expensive since it may involve scanning the GeometryStreams of all GeometricElements.
|
|
1507
|
+
* Since [[deleteElement]] does not perform these additional checks, it fails in order to prevent potentially referenced DefinitionElements from being deleted.
|
|
1508
|
+
* This method performs those expensive checks and then calls *delete* if not referenced.
|
|
1509
|
+
* @param ids The Ids of the DefinitionElements to attempt to delete. To prevent multiple passes over the same GeometricElements, it is best to pass in the entire array of
|
|
1510
|
+
* DefinitionElements rather than calling this method separately for each one. Ids that are not valid DefinitionElements will be ignored.
|
|
1511
|
+
* @returns An IdSet of the DefinitionElements that are used and were therefore not deleted.
|
|
1512
|
+
* @see deleteElement
|
|
1513
|
+
* @beta
|
|
1514
|
+
*/
|
|
1515
|
+
deleteDefinitionElements(definitionElementIds) {
|
|
1516
|
+
const usageInfo = this._iModel.nativeDb.queryDefinitionElementUsage(definitionElementIds);
|
|
1517
|
+
if (!usageInfo) {
|
|
1518
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadRequest, "Error querying for DefinitionElement usage");
|
|
1519
|
+
}
|
|
1520
|
+
const usedIdSet = usageInfo.usedIds ? core_bentley_1.Id64.toIdSet(usageInfo.usedIds) : new Set();
|
|
1521
|
+
const deleteIfUnused = (ids, used) => {
|
|
1522
|
+
if (ids) {
|
|
1523
|
+
ids.forEach((id) => {
|
|
1524
|
+
if (!used.has(id))
|
|
1525
|
+
this._iModel.elements.deleteElement(id);
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
};
|
|
1529
|
+
try {
|
|
1530
|
+
this._iModel.nativeDb.beginPurgeOperation();
|
|
1531
|
+
deleteIfUnused(usageInfo.spatialCategoryIds, usedIdSet);
|
|
1532
|
+
deleteIfUnused(usageInfo.drawingCategoryIds, usedIdSet);
|
|
1533
|
+
deleteIfUnused(usageInfo.viewDefinitionIds, usedIdSet);
|
|
1534
|
+
deleteIfUnused(usageInfo.geometryPartIds, usedIdSet);
|
|
1535
|
+
deleteIfUnused(usageInfo.lineStyleIds, usedIdSet);
|
|
1536
|
+
deleteIfUnused(usageInfo.renderMaterialIds, usedIdSet);
|
|
1537
|
+
deleteIfUnused(usageInfo.subCategoryIds, usedIdSet);
|
|
1538
|
+
deleteIfUnused(usageInfo.textureIds, usedIdSet);
|
|
1539
|
+
deleteIfUnused(usageInfo.displayStyleIds, usedIdSet);
|
|
1540
|
+
deleteIfUnused(usageInfo.categorySelectorIds, usedIdSet);
|
|
1541
|
+
deleteIfUnused(usageInfo.modelSelectorIds, usedIdSet);
|
|
1542
|
+
if (usageInfo.otherDefinitionElementIds) {
|
|
1543
|
+
this._iModel.elements.deleteElement(usageInfo.otherDefinitionElementIds);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
finally {
|
|
1547
|
+
this._iModel.nativeDb.endPurgeOperation();
|
|
1548
|
+
}
|
|
1549
|
+
if (usageInfo.viewDefinitionIds) {
|
|
1550
|
+
// take another pass in case a deleted ViewDefinition was the only usage of these view-related DefinitionElements
|
|
1551
|
+
let viewRelatedIds = [];
|
|
1552
|
+
if (usageInfo.displayStyleIds)
|
|
1553
|
+
viewRelatedIds = viewRelatedIds.concat(usageInfo.displayStyleIds.filter((id) => usedIdSet.has(id)));
|
|
1554
|
+
if (usageInfo.categorySelectorIds)
|
|
1555
|
+
viewRelatedIds = viewRelatedIds.concat(usageInfo.categorySelectorIds.filter((id) => usedIdSet.has(id)));
|
|
1556
|
+
if (usageInfo.modelSelectorIds)
|
|
1557
|
+
viewRelatedIds = viewRelatedIds.concat(usageInfo.modelSelectorIds.filter((id) => usedIdSet.has(id)));
|
|
1558
|
+
if (viewRelatedIds.length > 0) {
|
|
1559
|
+
const viewRelatedUsageInfo = this._iModel.nativeDb.queryDefinitionElementUsage(viewRelatedIds);
|
|
1560
|
+
if (viewRelatedUsageInfo) {
|
|
1561
|
+
const usedViewRelatedIdSet = viewRelatedUsageInfo.usedIds ? core_bentley_1.Id64.toIdSet(viewRelatedUsageInfo.usedIds) : new Set();
|
|
1562
|
+
try {
|
|
1563
|
+
this._iModel.nativeDb.beginPurgeOperation();
|
|
1564
|
+
deleteIfUnused(viewRelatedUsageInfo.displayStyleIds, usedViewRelatedIdSet);
|
|
1565
|
+
deleteIfUnused(viewRelatedUsageInfo.categorySelectorIds, usedViewRelatedIdSet);
|
|
1566
|
+
deleteIfUnused(viewRelatedUsageInfo.modelSelectorIds, usedViewRelatedIdSet);
|
|
1567
|
+
}
|
|
1568
|
+
finally {
|
|
1569
|
+
this._iModel.nativeDb.endPurgeOperation();
|
|
1570
|
+
}
|
|
1571
|
+
viewRelatedIds.forEach((id) => {
|
|
1572
|
+
if (!usedViewRelatedIdSet.has(id))
|
|
1573
|
+
usedIdSet.delete(id);
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return usedIdSet;
|
|
1579
|
+
}
|
|
1580
|
+
/** Query for the child elements of the specified element.
|
|
1581
|
+
* @returns Returns an array of child element identifiers.
|
|
1582
|
+
* @throws [[IModelError]]
|
|
1583
|
+
*/
|
|
1584
|
+
queryChildren(elementId) {
|
|
1585
|
+
const sql = "SELECT ECInstanceId FROM BisCore:Element WHERE Parent.Id=:elementId";
|
|
1586
|
+
return this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1587
|
+
statement.bindId("elementId", elementId);
|
|
1588
|
+
const childIds = [];
|
|
1589
|
+
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1590
|
+
childIds.push(statement.getValue(0).getId());
|
|
1591
|
+
}
|
|
1592
|
+
return childIds;
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
/** Query for the parent of the specified element.
|
|
1596
|
+
* @param elementId The element to check for a parent
|
|
1597
|
+
* @returns The identifier of the element's parent or undefined if the element has no parent
|
|
1598
|
+
* @throws [[IModelError]] if the element does not exist
|
|
1599
|
+
*/
|
|
1600
|
+
queryParent(elementId) {
|
|
1601
|
+
return this._iModel.withPreparedStatement(`select parent.id from ${Element_1.Element.classFullName} where ecinstanceid=?`, (stmt) => {
|
|
1602
|
+
stmt.bindId(1, elementId);
|
|
1603
|
+
if (stmt.step() !== core_bentley_1.DbResult.BE_SQLITE_ROW)
|
|
1604
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `Element=${elementId}`);
|
|
1605
|
+
const value = stmt.getValue(0);
|
|
1606
|
+
return value.isNull ? undefined : value.getId();
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
/** Returns true if the specified Element has a sub-model.
|
|
1610
|
+
* @see [[IModelDb.Models.getSubModel]]
|
|
1611
|
+
*/
|
|
1612
|
+
hasSubModel(elementId) {
|
|
1613
|
+
if (core_common_1.IModel.rootSubjectId === elementId)
|
|
1614
|
+
return false; // Special case since the RepositoryModel does not sub-model the root Subject
|
|
1615
|
+
// A sub-model will have the same Id value as the element it is describing
|
|
1616
|
+
const sql = "SELECT ECInstanceId FROM BisCore:Model WHERE ECInstanceId=:elementId";
|
|
1617
|
+
return this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1618
|
+
statement.bindId("elementId", elementId);
|
|
1619
|
+
return core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step();
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
/** Get the root subject element. */
|
|
1623
|
+
getRootSubject() { return this.getElement(core_common_1.IModel.rootSubjectId); }
|
|
1624
|
+
/** Query for aspects of a particular class (polymorphically) associated with this element.
|
|
1625
|
+
* @throws [[IModelError]]
|
|
1626
|
+
* @note Most cases should use the [[getAspects]] wrapper rather than calling this method directly.
|
|
1627
|
+
* @internal
|
|
1628
|
+
*/
|
|
1629
|
+
_queryAspects(elementId, fromClassFullName, excludedClassFullNames) {
|
|
1630
|
+
const sql = `SELECT ECInstanceId,ECClassId FROM ${fromClassFullName} WHERE Element.Id=:elementId ORDER BY ECClassId,ECInstanceId`; // ORDER BY to maximize statement reuse
|
|
1631
|
+
return this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1632
|
+
statement.bindId("elementId", elementId);
|
|
1633
|
+
const aspects = [];
|
|
1634
|
+
while (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1635
|
+
const aspectInstanceId = statement.getValue(0).getId();
|
|
1636
|
+
const aspectClassFullName = statement.getValue(1).getClassNameForClassId().replace(".", ":");
|
|
1637
|
+
if ((undefined === excludedClassFullNames) || (!excludedClassFullNames.has(aspectClassFullName))) {
|
|
1638
|
+
aspects.push(this._queryAspect(aspectInstanceId, aspectClassFullName));
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
return aspects;
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
/** Query for aspect by ECInstanceId
|
|
1645
|
+
* @throws [[IModelError]]
|
|
1646
|
+
*/
|
|
1647
|
+
_queryAspect(aspectInstanceId, aspectClassName) {
|
|
1648
|
+
const sql = `SELECT * FROM ${aspectClassName} WHERE ECInstanceId=:aspectInstanceId`;
|
|
1649
|
+
const aspect = this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1650
|
+
statement.bindId("aspectInstanceId", aspectInstanceId);
|
|
1651
|
+
if (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) {
|
|
1652
|
+
const aspectProps = statement.getRow(); // start with everything that SELECT * returned
|
|
1653
|
+
aspectProps.classFullName = aspectProps.className.replace(".", ":"); // add in property required by EntityProps
|
|
1654
|
+
aspectProps.className = undefined; // clear property from SELECT * that we don't want in the final instance
|
|
1655
|
+
return aspectProps;
|
|
1656
|
+
}
|
|
1657
|
+
return undefined;
|
|
1658
|
+
});
|
|
1659
|
+
if (undefined === aspect) {
|
|
1660
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `ElementAspect not found ${aspectInstanceId}, ${aspectClassName}`);
|
|
1661
|
+
}
|
|
1662
|
+
return this._iModel.constructEntity(aspect);
|
|
1663
|
+
}
|
|
1664
|
+
/** Get a single ElementAspect by its instance Id.
|
|
1665
|
+
* @throws [[IModelError]]
|
|
1666
|
+
*/
|
|
1667
|
+
getAspect(aspectInstanceId) {
|
|
1668
|
+
const sql = "SELECT ECClassId FROM BisCore:ElementAspect WHERE ECInstanceId=:aspectInstanceId";
|
|
1669
|
+
const aspectClassFullName = this._iModel.withPreparedStatement(sql, (statement) => {
|
|
1670
|
+
statement.bindId("aspectInstanceId", aspectInstanceId);
|
|
1671
|
+
return (core_bentley_1.DbResult.BE_SQLITE_ROW === statement.step()) ? statement.getValue(0).getClassNameForClassId().replace(".", ":") : undefined;
|
|
1672
|
+
});
|
|
1673
|
+
if (undefined === aspectClassFullName) {
|
|
1674
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NotFound, `ElementAspect not found ${aspectInstanceId}`);
|
|
1675
|
+
}
|
|
1676
|
+
return this._queryAspect(aspectInstanceId, aspectClassFullName);
|
|
1677
|
+
}
|
|
1678
|
+
/** Get the ElementAspect instances that are owned by the specified element.
|
|
1679
|
+
* @param elementId Get ElementAspects associated with this Element
|
|
1680
|
+
* @param aspectClassFullName Optionally filter ElementAspects polymorphically by this class name
|
|
1681
|
+
* @throws [[IModelError]]
|
|
1682
|
+
*/
|
|
1683
|
+
getAspects(elementId, aspectClassFullName) {
|
|
1684
|
+
if (undefined === aspectClassFullName) {
|
|
1685
|
+
const uniqueAspects = this._queryAspects(elementId, ElementAspect_1.ElementUniqueAspect.classFullName);
|
|
1686
|
+
const multiAspects = this._queryAspects(elementId, ElementAspect_1.ElementMultiAspect.classFullName);
|
|
1687
|
+
return uniqueAspects.concat(multiAspects);
|
|
1688
|
+
}
|
|
1689
|
+
const aspects = this._queryAspects(elementId, aspectClassFullName);
|
|
1690
|
+
return aspects;
|
|
1691
|
+
}
|
|
1692
|
+
/** Insert a new ElementAspect into the iModel.
|
|
1693
|
+
* @param aspectProps The properties of the new ElementAspect.
|
|
1694
|
+
* @throws [[IModelError]] if unable to insert the ElementAspect.
|
|
1695
|
+
* @returns the id of the newly inserted aspect.
|
|
1696
|
+
* @note Aspect Ids may collide with element Ids, so don't put both in a container like Set or Map
|
|
1697
|
+
* use [EntityReference]($common) for that instead.
|
|
1698
|
+
*/
|
|
1699
|
+
insertAspect(aspectProps) {
|
|
1700
|
+
try {
|
|
1701
|
+
return this._iModel.nativeDb.insertElementAspect(aspectProps);
|
|
1702
|
+
}
|
|
1703
|
+
catch (err) {
|
|
1704
|
+
throw new core_common_1.IModelError(err.errorNumber, `Error inserting ElementAspect [${err.message}], class: ${aspectProps.classFullName}`);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
/** Update an exist ElementAspect within the iModel.
|
|
1708
|
+
* @param aspectProps The properties to use to update the ElementAspect.
|
|
1709
|
+
* @throws [[IModelError]] if unable to update the ElementAspect.
|
|
1710
|
+
*/
|
|
1711
|
+
updateAspect(aspectProps) {
|
|
1712
|
+
try {
|
|
1713
|
+
this._iModel.nativeDb.updateElementAspect(aspectProps);
|
|
1714
|
+
}
|
|
1715
|
+
catch (err) {
|
|
1716
|
+
throw new core_common_1.IModelError(err.errorNumber, `Error updating ElementAspect [${err.message}], id: ${aspectProps.id}`);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
/** Delete one or more ElementAspects from this iModel.
|
|
1720
|
+
* @param aspectInstanceIds The set of instance Ids of the ElementAspect(s) to be deleted
|
|
1721
|
+
* @throws [[IModelError]] if unable to delete the ElementAspect.
|
|
1722
|
+
*/
|
|
1723
|
+
deleteAspect(aspectInstanceIds) {
|
|
1724
|
+
const iModel = this._iModel;
|
|
1725
|
+
core_bentley_1.Id64.toIdSet(aspectInstanceIds).forEach((aspectInstanceId) => {
|
|
1726
|
+
try {
|
|
1727
|
+
iModel.nativeDb.deleteElementAspect(aspectInstanceId);
|
|
1728
|
+
}
|
|
1729
|
+
catch (err) {
|
|
1730
|
+
throw new core_common_1.IModelError(err.errorNumber, `Error deleting ElementAspect [${err.message}], id: ${aspectInstanceId}`);
|
|
1731
|
+
}
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
IModelDb.Elements = Elements;
|
|
1736
|
+
/** The collection of views in an [[IModelDb]].
|
|
1737
|
+
* @public
|
|
1738
|
+
*/
|
|
1739
|
+
class Views {
|
|
1740
|
+
/** @internal */
|
|
1741
|
+
constructor(_iModel) {
|
|
1742
|
+
this._iModel = _iModel;
|
|
1743
|
+
}
|
|
1744
|
+
/** Query for the array of ViewDefinitionProps of the specified class and matching the specified IsPrivate setting.
|
|
1745
|
+
* @param className Query for view definitions of this class.
|
|
1746
|
+
* @param wantPrivate If true, include private view definitions.
|
|
1747
|
+
*/
|
|
1748
|
+
queryViewDefinitionProps(className = "BisCore.ViewDefinition", limit = IModelDb.defaultLimit, offset = 0, wantPrivate = false) {
|
|
1749
|
+
const where = (wantPrivate === false) ? "IsPrivate=FALSE" : "";
|
|
1750
|
+
const ids = this._iModel.queryEntityIds({ from: className, limit, offset, where });
|
|
1751
|
+
const props = [];
|
|
1752
|
+
const imodel = this._iModel;
|
|
1753
|
+
ids.forEach((id) => {
|
|
1754
|
+
try {
|
|
1755
|
+
props.push(imodel.elements.getElementProps(id));
|
|
1756
|
+
}
|
|
1757
|
+
catch (err) { }
|
|
1758
|
+
});
|
|
1759
|
+
return props;
|
|
1760
|
+
}
|
|
1761
|
+
/** Iterate all ViewDefinitions matching the supplied query.
|
|
1762
|
+
* @param params Specifies the query by which views are selected.
|
|
1763
|
+
* @param callback Function invoked for each ViewDefinition matching the query. Return false to terminate iteration, true to continue.
|
|
1764
|
+
* @returns true if all views were iterated, false if iteration was terminated early due to callback returning false.
|
|
1765
|
+
*
|
|
1766
|
+
* **Example: Finding all views of a specific DrawingModel**
|
|
1767
|
+
* ``` ts
|
|
1768
|
+
* [[include:IModelDb.Views.iterateViews]]
|
|
1769
|
+
* ```
|
|
1770
|
+
*/
|
|
1771
|
+
iterateViews(params, callback) {
|
|
1772
|
+
const ids = this._iModel.queryEntityIds(params);
|
|
1773
|
+
let finished = true;
|
|
1774
|
+
for (const id of ids) {
|
|
1775
|
+
try {
|
|
1776
|
+
const view = this._iModel.elements.getElement(id);
|
|
1777
|
+
if (undefined !== view && view instanceof ViewDefinition_1.ViewDefinition) {
|
|
1778
|
+
finished = callback(view);
|
|
1779
|
+
if (!finished)
|
|
1780
|
+
break;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
catch (err) { }
|
|
1784
|
+
}
|
|
1785
|
+
return finished;
|
|
1786
|
+
}
|
|
1787
|
+
loadViewStateProps(viewDefinitionElement, options, drawingExtents) {
|
|
1788
|
+
const elements = this._iModel.elements;
|
|
1789
|
+
const viewDefinitionProps = viewDefinitionElement.toJSON();
|
|
1790
|
+
const categorySelectorProps = elements.getElementProps(viewDefinitionProps.categorySelectorId);
|
|
1791
|
+
const displayStyleProps = elements.getElementProps({
|
|
1792
|
+
id: viewDefinitionProps.displayStyleId,
|
|
1793
|
+
displayStyle: options === null || options === void 0 ? void 0 : options.displayStyle,
|
|
1794
|
+
});
|
|
1795
|
+
const viewStateData = {
|
|
1796
|
+
viewDefinitionProps,
|
|
1797
|
+
displayStyleProps,
|
|
1798
|
+
categorySelectorProps,
|
|
1799
|
+
};
|
|
1800
|
+
const modelSelectorId = viewDefinitionProps.modelSelectorId;
|
|
1801
|
+
if (modelSelectorId !== undefined) {
|
|
1802
|
+
viewStateData.modelSelectorProps = elements.getElementProps(modelSelectorId);
|
|
1803
|
+
}
|
|
1804
|
+
else if (viewDefinitionElement instanceof ViewDefinition_1.SheetViewDefinition) {
|
|
1805
|
+
viewStateData.sheetProps = elements.getElementProps(viewDefinitionElement.baseModelId);
|
|
1806
|
+
viewStateData.sheetAttachments = Array.from(this._iModel.queryEntityIds({
|
|
1807
|
+
from: "BisCore.ViewAttachment",
|
|
1808
|
+
where: `Model.Id=${viewDefinitionElement.baseModelId}`,
|
|
1809
|
+
}));
|
|
1810
|
+
}
|
|
1811
|
+
else if (viewDefinitionElement instanceof ViewDefinition_1.DrawingViewDefinition) {
|
|
1812
|
+
// Ensure view has known extents
|
|
1813
|
+
if (drawingExtents && !drawingExtents.isNull)
|
|
1814
|
+
viewStateData.modelExtents = drawingExtents.toJSON();
|
|
1815
|
+
// Include information about the associated [[SectionDrawing]], if any.
|
|
1816
|
+
// NB: The SectionDrawing ECClass may not exist in the iModel's version of the BisCore ECSchema.
|
|
1817
|
+
try {
|
|
1818
|
+
const sectionDrawing = this._iModel.elements.tryGetElement(viewDefinitionElement.baseModelId);
|
|
1819
|
+
if (sectionDrawing && sectionDrawing.spatialView && core_bentley_1.Id64.isValidId64(sectionDrawing.spatialView.id)) {
|
|
1820
|
+
viewStateData.sectionDrawing = {
|
|
1821
|
+
spatialView: sectionDrawing.spatialView.id,
|
|
1822
|
+
displaySpatialView: true === sectionDrawing.jsonProperties.displaySpatialView,
|
|
1823
|
+
drawingToSpatialTransform: sectionDrawing.jsonProperties.drawingToSpatialTransform,
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
catch {
|
|
1828
|
+
//
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
return viewStateData;
|
|
1832
|
+
}
|
|
1833
|
+
/** @deprecated in 3.x. use [[getViewStateProps]]. */
|
|
1834
|
+
getViewStateData(viewDefinitionId, options) {
|
|
1835
|
+
const view = this._iModel.elements.getElement(viewDefinitionId);
|
|
1836
|
+
let drawingExtents;
|
|
1837
|
+
if (view instanceof ViewDefinition_1.DrawingViewDefinition) {
|
|
1838
|
+
try {
|
|
1839
|
+
drawingExtents = core_geometry_1.Range3d.fromJSON(this._iModel.nativeDb.queryModelExtents({ id: view.baseModelId }).modelExtents);
|
|
1840
|
+
}
|
|
1841
|
+
catch {
|
|
1842
|
+
//
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
return this.loadViewStateProps(view, options, drawingExtents);
|
|
1846
|
+
}
|
|
1847
|
+
/** Obtain a [ViewStateProps]($common) for a [[ViewDefinition]] specified by element Id. */
|
|
1848
|
+
async getViewStateProps(viewDefinitionId, options) {
|
|
1849
|
+
const view = this._iModel.elements.getElement(viewDefinitionId);
|
|
1850
|
+
let drawingExtents;
|
|
1851
|
+
if (view instanceof ViewDefinition_1.DrawingViewDefinition)
|
|
1852
|
+
drawingExtents = (await this._iModel.models.queryRange(view.baseModelId));
|
|
1853
|
+
return this.loadViewStateProps(view, options, drawingExtents);
|
|
1854
|
+
}
|
|
1855
|
+
getViewThumbnailArg(viewDefinitionId) {
|
|
1856
|
+
return { namespace: "dgn_View", name: "Thumbnail", id: viewDefinitionId };
|
|
1857
|
+
}
|
|
1858
|
+
/** Get the thumbnail for a view.
|
|
1859
|
+
* @param viewDefinitionId The Id of the view for thumbnail
|
|
1860
|
+
* @returns the ThumbnailProps, or undefined if no thumbnail exists.
|
|
1861
|
+
*/
|
|
1862
|
+
getThumbnail(viewDefinitionId) {
|
|
1863
|
+
const viewArg = this.getViewThumbnailArg(viewDefinitionId);
|
|
1864
|
+
const sizeProps = this._iModel.nativeDb.queryFileProperty(viewArg, true);
|
|
1865
|
+
if (undefined === sizeProps)
|
|
1866
|
+
return undefined;
|
|
1867
|
+
const out = JSON.parse(sizeProps);
|
|
1868
|
+
out.image = this._iModel.nativeDb.queryFileProperty(viewArg, false);
|
|
1869
|
+
return out;
|
|
1870
|
+
}
|
|
1871
|
+
/** Save a thumbnail for a view.
|
|
1872
|
+
* @param viewDefinitionId The Id of the view for thumbnail
|
|
1873
|
+
* @param thumbnail The thumbnail data.
|
|
1874
|
+
* @returns 0 if successful
|
|
1875
|
+
*/
|
|
1876
|
+
saveThumbnail(viewDefinitionId, thumbnail) {
|
|
1877
|
+
const viewArg = this.getViewThumbnailArg(viewDefinitionId);
|
|
1878
|
+
const props = { format: thumbnail.format, height: thumbnail.height, width: thumbnail.width };
|
|
1879
|
+
this._iModel.nativeDb.saveFileProperty(viewArg, JSON.stringify(props), thumbnail.image);
|
|
1880
|
+
return 0;
|
|
1881
|
+
}
|
|
1882
|
+
/** Set the default view property the iModel
|
|
1883
|
+
* @param viewId The Id of the ViewDefinition to use as the default
|
|
1884
|
+
*/
|
|
1885
|
+
setDefaultViewId(viewId) {
|
|
1886
|
+
const spec = { namespace: "dgn_View", name: "DefaultView" };
|
|
1887
|
+
const blob32 = new Uint32Array(2);
|
|
1888
|
+
blob32[0] = core_bentley_1.Id64.getLowerUint32(viewId);
|
|
1889
|
+
blob32[1] = core_bentley_1.Id64.getUpperUint32(viewId);
|
|
1890
|
+
const blob8 = new Uint8Array(blob32.buffer);
|
|
1891
|
+
this._iModel.saveFileProperty(spec, undefined, blob8);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
/** Default parameters for iterating/querying ViewDefinitions. Includes all subclasses of ViewDefinition, excluding only those marked 'private'. */
|
|
1895
|
+
Views.defaultQueryParams = { from: "BisCore.ViewDefinition", where: "IsPrivate=FALSE" };
|
|
1896
|
+
IModelDb.Views = Views;
|
|
1897
|
+
/** Represents the current state of a pollable tile content request.
|
|
1898
|
+
* Note: lack of a "completed" state because polling a completed request returns the content as a Uint8Array.
|
|
1899
|
+
* @internal
|
|
1900
|
+
*/
|
|
1901
|
+
let TileContentState;
|
|
1902
|
+
(function (TileContentState) {
|
|
1903
|
+
TileContentState[TileContentState["New"] = 0] = "New";
|
|
1904
|
+
TileContentState[TileContentState["Pending"] = 1] = "Pending";
|
|
1905
|
+
TileContentState[TileContentState["Loading"] = 2] = "Loading";
|
|
1906
|
+
})(TileContentState = IModelDb.TileContentState || (IModelDb.TileContentState = {}));
|
|
1907
|
+
/** @internal */
|
|
1908
|
+
class Tiles {
|
|
1909
|
+
/** @internal */
|
|
1910
|
+
constructor(_iModel) {
|
|
1911
|
+
this._iModel = _iModel;
|
|
1912
|
+
}
|
|
1913
|
+
/** @internal */
|
|
1914
|
+
async requestTileTreeProps(id) {
|
|
1915
|
+
return new Promise((resolve, reject) => {
|
|
1916
|
+
this._iModel.nativeDb.getTileTree(id, (ret) => {
|
|
1917
|
+
if (undefined !== ret.error)
|
|
1918
|
+
reject(new core_common_1.IModelError(ret.error.status, `TreeId=${id}`));
|
|
1919
|
+
else
|
|
1920
|
+
resolve(ret.result);
|
|
1921
|
+
});
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1924
|
+
pollTileContent(resolve, reject, treeId, tileId) {
|
|
1925
|
+
let ret;
|
|
1926
|
+
try {
|
|
1927
|
+
ret = this._iModel.nativeDb.pollTileContent(treeId, tileId);
|
|
1928
|
+
}
|
|
1929
|
+
catch (err) {
|
|
1930
|
+
// Typically "imodel not open".
|
|
1931
|
+
reject(err);
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
if (ret.error) {
|
|
1935
|
+
reject(new core_common_1.IModelError(ret.error.status, `TreeId=${treeId} TileId=${tileId}`));
|
|
1936
|
+
}
|
|
1937
|
+
else if (ret.result && typeof ret.result !== "number") { // if type is not a number, it's the TileContent interface
|
|
1938
|
+
const res = ret.result;
|
|
1939
|
+
const iModelId = this._iModel.iModelId;
|
|
1940
|
+
const tileSizeThreshold = IModelHost_1.IModelHost.logTileSizeThreshold;
|
|
1941
|
+
const tileSize = res.content.length;
|
|
1942
|
+
if (tileSize > tileSizeThreshold) {
|
|
1943
|
+
core_bentley_1.Logger.logWarning(loggerCategory, "Tile size (in bytes) larger than specified threshold", () => ({ tileSize, tileSizeThreshold, treeId, tileId, iModelId }));
|
|
1944
|
+
}
|
|
1945
|
+
const loadTimeThreshold = IModelHost_1.IModelHost.logTileLoadTimeThreshold;
|
|
1946
|
+
const loadTime = res.elapsedSeconds;
|
|
1947
|
+
if (loadTime > loadTimeThreshold) {
|
|
1948
|
+
core_bentley_1.Logger.logWarning(loggerCategory, "Tile load time (in seconds) greater than specified threshold", () => ({ loadTime, loadTimeThreshold, treeId, tileId, iModelId }));
|
|
1949
|
+
}
|
|
1950
|
+
resolve(res);
|
|
1951
|
+
}
|
|
1952
|
+
else { // if the type is a number, it's the TileContentState enum
|
|
1953
|
+
// ###TODO: Decide appropriate timeout interval. May want to switch on state (new vs loading vs pending)
|
|
1954
|
+
setTimeout(() => this.pollTileContent(resolve, reject, treeId, tileId), 10);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
/** @internal */
|
|
1958
|
+
async requestTileContent(treeId, tileId) {
|
|
1959
|
+
return new Promise((resolve, reject) => {
|
|
1960
|
+
this.pollTileContent(resolve, reject, treeId, tileId);
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1963
|
+
/** @internal */
|
|
1964
|
+
async getTileContent(treeId, tileId) {
|
|
1965
|
+
const ret = await new Promise((resolve) => {
|
|
1966
|
+
this._iModel.nativeDb.getTileContent(treeId, tileId, resolve);
|
|
1967
|
+
});
|
|
1968
|
+
if (undefined !== ret.error) {
|
|
1969
|
+
throw new core_common_1.IModelError(ret.error.status, `TreeId=${treeId} TileId=${tileId}`);
|
|
1970
|
+
}
|
|
1971
|
+
(0, core_bentley_1.assert)(undefined !== ret.result);
|
|
1972
|
+
return ret.result;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
IModelDb.Tiles = Tiles;
|
|
1976
|
+
})(IModelDb = exports.IModelDb || (exports.IModelDb = {}));
|
|
1977
|
+
/**
|
|
1978
|
+
* A local copy of an iModel from iModelHub that can pull and potentially push changesets.
|
|
1979
|
+
* BriefcaseDb raises a set of events to allow apps and subsystems to track its object life cycle, including [[onOpen]] and [[onOpened]].
|
|
1980
|
+
* @public
|
|
1981
|
+
*/
|
|
1982
|
+
class BriefcaseDb extends IModelDb {
|
|
1983
|
+
constructor(args) {
|
|
1984
|
+
super({ ...args, changeset: args.nativeDb.getCurrentChangeset() });
|
|
1985
|
+
/** Manages local changes to this briefcase. */
|
|
1986
|
+
this.txns = new TxnManager_1.TxnManager(this);
|
|
1987
|
+
this._openMode = args.openMode;
|
|
1988
|
+
this.briefcaseId = args.briefcaseId;
|
|
1989
|
+
if (this.useLockServer) // if the iModel uses a lock server, create a ServerBasedLocks LockControl for this BriefcaseDb.
|
|
1990
|
+
this._locks = new ServerBasedLocks_1.ServerBasedLocks(this);
|
|
1991
|
+
}
|
|
1992
|
+
/** override superclass method */
|
|
1993
|
+
get isBriefcase() { return true; }
|
|
1994
|
+
static findByKey(key) {
|
|
1995
|
+
return super.findByKey(key);
|
|
1996
|
+
}
|
|
1997
|
+
static tryFindByKey(key) {
|
|
1998
|
+
const db = super.tryFindByKey(key);
|
|
1999
|
+
return (db === null || db === void 0 ? void 0 : db.isBriefcaseDb()) ? db : undefined;
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* The Guid that identifies the *context* that owns this iModel.
|
|
2003
|
+
* GuidString | undefined for the superclass, but required for BriefcaseDb
|
|
2004
|
+
* */
|
|
2005
|
+
get iTwinId() { return super.iTwinId; } // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
2006
|
+
/**
|
|
2007
|
+
* Determine whether this BriefcaseDb should use a lock server.
|
|
2008
|
+
* All must be true:
|
|
2009
|
+
* - file is open for write
|
|
2010
|
+
* - has an assigned briefcaseId
|
|
2011
|
+
* - the "no locking" flag is not present. This is a property of an iModel, established when the iModel is created in IModelHub.
|
|
2012
|
+
*/
|
|
2013
|
+
get useLockServer() {
|
|
2014
|
+
return !this.isReadonly && (this.briefcaseId !== core_common_1.BriefcaseIdValue.Unassigned) && (undefined === this.nativeDb.queryLocalValue(BriefcaseLocalValue.NoLocking));
|
|
2015
|
+
}
|
|
2016
|
+
/** Upgrades the profile or domain schemas. File must be closed before this call and is always left closed. */
|
|
2017
|
+
static async doUpgrade(briefcase, upgradeOptions, description) {
|
|
2018
|
+
const nativeDb = this.openDgnDb({ path: briefcase.fileName }, core_bentley_1.OpenMode.ReadWrite, upgradeOptions); // performs the upgrade
|
|
2019
|
+
const wasChanges = nativeDb.hasPendingTxns();
|
|
2020
|
+
nativeDb.closeIModel();
|
|
2021
|
+
if (wasChanges)
|
|
2022
|
+
await withBriefcaseDb(briefcase, async (db) => db.pushChanges({ ...briefcase, description, retainLocks: true }));
|
|
2023
|
+
}
|
|
2024
|
+
/** Upgrades the schemas in the iModel based on the current version of the software. Follows a sequence of operations -
|
|
2025
|
+
* * Acquires a schema lock to prevent other users from making any other changes while upgrade is happening
|
|
2026
|
+
* * Updates the local briefcase with the schema changes.
|
|
2027
|
+
* * Pushes the resulting changeset(s) to iModelHub.
|
|
2028
|
+
* Note that the upgrade requires that the local briefcase be closed, and may result in one or two change sets depending on whether both
|
|
2029
|
+
* profile and domain schemas need to get upgraded.
|
|
2030
|
+
* @see ($docs/learning/backend/IModelDb.md#upgrading-schemas-in-an-imodel)
|
|
2031
|
+
*/
|
|
2032
|
+
static async upgradeSchemas(briefcase) {
|
|
2033
|
+
// upgrading schemas involves closing and reopening the file repeatedly. That's because the process of upgrading
|
|
2034
|
+
// happens on a file open. We have to open-and-close the file at *each* of these steps:
|
|
2035
|
+
// - acquire schema lock
|
|
2036
|
+
// - upgrade profile
|
|
2037
|
+
// - push changes
|
|
2038
|
+
// - upgrade domain
|
|
2039
|
+
// - push changes
|
|
2040
|
+
// - release schema lock
|
|
2041
|
+
// good thing computers are fast. Fortunately upgrading should be rare (and the push time will dominate anyway.) Don't try to optimize any of this away.
|
|
2042
|
+
await withBriefcaseDb(briefcase, async (db) => db.acquireSchemaLock()); // may not really acquire lock if iModel uses "noLocks" mode.
|
|
2043
|
+
try {
|
|
2044
|
+
await this.doUpgrade(briefcase, { profile: core_common_1.ProfileOptions.Upgrade, schemaLockHeld: true }, "Upgraded profile");
|
|
2045
|
+
await this.doUpgrade(briefcase, { domain: core_common_1.DomainOptions.Upgrade, schemaLockHeld: true }, "Upgraded domain schemas");
|
|
2046
|
+
}
|
|
2047
|
+
finally {
|
|
2048
|
+
await withBriefcaseDb(briefcase, async (db) => db.locks.releaseAllLocks());
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
/** Open a briefcase file and return a new BriefcaseDb to interact with it.
|
|
2052
|
+
* @param args parameters that specify the file name, and options for opening the briefcase file
|
|
2053
|
+
*/
|
|
2054
|
+
static async open(args) {
|
|
2055
|
+
var _a;
|
|
2056
|
+
this.onOpen.raiseEvent(args);
|
|
2057
|
+
const file = { path: args.fileName, key: args.key };
|
|
2058
|
+
const openMode = args.readonly ? core_bentley_1.OpenMode.Readonly : core_bentley_1.OpenMode.ReadWrite;
|
|
2059
|
+
const nativeDb = this.openDgnDb(file, openMode, undefined, args);
|
|
2060
|
+
const briefcaseDb = new BriefcaseDb({ nativeDb, key: (_a = file.key) !== null && _a !== void 0 ? _a : core_bentley_1.Guid.createValue(), openMode, briefcaseId: nativeDb.getBriefcaseId() });
|
|
2061
|
+
if (openMode === core_bentley_1.OpenMode.ReadWrite && CodeService_1.CodeService.createForIModel) {
|
|
2062
|
+
try {
|
|
2063
|
+
const codeService = CodeService_1.CodeService.createForIModel(briefcaseDb);
|
|
2064
|
+
briefcaseDb._codeService = codeService;
|
|
2065
|
+
this.onCodeServiceCreated.raiseEvent(codeService);
|
|
2066
|
+
}
|
|
2067
|
+
catch (e) {
|
|
2068
|
+
if (e.errorId !== "NoCodeIndex") // no code index means iModel isn't enforcing codes.
|
|
2069
|
+
throw e;
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
BriefcaseManager_1.BriefcaseManager.logUsage(briefcaseDb);
|
|
2073
|
+
this.onOpened.raiseEvent(briefcaseDb, args);
|
|
2074
|
+
return briefcaseDb;
|
|
2075
|
+
}
|
|
2076
|
+
closeAndReopen(openMode) {
|
|
2077
|
+
const fileName = this.pathName;
|
|
2078
|
+
this.nativeDb.closeIModel();
|
|
2079
|
+
this.nativeDb.openIModel(fileName, openMode);
|
|
2080
|
+
}
|
|
2081
|
+
/** Pull and apply changesets from iModelHub */
|
|
2082
|
+
async pullChanges(arg) {
|
|
2083
|
+
if (this.isReadonly) // we allow pulling changes into a briefcase that is readonly - close and reopen it writeable
|
|
2084
|
+
this.closeAndReopen(core_bentley_1.OpenMode.ReadWrite);
|
|
2085
|
+
try {
|
|
2086
|
+
await BriefcaseManager_1.BriefcaseManager.pullAndApplyChangesets(this, arg !== null && arg !== void 0 ? arg : {});
|
|
2087
|
+
this.initializeIModelDb();
|
|
2088
|
+
}
|
|
2089
|
+
finally {
|
|
2090
|
+
if (this.isReadonly) // if the briefcase was opened readonly - close and reopen it readonly
|
|
2091
|
+
this.closeAndReopen(core_bentley_1.OpenMode.Readonly);
|
|
2092
|
+
}
|
|
2093
|
+
IpcHost_1.IpcHost.notifyTxns(this, "notifyPulledChanges", this.changeset);
|
|
2094
|
+
}
|
|
2095
|
+
/** Push changes to iModelHub. */
|
|
2096
|
+
async pushChanges(arg) {
|
|
2097
|
+
if (this.briefcaseId === core_common_1.BriefcaseIdValue.Unassigned)
|
|
2098
|
+
return;
|
|
2099
|
+
if (this.nativeDb.hasUnsavedChanges())
|
|
2100
|
+
throw new core_common_1.IModelError(core_bentley_1.ChangeSetStatus.HasUncommittedChanges, "Cannot push with unsaved changes");
|
|
2101
|
+
if (!this.nativeDb.hasPendingTxns())
|
|
2102
|
+
return; // nothing to push
|
|
2103
|
+
await BriefcaseManager_1.BriefcaseManager.pullMergePush(this, arg);
|
|
2104
|
+
this.initializeIModelDb();
|
|
2105
|
+
const changeset = this.changeset;
|
|
2106
|
+
IpcHost_1.IpcHost.notifyTxns(this, "notifyPushedChanges", changeset);
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
exports.BriefcaseDb = BriefcaseDb;
|
|
2110
|
+
/**
|
|
2111
|
+
* Event raised just before a BriefcaseDb is opened. Supplies the arguments that will be used to open the BriefcaseDb.
|
|
2112
|
+
* Throw an exception to stop the open.
|
|
2113
|
+
*
|
|
2114
|
+
* **Example:**
|
|
2115
|
+
* ``` ts
|
|
2116
|
+
* [[include:BriefcaseDb.onOpen]]
|
|
2117
|
+
* ```
|
|
2118
|
+
*/
|
|
2119
|
+
BriefcaseDb.onOpen = new core_bentley_1.BeEvent();
|
|
2120
|
+
/**
|
|
2121
|
+
* Event raised just after a BriefcaseDb is opened. Supplies the newly opened BriefcaseDb and the arguments that were used to open it.
|
|
2122
|
+
*
|
|
2123
|
+
* **Example:**
|
|
2124
|
+
* ``` ts
|
|
2125
|
+
* [[include:BriefcaseDb.onOpened]]
|
|
2126
|
+
* ```
|
|
2127
|
+
*/
|
|
2128
|
+
BriefcaseDb.onOpened = new core_bentley_1.BeEvent();
|
|
2129
|
+
/** @alpha */
|
|
2130
|
+
BriefcaseDb.onCodeServiceCreated = new core_bentley_1.BeEvent();
|
|
2131
|
+
/** Used to reattach Daemon from a user's accessToken for V2 checkpoints.
|
|
2132
|
+
* @note Reattach only happens if the previous access token either has expired or is about to expire within an application-supplied safety duration.
|
|
2133
|
+
*/
|
|
2134
|
+
class RefreshV2CheckpointSas {
|
|
2135
|
+
constructor(sasToken, safetySeconds) {
|
|
2136
|
+
/** the time at which the current token should be refreshed (its expiry minus safetySeconds) */
|
|
2137
|
+
this._timestamp = 0;
|
|
2138
|
+
this._safetySeconds = safetySeconds !== null && safetySeconds !== void 0 ? safetySeconds : 60 * 60; // default to 1 hour
|
|
2139
|
+
this.setTimestamp(sasToken);
|
|
2140
|
+
}
|
|
2141
|
+
async performRefresh(accessToken, iModel) {
|
|
2142
|
+
this._timestamp = 0; // everyone needs to wait until token is valid
|
|
2143
|
+
// we're going to request that the checkpoint manager use this user's accessToken to obtain a new access token for this checkpoint's storage account.
|
|
2144
|
+
core_bentley_1.Logger.logInfo(BackendLoggerCategory_1.BackendLoggerCategory.Authorization, "attempting to refresh sasToken for checkpoint");
|
|
2145
|
+
try {
|
|
2146
|
+
// this exchanges the supplied user accessToken for an expiring blob-store token to read the checkpoint.
|
|
2147
|
+
const container = iModel.nativeDb.cloudContainer;
|
|
2148
|
+
if (!container)
|
|
2149
|
+
throw new Error("checkpoint is not from a cloud container");
|
|
2150
|
+
(0, core_bentley_1.assert)(undefined !== iModel.iTwinId);
|
|
2151
|
+
const props = await IModelHost_1.IModelHost.hubAccess.queryV2Checkpoint({ accessToken, iTwinId: iModel.iTwinId, iModelId: iModel.iModelId, changeset: iModel.changeset });
|
|
2152
|
+
if (!props)
|
|
2153
|
+
throw new Error("can't reset checkpoint sas token");
|
|
2154
|
+
container.accessToken = props.sasToken;
|
|
2155
|
+
this.setTimestamp(props.sasToken);
|
|
2156
|
+
core_bentley_1.Logger.logInfo(BackendLoggerCategory_1.BackendLoggerCategory.Authorization, "refreshed checkpoint sasToken successfully");
|
|
2157
|
+
}
|
|
2158
|
+
finally {
|
|
2159
|
+
this._promise = undefined;
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
setTimestamp(sasToken) {
|
|
2163
|
+
const exp = new URLSearchParams(sasToken).get("se");
|
|
2164
|
+
const sasTokenExpiry = exp ? Date.parse(exp) : 0;
|
|
2165
|
+
this._timestamp = sasTokenExpiry - (this._safetySeconds * 1000);
|
|
2166
|
+
if (this._timestamp < Date.now())
|
|
2167
|
+
core_bentley_1.Logger.logError(BackendLoggerCategory_1.BackendLoggerCategory.Authorization, "attached with timestamp that expires before safety interval");
|
|
2168
|
+
}
|
|
2169
|
+
async refreshSas(accessToken, iModel) {
|
|
2170
|
+
if (this._timestamp > Date.now())
|
|
2171
|
+
return; // current token is fine
|
|
2172
|
+
if (undefined === this._promise) // has reattach already begun?
|
|
2173
|
+
this._promise = this.performRefresh(accessToken, iModel); // no, start it
|
|
2174
|
+
return this._promise;
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
/** A *snapshot* iModel database file that is used for archival and data transfer purposes.
|
|
2178
|
+
* @see [Snapshot iModels]($docs/learning/backend/AccessingIModels.md#snapshot-imodels)
|
|
2179
|
+
* @see [About IModelDb]($docs/learning/backend/IModelDb.md)
|
|
2180
|
+
* @public
|
|
2181
|
+
*/
|
|
2182
|
+
class SnapshotDb extends IModelDb {
|
|
2183
|
+
constructor(nativeDb, key) {
|
|
2184
|
+
super({ nativeDb, key, changeset: nativeDb.getCurrentChangeset() });
|
|
2185
|
+
this._openMode = nativeDb.isReadonly() ? core_bentley_1.OpenMode.Readonly : core_bentley_1.OpenMode.ReadWrite;
|
|
2186
|
+
}
|
|
2187
|
+
get isSnapshot() { return true; }
|
|
2188
|
+
static findByKey(key) {
|
|
2189
|
+
return super.findByKey(key);
|
|
2190
|
+
}
|
|
2191
|
+
static tryFindByKey(key) {
|
|
2192
|
+
const db = super.tryFindByKey(key);
|
|
2193
|
+
return (db === null || db === void 0 ? void 0 : db.isSnapshotDb()) ? db : undefined;
|
|
2194
|
+
}
|
|
2195
|
+
/** Create an *empty* local [Snapshot]($docs/learning/backend/AccessingIModels.md#snapshot-imodels) iModel file.
|
|
2196
|
+
* Snapshots are not synchronized with iModelHub, so do not have a change timeline.
|
|
2197
|
+
* > Note: A *snapshot* cannot be modified after [[close]] is called.
|
|
2198
|
+
* @param filePath The file that will contain the new iModel *snapshot*
|
|
2199
|
+
* @param options The parameters that define the new iModel *snapshot*
|
|
2200
|
+
* @returns A writeable SnapshotDb
|
|
2201
|
+
* @see [Snapshot iModels]($docs/learning/backend/AccessingIModels.md#snapshot-imodels)
|
|
2202
|
+
*/
|
|
2203
|
+
static createEmpty(filePath, options) {
|
|
2204
|
+
const nativeDb = new IModelHost_1.IModelHost.platform.DgnDb();
|
|
2205
|
+
nativeDb.createIModel(filePath, options);
|
|
2206
|
+
nativeDb.resetBriefcaseId(core_common_1.BriefcaseIdValue.Unassigned);
|
|
2207
|
+
const snapshotDb = new SnapshotDb(nativeDb, core_bentley_1.Guid.createValue());
|
|
2208
|
+
if (options.createClassViews)
|
|
2209
|
+
snapshotDb._createClassViewsOnClose = true; // save flag that will be checked when close() is called
|
|
2210
|
+
return snapshotDb;
|
|
2211
|
+
}
|
|
2212
|
+
/** Create a local [Snapshot]($docs/learning/backend/AccessingIModels.md#snapshot-imodels) iModel file, using this iModel as a *seed* or starting point.
|
|
2213
|
+
* Snapshots are not synchronized with iModelHub, so do not have a change timeline.
|
|
2214
|
+
* > Note: A *snapshot* cannot be modified after [[close]] is called.
|
|
2215
|
+
* @param iModelDb The snapshot will be initialized from the current contents of this iModelDb
|
|
2216
|
+
* @param snapshotFile The file that will contain the new iModel *snapshot*
|
|
2217
|
+
* @param options Optional properties that determine how the snapshot iModel is created.
|
|
2218
|
+
* @returns A writeable SnapshotDb
|
|
2219
|
+
* @see [Snapshot iModels]($docs/learning/backend/AccessingIModels.md#snapshot-imodels)
|
|
2220
|
+
*/
|
|
2221
|
+
static createFrom(iModelDb, snapshotFile, options) {
|
|
2222
|
+
iModelDb.performCheckpoint();
|
|
2223
|
+
IModelJsFs_1.IModelJsFs.copySync(iModelDb.pathName, snapshotFile);
|
|
2224
|
+
const nativeDb = new IModelHost_1.IModelHost.platform.DgnDb();
|
|
2225
|
+
nativeDb.openIModel(snapshotFile, core_bentley_1.OpenMode.ReadWrite, undefined, options);
|
|
2226
|
+
nativeDb.vacuum();
|
|
2227
|
+
// Replace iModelId if seedFile is a snapshot, preserve iModelId if seedFile is an iModelHub-managed briefcase
|
|
2228
|
+
if (!BriefcaseManager_1.BriefcaseManager.isValidBriefcaseId(nativeDb.getBriefcaseId()))
|
|
2229
|
+
nativeDb.setIModelId(core_bentley_1.Guid.createValue());
|
|
2230
|
+
nativeDb.deleteLocalValue(BriefcaseLocalValue.StandaloneEdit);
|
|
2231
|
+
nativeDb.saveChanges();
|
|
2232
|
+
nativeDb.deleteAllTxns();
|
|
2233
|
+
nativeDb.resetBriefcaseId(core_common_1.BriefcaseIdValue.Unassigned);
|
|
2234
|
+
const snapshotDb = new SnapshotDb(nativeDb, core_bentley_1.Guid.createValue());
|
|
2235
|
+
if (options === null || options === void 0 ? void 0 : options.createClassViews)
|
|
2236
|
+
snapshotDb._createClassViewsOnClose = true; // save flag that will be checked when close() is called
|
|
2237
|
+
return snapshotDb;
|
|
2238
|
+
}
|
|
2239
|
+
/** open this SnapshotDb read/write, strictly to apply incoming changesets. Used for creating new checkpoints.
|
|
2240
|
+
* @internal
|
|
2241
|
+
*/
|
|
2242
|
+
static openForApplyChangesets(path, props) {
|
|
2243
|
+
const file = { path, key: props === null || props === void 0 ? void 0 : props.key };
|
|
2244
|
+
const nativeDb = this.openDgnDb(file, core_bentley_1.OpenMode.ReadWrite, undefined, props);
|
|
2245
|
+
(0, core_bentley_1.assert)(undefined !== file.key);
|
|
2246
|
+
return new SnapshotDb(nativeDb, file.key);
|
|
2247
|
+
}
|
|
2248
|
+
/** Open a read-only iModel *snapshot*.
|
|
2249
|
+
* @param path the full path of the snapshot iModel file to open.
|
|
2250
|
+
* @param props options for opening snapshot
|
|
2251
|
+
* @see [[close]]
|
|
2252
|
+
* @throws [[IModelError]] If the file is not found or is not a valid *snapshot*.
|
|
2253
|
+
*/
|
|
2254
|
+
static openFile(path, opts) {
|
|
2255
|
+
const file = { path, key: opts === null || opts === void 0 ? void 0 : opts.key };
|
|
2256
|
+
const nativeDb = this.openDgnDb(file, core_bentley_1.OpenMode.Readonly, undefined, opts);
|
|
2257
|
+
(0, core_bentley_1.assert)(undefined !== file.key);
|
|
2258
|
+
return new SnapshotDb(nativeDb, file.key);
|
|
2259
|
+
}
|
|
2260
|
+
/** Open a previously downloaded V1 checkpoint file.
|
|
2261
|
+
* @note The key is generated by this call is predictable and is formed from the IModelId and ChangeSetId.
|
|
2262
|
+
* This is so every backend working on the same checkpoint will use the same key, to permit multiple backends
|
|
2263
|
+
* servicing the same checkpoint.
|
|
2264
|
+
* @internal
|
|
2265
|
+
*/
|
|
2266
|
+
static openCheckpointV1(fileName, checkpoint) {
|
|
2267
|
+
const snapshot = this.openFile(fileName, { key: CheckpointManager_1.CheckpointManager.getKey(checkpoint) });
|
|
2268
|
+
snapshot._iTwinId = checkpoint.iTwinId;
|
|
2269
|
+
return snapshot;
|
|
2270
|
+
}
|
|
2271
|
+
/** Open a V2 *checkpoint*, a special form of snapshot iModel that represents a read-only snapshot of an iModel from iModelHub at a particular point in time.
|
|
2272
|
+
* > Note: The checkpoint daemon must already be running and a checkpoint must already exist in iModelHub's storage *before* this function is called.
|
|
2273
|
+
* @param checkpoint The checkpoint to open
|
|
2274
|
+
* @note The key generated by this call is predictable and is formed from the IModelId and ChangeSetId.
|
|
2275
|
+
* This is so every backend working on the same checkpoint will use the same key, to permit multiple backends
|
|
2276
|
+
* servicing the same checkpoint.
|
|
2277
|
+
* @throws [[IModelError]] If the checkpoint is not found in iModelHub or the checkpoint daemon is not supported in the current environment.
|
|
2278
|
+
* @internal
|
|
2279
|
+
*/
|
|
2280
|
+
static async openCheckpointV2(checkpoint) {
|
|
2281
|
+
const { dbName, container } = await CheckpointManager_1.V2CheckpointManager.attach(checkpoint);
|
|
2282
|
+
const key = CheckpointManager_1.CheckpointManager.getKey(checkpoint);
|
|
2283
|
+
const tempFileBase = (0, path_1.join)(IModelHost_1.IModelHost.cacheDir, `${checkpoint.iModelId}\$${checkpoint.changeset.id}`); // temp files for this checkpoint should go in the cacheDir.
|
|
2284
|
+
const snapshot = SnapshotDb.openFile(dbName, { key, tempFileBase, container });
|
|
2285
|
+
snapshot._iTwinId = checkpoint.iTwinId;
|
|
2286
|
+
try {
|
|
2287
|
+
CheckpointManager_1.CheckpointManager.validateCheckpointGuids(checkpoint, snapshot);
|
|
2288
|
+
}
|
|
2289
|
+
catch (err) {
|
|
2290
|
+
snapshot.close();
|
|
2291
|
+
throw err;
|
|
2292
|
+
}
|
|
2293
|
+
snapshot._refreshSas = new RefreshV2CheckpointSas(container.accessToken, checkpoint.reattachSafetySeconds);
|
|
2294
|
+
return snapshot;
|
|
2295
|
+
}
|
|
2296
|
+
/** Used to refresh the container sasToken using the current user's accessToken
|
|
2297
|
+
* @internal
|
|
2298
|
+
*/
|
|
2299
|
+
async refreshContainerSas(userAccessToken) {
|
|
2300
|
+
var _a;
|
|
2301
|
+
return (_a = this._refreshSas) === null || _a === void 0 ? void 0 : _a.refreshSas(userAccessToken, this);
|
|
2302
|
+
}
|
|
2303
|
+
/** @internal */
|
|
2304
|
+
beforeClose() {
|
|
2305
|
+
super.beforeClose();
|
|
2306
|
+
if (this._createClassViewsOnClose) { // check for flag set during create
|
|
2307
|
+
if (core_bentley_1.BentleyStatus.SUCCESS !== this.nativeDb.createClassViewsInDb()) {
|
|
2308
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.SQLiteError, "Error creating class views");
|
|
2309
|
+
}
|
|
2310
|
+
else {
|
|
2311
|
+
this.saveChanges();
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
exports.SnapshotDb = SnapshotDb;
|
|
2317
|
+
/**
|
|
2318
|
+
* Standalone iModels are read/write files that are not associated with an iTwin or managed by iModelHub.
|
|
2319
|
+
* They are relevant only for testing, or for small-scale single-user scenarios.
|
|
2320
|
+
* Standalone iModels are designed such that the API for Standalone iModels and Briefcase
|
|
2321
|
+
* iModels (those synchronized with iModelHub) are as similar and consistent as possible.
|
|
2322
|
+
* This leads to a straightforward process where the a user starts with StandaloneDb and can
|
|
2323
|
+
* optionally choose to upgrade to an iTwin.
|
|
2324
|
+
*
|
|
2325
|
+
* Some additional details. Standalone iModels:
|
|
2326
|
+
* - always have [Guid.empty]($bentley) for their iTwinId (they are "unassociated" files)
|
|
2327
|
+
* - always have BriefcaseId === [BriefcaseIdValue.Unassigned]($common)
|
|
2328
|
+
* - are connected to the frontend via [BriefcaseConnection.openStandalone]($frontend)
|
|
2329
|
+
* - may be opened without supplying any user credentials
|
|
2330
|
+
* - may be opened read/write
|
|
2331
|
+
* - cannot apply a changeset to nor generate a changesets (since there is no timeline from which to get/push changesets)
|
|
2332
|
+
* @public
|
|
2333
|
+
*/
|
|
2334
|
+
class StandaloneDb extends BriefcaseDb {
|
|
2335
|
+
get isStandalone() { return true; }
|
|
2336
|
+
get useLockServer() { return false; } // standalone iModels have no lock server
|
|
2337
|
+
static findByKey(key) {
|
|
2338
|
+
return super.findByKey(key);
|
|
2339
|
+
}
|
|
2340
|
+
static tryFindByKey(key) {
|
|
2341
|
+
const db = super.tryFindByKey(key);
|
|
2342
|
+
return (db === null || db === void 0 ? void 0 : db.isStandaloneDb()) ? db : undefined;
|
|
2343
|
+
}
|
|
2344
|
+
/** Create an *empty* standalone iModel.
|
|
2345
|
+
* @param filePath The file path for the iModel
|
|
2346
|
+
* @param args The parameters that define the new iModel
|
|
2347
|
+
*/
|
|
2348
|
+
static createEmpty(filePath, args) {
|
|
2349
|
+
const nativeDb = new IModelHost_1.IModelHost.platform.DgnDb();
|
|
2350
|
+
nativeDb.createIModel(filePath, args);
|
|
2351
|
+
nativeDb.saveLocalValue(BriefcaseLocalValue.StandaloneEdit, args.allowEdit);
|
|
2352
|
+
nativeDb.setITwinId(core_bentley_1.Guid.empty);
|
|
2353
|
+
nativeDb.resetBriefcaseId(core_common_1.BriefcaseIdValue.Unassigned);
|
|
2354
|
+
nativeDb.saveChanges();
|
|
2355
|
+
return new StandaloneDb({ nativeDb, key: core_bentley_1.Guid.createValue(), briefcaseId: core_common_1.BriefcaseIdValue.Unassigned, openMode: core_bentley_1.OpenMode.ReadWrite });
|
|
2356
|
+
}
|
|
2357
|
+
/**
|
|
2358
|
+
* Upgrades the schemas in the standalone iModel file.
|
|
2359
|
+
* Note that the upgrade requires that the file be closed, and will leave it back in the closed state.
|
|
2360
|
+
* @param filePath Full path name of the standalone iModel file.
|
|
2361
|
+
* @see ($docs/learning/backend/IModelDb.md#upgrading-schemas-in-an-imodel)
|
|
2362
|
+
* @see [[StandaloneDb.validateSchemas]]
|
|
2363
|
+
*/
|
|
2364
|
+
static upgradeStandaloneSchemas(filePath) {
|
|
2365
|
+
let nativeDb = this.openDgnDb({ path: filePath }, core_bentley_1.OpenMode.ReadWrite, { profile: core_common_1.ProfileOptions.Upgrade, schemaLockHeld: true });
|
|
2366
|
+
nativeDb.closeIModel();
|
|
2367
|
+
nativeDb = this.openDgnDb({ path: filePath }, core_bentley_1.OpenMode.ReadWrite, { domain: core_common_1.DomainOptions.Upgrade, schemaLockHeld: true });
|
|
2368
|
+
nativeDb.closeIModel();
|
|
2369
|
+
}
|
|
2370
|
+
/** Open a standalone iModel file.
|
|
2371
|
+
* @param filePath The path of the standalone iModel file.
|
|
2372
|
+
* @param openMode Optional open mode for the standalone iModel. The default is read/write.
|
|
2373
|
+
* @throws [[IModelError]] if the file is not a standalone iModel.
|
|
2374
|
+
* @see [BriefcaseConnection.openStandalone]($frontend) to open a StandaloneDb from the frontend
|
|
2375
|
+
*/
|
|
2376
|
+
static openFile(filePath, openMode = core_bentley_1.OpenMode.ReadWrite, options) {
|
|
2377
|
+
const file = { path: filePath, key: options === null || options === void 0 ? void 0 : options.key };
|
|
2378
|
+
const nativeDb = this.openDgnDb(file, openMode, undefined, options);
|
|
2379
|
+
try {
|
|
2380
|
+
const iTwinId = nativeDb.getITwinId();
|
|
2381
|
+
if (iTwinId !== core_bentley_1.Guid.empty) // a "standalone" iModel means it is not associated with an iTwin
|
|
2382
|
+
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.WrongIModel, `${filePath} is not a Standalone iModel. iTwinId=${iTwinId}`);
|
|
2383
|
+
(0, core_bentley_1.assert)(undefined !== file.key);
|
|
2384
|
+
return new StandaloneDb({ nativeDb, key: file.key, openMode, briefcaseId: core_common_1.BriefcaseIdValue.Unassigned });
|
|
2385
|
+
}
|
|
2386
|
+
catch (error) {
|
|
2387
|
+
nativeDb.closeIModel();
|
|
2388
|
+
throw error;
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
exports.StandaloneDb = StandaloneDb;
|
|
2393
2393
|
//# sourceMappingURL=IModelDb.js.map
|