@itwin/core-backend 3.3.0-dev.8 → 3.3.0-dev.82
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 +26 -1
- package/lib/cjs/BackendHubAccess.d.ts +32 -15
- package/lib/cjs/BackendHubAccess.d.ts.map +1 -1
- package/lib/cjs/BackendHubAccess.js.map +1 -1
- package/lib/cjs/BisCoreSchema.d.ts +1 -1
- package/lib/cjs/BisCoreSchema.js +1 -1
- package/lib/cjs/BisCoreSchema.js.map +1 -1
- package/lib/cjs/BriefcaseManager.d.ts +10 -11
- package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
- package/lib/cjs/BriefcaseManager.js +10 -11
- package/lib/cjs/BriefcaseManager.js.map +1 -1
- package/lib/cjs/CheckpointManager.d.ts +28 -6
- package/lib/cjs/CheckpointManager.d.ts.map +1 -1
- package/lib/cjs/CheckpointManager.js +117 -22
- package/lib/cjs/CheckpointManager.js.map +1 -1
- package/lib/cjs/ClassRegistry.js +6 -6
- package/lib/cjs/ClassRegistry.js.map +1 -1
- package/lib/cjs/CodeSpecs.d.ts +1 -1
- package/lib/cjs/CodeSpecs.js +1 -1
- package/lib/cjs/CodeSpecs.js.map +1 -1
- package/lib/cjs/DisplayStyle.d.ts +2 -2
- package/lib/cjs/DisplayStyle.d.ts.map +1 -1
- package/lib/cjs/DisplayStyle.js +14 -17
- package/lib/cjs/DisplayStyle.js.map +1 -1
- package/lib/cjs/Element.d.ts +36 -25
- package/lib/cjs/Element.d.ts.map +1 -1
- package/lib/cjs/Element.js +54 -39
- package/lib/cjs/Element.js.map +1 -1
- package/lib/cjs/GeoCoordConfig.d.ts +14 -0
- package/lib/cjs/GeoCoordConfig.d.ts.map +1 -0
- package/lib/cjs/GeoCoordConfig.js +74 -0
- package/lib/cjs/GeoCoordConfig.js.map +1 -0
- package/lib/cjs/HubMock.d.ts +94 -0
- package/lib/cjs/HubMock.d.ts.map +1 -0
- package/lib/cjs/HubMock.js +191 -0
- package/lib/cjs/HubMock.js.map +1 -0
- package/lib/cjs/IModelDb.d.ts +26 -15
- package/lib/cjs/IModelDb.d.ts.map +1 -1
- package/lib/cjs/IModelDb.js +44 -35
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/cjs/IModelHost.d.ts +75 -33
- package/lib/cjs/IModelHost.d.ts.map +1 -1
- package/lib/cjs/IModelHost.js +66 -80
- package/lib/cjs/IModelHost.js.map +1 -1
- package/lib/cjs/IpcHost.d.ts +2 -2
- package/lib/cjs/IpcHost.d.ts.map +1 -1
- package/lib/cjs/IpcHost.js +1 -1
- package/lib/cjs/IpcHost.js.map +1 -1
- package/lib/cjs/LocalHub.d.ts +163 -0
- package/lib/cjs/LocalHub.d.ts.map +1 -0
- package/lib/cjs/LocalHub.js +588 -0
- package/lib/cjs/LocalHub.js.map +1 -0
- package/lib/cjs/LocalhostIpcHost.d.ts +2 -2
- package/lib/cjs/LocalhostIpcHost.d.ts.map +1 -1
- package/lib/cjs/LocalhostIpcHost.js +16 -0
- package/lib/cjs/LocalhostIpcHost.js.map +1 -1
- package/lib/cjs/RpcBackend.d.ts +3 -1
- package/lib/cjs/RpcBackend.d.ts.map +1 -1
- package/lib/cjs/RpcBackend.js +27 -3
- package/lib/cjs/RpcBackend.js.map +1 -1
- package/lib/cjs/SQLiteDb.d.ts +138 -30
- package/lib/cjs/SQLiteDb.d.ts.map +1 -1
- package/lib/cjs/SQLiteDb.js +110 -38
- package/lib/cjs/SQLiteDb.js.map +1 -1
- package/lib/cjs/SqliteStatement.d.ts +8 -0
- package/lib/cjs/SqliteStatement.d.ts.map +1 -1
- package/lib/cjs/SqliteStatement.js +10 -0
- package/lib/cjs/SqliteStatement.js.map +1 -1
- package/lib/cjs/ViewDefinition.d.ts +6 -6
- package/lib/cjs/ViewDefinition.d.ts.map +1 -1
- package/lib/cjs/ViewDefinition.js +20 -20
- package/lib/cjs/ViewDefinition.js.map +1 -1
- package/lib/cjs/assets/Settings/Schemas/Cloud.Schema.json +68 -0
- package/lib/cjs/assets/Settings/Schemas/Gcs.schema.json +22 -0
- package/lib/cjs/assets/Settings/Schemas/Workspace.Schema.json +53 -0
- package/lib/cjs/assets/Settings/backend.setting.json5 +139 -0
- package/lib/cjs/core-backend.d.ts +9 -7
- package/lib/cjs/core-backend.d.ts.map +1 -1
- package/lib/cjs/core-backend.js +9 -7
- package/lib/cjs/core-backend.js.map +1 -1
- package/lib/cjs/rpc-impl/RpcBriefcaseUtility.d.ts +1 -1
- package/lib/cjs/rpc-impl/RpcBriefcaseUtility.d.ts.map +1 -1
- package/lib/cjs/rpc-impl/RpcBriefcaseUtility.js +8 -12
- package/lib/cjs/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
- package/lib/cjs/workspace/Settings.d.ts +8 -3
- package/lib/cjs/workspace/Settings.d.ts.map +1 -1
- package/lib/cjs/workspace/Settings.js +9 -0
- package/lib/cjs/workspace/Settings.js.map +1 -1
- package/lib/cjs/workspace/SettingsSchemas.d.ts +76 -0
- package/lib/cjs/workspace/SettingsSchemas.d.ts.map +1 -0
- package/lib/cjs/workspace/SettingsSchemas.js +192 -0
- package/lib/cjs/workspace/SettingsSchemas.js.map +1 -0
- package/lib/cjs/workspace/Workspace.d.ts +240 -144
- package/lib/cjs/workspace/Workspace.d.ts.map +1 -1
- package/lib/cjs/workspace/Workspace.js +258 -95
- package/lib/cjs/workspace/Workspace.js.map +1 -1
- package/package.json +23 -18
- package/lib/cjs/assets/Settings.schema.json +0 -25
- package/lib/cjs/workspace/SettingsSpecRegistry.d.ts +0 -68
- package/lib/cjs/workspace/SettingsSpecRegistry.d.ts.map +0 -1
- package/lib/cjs/workspace/SettingsSpecRegistry.js +0 -122
- package/lib/cjs/workspace/SettingsSpecRegistry.js.map +0 -1
|
@@ -7,89 +7,174 @@
|
|
|
7
7
|
* @module Workspace
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.EditableWorkspaceDb = exports.ITwinWorkspaceDb = exports.ITwinWorkspaceContainer = exports.ITwinWorkspace = void 0;
|
|
10
|
+
exports.EditableWorkspaceDb = exports.ITwinWorkspaceDb = exports.ITwinWorkspaceContainer = exports.ITwinWorkspace = exports.WorkspaceSetting = void 0;
|
|
11
11
|
const crypto_1 = require("crypto");
|
|
12
12
|
const fs = require("fs-extra");
|
|
13
13
|
const path_1 = require("path");
|
|
14
|
+
const semver = require("semver");
|
|
14
15
|
const imodeljs_native_1 = require("@bentley/imodeljs-native");
|
|
15
16
|
const core_bentley_1 = require("@itwin/core-bentley");
|
|
16
17
|
const core_common_1 = require("@itwin/core-common");
|
|
18
|
+
const IModelHost_1 = require("../IModelHost");
|
|
17
19
|
const IModelJsFs_1 = require("../IModelJsFs");
|
|
18
20
|
const SQLiteDb_1 = require("../SQLiteDb");
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
const Settings_1 = require("./Settings");
|
|
22
|
+
const SettingsSchemas_1 = require("./SettingsSchemas");
|
|
23
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
24
|
+
// cspell:ignore rowid primarykey
|
|
25
|
+
/** The Settings used by Workspace api
|
|
21
26
|
* @beta
|
|
22
27
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
exports.WorkspaceSetting = {
|
|
29
|
+
Accounts: "cloud/accounts",
|
|
30
|
+
Containers: "cloud/containers",
|
|
31
|
+
Databases: "workspace/databases",
|
|
32
|
+
};
|
|
28
33
|
/** @internal */
|
|
29
34
|
class ITwinWorkspace {
|
|
30
35
|
constructor(settings, opts) {
|
|
31
36
|
var _a;
|
|
32
37
|
this._containers = new Map();
|
|
33
38
|
this.settings = settings;
|
|
34
|
-
this.containerDir = (_a = opts === null || opts === void 0 ? void 0 : opts.containerDir) !== null && _a !== void 0 ? _a : (0, path_1.join)(
|
|
39
|
+
this.containerDir = (_a = opts === null || opts === void 0 ? void 0 : opts.containerDir) !== null && _a !== void 0 ? _a : (0, path_1.join)(IModelHost_1.IModelHost.cacheDir, "Workspace");
|
|
40
|
+
this._cloudCacheProps = opts === null || opts === void 0 ? void 0 : opts.cloudCacheProps;
|
|
41
|
+
let settingsFiles = opts === null || opts === void 0 ? void 0 : opts.settingsFiles;
|
|
42
|
+
if (settingsFiles) {
|
|
43
|
+
if (typeof settingsFiles === "string")
|
|
44
|
+
settingsFiles = [settingsFiles];
|
|
45
|
+
settingsFiles.forEach((file) => settings.addFile(file, Settings_1.SettingsPriority.application));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
get cloudCache() {
|
|
49
|
+
var _a, _b, _c, _d, _e, _f;
|
|
50
|
+
if (undefined === this._cloudCache) {
|
|
51
|
+
const cacheProps = {
|
|
52
|
+
...this._cloudCacheProps,
|
|
53
|
+
rootDir: (_b = (_a = this._cloudCacheProps) === null || _a === void 0 ? void 0 : _a.rootDir) !== null && _b !== void 0 ? _b : (0, path_1.join)(this.containerDir, "cloud"),
|
|
54
|
+
cacheSize: (_d = (_c = this._cloudCacheProps) === null || _c === void 0 ? void 0 : _c.cacheSize) !== null && _d !== void 0 ? _d : "20G",
|
|
55
|
+
name: (_f = (_e = this._cloudCacheProps) === null || _e === void 0 ? void 0 : _e.name) !== null && _f !== void 0 ? _f : "workspace",
|
|
56
|
+
};
|
|
57
|
+
IModelJsFs_1.IModelJsFs.recursiveMkDirSync(cacheProps.rootDir);
|
|
58
|
+
if (cacheProps.clearContents)
|
|
59
|
+
fs.emptyDirSync(cacheProps.rootDir);
|
|
60
|
+
this._cloudCache = SQLiteDb_1.SQLiteDb.createCloudCache(cacheProps);
|
|
61
|
+
}
|
|
62
|
+
return this._cloudCache;
|
|
35
63
|
}
|
|
36
64
|
addContainer(toAdd) {
|
|
37
65
|
if (undefined !== this._containers.get(toAdd.id))
|
|
38
66
|
throw new Error("container already exists in workspace");
|
|
39
67
|
this._containers.set(toAdd.id, toAdd);
|
|
40
68
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const id = this.resolveContainerId(props);
|
|
44
|
-
if (undefined === id)
|
|
45
|
-
throw new Error(`can't resolve workspace container name [${props.containerName}]`);
|
|
46
|
-
return (_a = this._containers.get(id)) !== null && _a !== void 0 ? _a : new ITwinWorkspaceContainer(this, id);
|
|
69
|
+
findContainer(containerId) {
|
|
70
|
+
return this._containers.get(containerId);
|
|
47
71
|
}
|
|
48
|
-
|
|
49
|
-
|
|
72
|
+
getContainer(props, account) {
|
|
73
|
+
var _a;
|
|
74
|
+
return (_a = this.findContainer(props.containerId)) !== null && _a !== void 0 ? _a : new ITwinWorkspaceContainer(this, props, account);
|
|
75
|
+
}
|
|
76
|
+
getWorkspaceDbFromProps(dbProps, containerProps, account) {
|
|
77
|
+
return this.getContainer(containerProps, account).getWorkspaceDb(dbProps);
|
|
78
|
+
}
|
|
79
|
+
getWorkspaceDb(dbAlias) {
|
|
80
|
+
const dbProps = this.resolveDatabase(dbAlias);
|
|
81
|
+
const containerProps = this.resolveContainer(dbProps.containerName);
|
|
82
|
+
const account = containerProps.accountName !== "" ? this.resolveAccount(containerProps.accountName) : undefined;
|
|
83
|
+
let container = this.findContainer(containerProps.containerId);
|
|
84
|
+
if (undefined === container) {
|
|
85
|
+
container = this.getContainer(containerProps, account);
|
|
86
|
+
}
|
|
87
|
+
return container === null || container === void 0 ? void 0 : container.getWorkspaceDb(dbProps);
|
|
50
88
|
}
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
const setting = db.getString(settingRsc.rscName);
|
|
89
|
+
loadSettingsDictionary(settingRsc, db, priority) {
|
|
90
|
+
const setting = db.getString(settingRsc);
|
|
54
91
|
if (undefined === setting)
|
|
55
|
-
throw new Error(`could not load setting resource ${settingRsc
|
|
56
|
-
this.settings.addJson(`${db.container.id}/${db.dbName}/${settingRsc
|
|
92
|
+
throw new Error(`could not load setting resource ${settingRsc}`);
|
|
93
|
+
this.settings.addJson(`${db.container.id}/${db.dbName}/${settingRsc}`, priority, setting);
|
|
57
94
|
}
|
|
58
95
|
close() {
|
|
59
96
|
this.settings.close();
|
|
60
97
|
for (const [_id, container] of this._containers)
|
|
61
98
|
container.close();
|
|
62
99
|
this._containers.clear();
|
|
100
|
+
if (this._cloudCache) {
|
|
101
|
+
this._cloudCache.destroy();
|
|
102
|
+
this._cloudCache = undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
resolveAccount(accountName) {
|
|
106
|
+
const resolved = this.settings.resolveSetting(exports.WorkspaceSetting.Accounts, (val) => {
|
|
107
|
+
if (Array.isArray(val)) {
|
|
108
|
+
for (const entry of val) {
|
|
109
|
+
if (typeof entry === "object" && entry.name === accountName)
|
|
110
|
+
return SettingsSchemas_1.SettingsSchemas.validateArrayObject(entry, exports.WorkspaceSetting.Accounts, accountName);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return undefined; // keep going through all settings dictionaries
|
|
114
|
+
});
|
|
115
|
+
if (resolved === undefined)
|
|
116
|
+
throw new Error(`no setting "${exports.WorkspaceSetting.Accounts}" entry for "${accountName}"`);
|
|
117
|
+
return resolved;
|
|
118
|
+
}
|
|
119
|
+
resolveContainer(containerName) {
|
|
120
|
+
const resolved = this.settings.resolveSetting(exports.WorkspaceSetting.Containers, (val) => {
|
|
121
|
+
if (Array.isArray(val)) {
|
|
122
|
+
for (const entry of val) {
|
|
123
|
+
if (typeof entry === "object" && entry.name === containerName)
|
|
124
|
+
return SettingsSchemas_1.SettingsSchemas.validateArrayObject(entry, exports.WorkspaceSetting.Containers, containerName);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return undefined; // keep going through all settings dictionaries
|
|
128
|
+
});
|
|
129
|
+
if (resolved === undefined)
|
|
130
|
+
throw new Error(`no setting "${exports.WorkspaceSetting.Containers}" entry for "${containerName}"`);
|
|
131
|
+
return resolved;
|
|
63
132
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return props.containerId; // if the container id is supplied, just use it
|
|
67
|
-
const id = this.settings.resolveSetting(WorkspaceSetting.ContainerAlias, (val) => {
|
|
133
|
+
resolveDatabase(databaseName) {
|
|
134
|
+
const resolved = this.settings.resolveSetting(exports.WorkspaceSetting.Databases, (val) => {
|
|
68
135
|
if (Array.isArray(val)) {
|
|
69
136
|
for (const entry of val) {
|
|
70
|
-
if (typeof entry === "object" && entry.name ===
|
|
71
|
-
return entry.
|
|
137
|
+
if (typeof entry === "object" && entry.name === databaseName)
|
|
138
|
+
return SettingsSchemas_1.SettingsSchemas.validateArrayObject(entry, exports.WorkspaceSetting.Databases, databaseName);
|
|
72
139
|
}
|
|
73
140
|
}
|
|
74
141
|
return undefined; // keep going through all settings dictionaries
|
|
75
|
-
}
|
|
76
|
-
if (
|
|
77
|
-
throw new Error("
|
|
78
|
-
return
|
|
142
|
+
});
|
|
143
|
+
if (resolved === undefined)
|
|
144
|
+
throw new Error(`no setting "${exports.WorkspaceSetting.Databases}" entry for "${databaseName}"`);
|
|
145
|
+
return resolved;
|
|
79
146
|
}
|
|
80
147
|
}
|
|
81
148
|
exports.ITwinWorkspace = ITwinWorkspace;
|
|
82
149
|
/** @internal */
|
|
83
150
|
class ITwinWorkspaceContainer {
|
|
84
|
-
constructor(workspace,
|
|
151
|
+
constructor(workspace, props, account) {
|
|
85
152
|
this._wsDbs = new Map();
|
|
86
|
-
ITwinWorkspaceContainer.validateContainerId(
|
|
153
|
+
ITwinWorkspaceContainer.validateContainerId(props.containerId);
|
|
87
154
|
this.workspace = workspace;
|
|
88
|
-
this.id =
|
|
155
|
+
this.id = props.containerId;
|
|
156
|
+
if ((account === null || account === void 0 ? void 0 : account.accessName) && account.storageType)
|
|
157
|
+
this.cloudContainer = SQLiteDb_1.SQLiteDb.createCloudContainer({ accessToken: "", ...props, ...account });
|
|
89
158
|
workspace.addContainer(this);
|
|
90
159
|
this.filesDir = (0, path_1.join)(this.dirName, "Files");
|
|
160
|
+
const cloudContainer = this.cloudContainer;
|
|
161
|
+
if (undefined === cloudContainer)
|
|
162
|
+
return;
|
|
163
|
+
cloudContainer.connect(this.workspace.cloudCache);
|
|
164
|
+
if (props.syncOnConnect) {
|
|
165
|
+
try {
|
|
166
|
+
cloudContainer.checkForChanges();
|
|
167
|
+
}
|
|
168
|
+
catch (e) {
|
|
169
|
+
// must be offline
|
|
170
|
+
}
|
|
171
|
+
}
|
|
91
172
|
}
|
|
92
173
|
get dirName() { return (0, path_1.join)(this.workspace.containerDir, this.id); }
|
|
174
|
+
static noLeadingOrTrailingSpaces(name, msg) {
|
|
175
|
+
if (name.trim() !== name)
|
|
176
|
+
throw new Error(`${msg} [${name}] may not have leading or tailing spaces`);
|
|
177
|
+
}
|
|
93
178
|
/** rules for ContainerIds (from Azure, see https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata)
|
|
94
179
|
* - may only contain lower case letters, numbers or dashes
|
|
95
180
|
* - may not start or end with with a dash nor have more than one dash in a row
|
|
@@ -99,79 +184,148 @@ class ITwinWorkspaceContainer {
|
|
|
99
184
|
if (!/^(?=.{3,63}$)[a-z0-9]+(-[a-z0-9]+)*$/g.test(id))
|
|
100
185
|
throw new Error(`invalid containerId: [${id}]`);
|
|
101
186
|
}
|
|
187
|
+
static validateDbName(dbName) {
|
|
188
|
+
if (dbName === "" || dbName.length > 255 || /[#\.<>:"/\\"`'|?*\u0000-\u001F]/g.test(dbName) || /^(con|prn|aux|nul|com\d|lpt\d)$/i.test(dbName))
|
|
189
|
+
throw new Error(`invalid dbName: [${dbName}]`);
|
|
190
|
+
this.noLeadingOrTrailingSpaces(dbName, "dbName");
|
|
191
|
+
}
|
|
192
|
+
static validateVersion(version) {
|
|
193
|
+
var _a, _b;
|
|
194
|
+
version = version !== null && version !== void 0 ? version : "1.0.0";
|
|
195
|
+
if (version) {
|
|
196
|
+
const opts = { loose: true, includePrerelease: true };
|
|
197
|
+
// clean allows prerelease, so try it first. If that fails attempt to coerce it (coerce strips prerelease even if you say not to.)
|
|
198
|
+
const semVersion = (_a = semver.clean(version, opts)) !== null && _a !== void 0 ? _a : (_b = semver.coerce(version, opts)) === null || _b === void 0 ? void 0 : _b.version;
|
|
199
|
+
if (!semVersion)
|
|
200
|
+
throw new Error("invalid version specification");
|
|
201
|
+
version = semVersion;
|
|
202
|
+
}
|
|
203
|
+
return version;
|
|
204
|
+
}
|
|
205
|
+
static parseDbFileName(dbFileName) {
|
|
206
|
+
const parts = dbFileName.split(":");
|
|
207
|
+
return { dbName: parts[0], version: parts[1] };
|
|
208
|
+
}
|
|
209
|
+
static makeDbFileName(dbName, version) {
|
|
210
|
+
return `${dbName}:${this.validateVersion(version)}`;
|
|
211
|
+
}
|
|
212
|
+
static resolveCloudFileName(cloudContainer, props) {
|
|
213
|
+
var _a;
|
|
214
|
+
const dbName = props.dbName;
|
|
215
|
+
const dbs = cloudContainer.queryDatabases(`${dbName}*`); // get all databases that start with dbName
|
|
216
|
+
const versions = [];
|
|
217
|
+
for (const db of dbs) {
|
|
218
|
+
const thisDb = ITwinWorkspaceContainer.parseDbFileName(db);
|
|
219
|
+
if (thisDb.dbName === dbName && "string" === typeof thisDb.version && thisDb.version.length > 0)
|
|
220
|
+
versions.push(thisDb.version);
|
|
221
|
+
}
|
|
222
|
+
if (versions.length === 0)
|
|
223
|
+
versions[0] = "1.0.0";
|
|
224
|
+
const range = (_a = props.version) !== null && _a !== void 0 ? _a : "*";
|
|
225
|
+
try {
|
|
226
|
+
const version = semver.maxSatisfying(versions, range, { loose: true, includePrerelease: props.includePrerelease });
|
|
227
|
+
if (version)
|
|
228
|
+
return `${dbName}:${version}`;
|
|
229
|
+
}
|
|
230
|
+
catch (e) {
|
|
231
|
+
}
|
|
232
|
+
throw new Error(`No version of [${dbName}] available for "${range}"`);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Create a copy of an existing [[WorkspaceDb]] in a cloud container with a new version number.
|
|
236
|
+
* @param cloudContainer The attached cloud container holding the existing WorkspaceDb
|
|
237
|
+
* @param fromProps Properties that describe the source WorkspaceDb for the new version
|
|
238
|
+
* @param versionType The type of version increment to apply to the existing version.
|
|
239
|
+
* @note This requires that the cloudContainer is attached and the write lock on the container be held. The copy should be modified with
|
|
240
|
+
* new content before the write lock is released, and thereafter should never be modified again.
|
|
241
|
+
*/
|
|
242
|
+
static async makeNewVersion(cloudContainer, fromProps, versionType) {
|
|
243
|
+
const oldName = this.resolveCloudFileName(cloudContainer, fromProps);
|
|
244
|
+
const oldDb = this.parseDbFileName(oldName);
|
|
245
|
+
const newVersion = semver.inc(oldDb.version, versionType);
|
|
246
|
+
if (!newVersion)
|
|
247
|
+
throw new Error("invalid version");
|
|
248
|
+
const newName = this.makeDbFileName(oldDb.dbName, newVersion);
|
|
249
|
+
await cloudContainer.copyDatabase(oldName, newName);
|
|
250
|
+
return { oldName, newName };
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Convert a WorkspaceDb.Props specification into a DbFileName. For cloud-based containers, this resolves to the dbName, incorporating the appropriate
|
|
254
|
+
* version. For file-based containers, this returns a local filename of a WorkspaceDb file with the extension ".itwin-workspace"
|
|
255
|
+
*/
|
|
256
|
+
resolveDbFileName(props) {
|
|
257
|
+
const cloudContainer = this.cloudContainer;
|
|
258
|
+
if (undefined === cloudContainer)
|
|
259
|
+
return (0, path_1.join)(this.dirName, `${props.dbName}.${ITwinWorkspaceDb.fileExt}`); // local file, versions not allowed
|
|
260
|
+
return ITwinWorkspaceContainer.resolveCloudFileName(cloudContainer, props);
|
|
261
|
+
}
|
|
102
262
|
addWorkspaceDb(toAdd) {
|
|
103
263
|
if (undefined !== this._wsDbs.get(toAdd.dbName))
|
|
104
|
-
throw new Error(
|
|
264
|
+
throw new Error(`workspaceDb ${toAdd.dbName} already exists in workspace`);
|
|
105
265
|
this._wsDbs.set(toAdd.dbName, toAdd);
|
|
106
266
|
}
|
|
107
|
-
|
|
267
|
+
getWorkspaceDb(props) {
|
|
108
268
|
var _a;
|
|
109
|
-
const db = (_a = this._wsDbs.get(props.dbName)) !== null && _a !== void 0 ? _a : new ITwinWorkspaceDb(props
|
|
110
|
-
if (!db.isOpen)
|
|
111
|
-
if (props.cloudProps)
|
|
112
|
-
await imodeljs_native_1.CloudSqlite.downloadDb({ ...db, ...props.cloudProps, containerId: this.id });
|
|
269
|
+
const db = (_a = this._wsDbs.get(props.dbName)) !== null && _a !== void 0 ? _a : new ITwinWorkspaceDb(props, this);
|
|
270
|
+
if (!db.isOpen)
|
|
113
271
|
db.open();
|
|
114
|
-
}
|
|
115
272
|
return db;
|
|
116
273
|
}
|
|
117
274
|
dropWorkspaceDb(toDrop) {
|
|
118
275
|
const name = toDrop.dbName;
|
|
119
276
|
const wsDb = this._wsDbs.get(name);
|
|
120
277
|
if (wsDb === toDrop) {
|
|
121
|
-
wsDb.close();
|
|
122
278
|
this._wsDbs.delete(name);
|
|
279
|
+
wsDb.close();
|
|
123
280
|
}
|
|
124
281
|
}
|
|
125
282
|
close() {
|
|
283
|
+
var _a;
|
|
126
284
|
for (const [_name, db] of this._wsDbs)
|
|
127
285
|
db.close();
|
|
128
286
|
this._wsDbs.clear();
|
|
287
|
+
(_a = this.cloudContainer) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
129
288
|
}
|
|
289
|
+
/** Delete all local files extracted by [[WorkspaceDb.getFile]] for this container. */
|
|
130
290
|
purgeContainerFiles() {
|
|
131
291
|
IModelJsFs_1.IModelJsFs.purgeDirSync(this.filesDir);
|
|
132
292
|
}
|
|
133
293
|
}
|
|
134
294
|
exports.ITwinWorkspaceContainer = ITwinWorkspaceContainer;
|
|
135
295
|
/**
|
|
136
|
-
*
|
|
137
|
-
* @
|
|
296
|
+
* Implementation of WorkspaceDb
|
|
297
|
+
* @internal
|
|
138
298
|
*/
|
|
139
299
|
class ITwinWorkspaceDb {
|
|
140
|
-
constructor(
|
|
141
|
-
|
|
142
|
-
this.
|
|
143
|
-
|
|
144
|
-
this.
|
|
300
|
+
constructor(props, container) {
|
|
301
|
+
/** the SQLiteDb for this WorkspaceDb*/
|
|
302
|
+
this.sqliteDb = new SQLiteDb_1.SQLiteDb();
|
|
303
|
+
/** called before db is closed */
|
|
304
|
+
this.onClose = new core_bentley_1.BeEvent();
|
|
305
|
+
ITwinWorkspaceContainer.validateDbName(props.dbName);
|
|
306
|
+
this.dbName = props.dbName;
|
|
145
307
|
this.container = container;
|
|
146
|
-
this.
|
|
308
|
+
this.dbFileName = container.resolveDbFileName(props);
|
|
147
309
|
container.addWorkspaceDb(this);
|
|
148
310
|
}
|
|
149
|
-
|
|
150
|
-
if (name.trim() !== name)
|
|
151
|
-
throw new Error(`${msg} [${name}] may not have leading or tailing spaces`);
|
|
152
|
-
}
|
|
153
|
-
static validateDbName(dbName) {
|
|
154
|
-
if (dbName === "" || dbName.length > 255 || /[#\.<>:"/\\"`'|?*\u0000-\u001F]/g.test(dbName) || /^(con|prn|aux|nul|com\d|lpt\d)$/i.test(dbName))
|
|
155
|
-
throw new Error(`invalid dbName: [${dbName}]`);
|
|
156
|
-
this.noLeadingOrTrailingSpaces(dbName, "dbName");
|
|
157
|
-
}
|
|
311
|
+
/** true if this WorkspaceDb is currently open */
|
|
158
312
|
get isOpen() { return this.sqliteDb.isOpen; }
|
|
159
313
|
queryFileResource(rscName) {
|
|
160
314
|
const info = this.sqliteDb.nativeDb.queryEmbeddedFile(rscName);
|
|
161
315
|
if (undefined === info)
|
|
162
316
|
return undefined;
|
|
163
317
|
// since resource names can contain illegal characters, path separators, etc., we make the local file name from its hash, in hex.
|
|
164
|
-
let localFileName = (0, path_1.join)(this.container.filesDir, (0, crypto_1.createHash)("sha1").update(rscName).digest("hex"));
|
|
318
|
+
let localFileName = (0, path_1.join)(this.container.filesDir, (0, crypto_1.createHash)("sha1").update(this.dbFileName).update(rscName).digest("hex"));
|
|
165
319
|
if (info.fileExt !== "") // since some applications may expect to see the extension, append it here if it was supplied.
|
|
166
320
|
localFileName = `${localFileName}.${info.fileExt}`;
|
|
167
321
|
return { localFileName, info };
|
|
168
322
|
}
|
|
169
323
|
open() {
|
|
170
|
-
this.sqliteDb.openDb(this.
|
|
324
|
+
this.sqliteDb.openDb(this.dbFileName, core_bentley_1.OpenMode.Readonly, this.container.cloudContainer);
|
|
171
325
|
}
|
|
172
326
|
close() {
|
|
173
327
|
if (this.isOpen) {
|
|
174
|
-
this.
|
|
328
|
+
this.onClose.raiseEvent();
|
|
175
329
|
this.sqliteDb.closeDb();
|
|
176
330
|
this.container.dropWorkspaceDb(this);
|
|
177
331
|
}
|
|
@@ -183,12 +337,12 @@ class ITwinWorkspaceDb {
|
|
|
183
337
|
});
|
|
184
338
|
}
|
|
185
339
|
/** Get a BlobIO reader for a blob WorkspaceResource.
|
|
186
|
-
* @note when finished, caller must call `close` on the BlobIO.
|
|
340
|
+
* @note when finished, caller *must* call `close` on the BlobIO.
|
|
187
341
|
*/
|
|
188
342
|
getBlobReader(rscName) {
|
|
189
343
|
return this.sqliteDb.withSqliteStatement("SELECT rowid from blobs WHERE id=?", (stmt) => {
|
|
190
344
|
stmt.bindString(1, rscName);
|
|
191
|
-
const blobReader =
|
|
345
|
+
const blobReader = SQLiteDb_1.SQLiteDb.createBlobIO();
|
|
192
346
|
blobReader.open(this.sqliteDb.nativeDb, { tableName: "blobs", columnName: "value", row: stmt.getValueInteger(0) });
|
|
193
347
|
return blobReader;
|
|
194
348
|
});
|
|
@@ -220,21 +374,23 @@ class ITwinWorkspaceDb {
|
|
|
220
374
|
fs.chmodSync(localFileName, "0444"); // set file readonly
|
|
221
375
|
return localFileName;
|
|
222
376
|
}
|
|
377
|
+
prefetch(opts) {
|
|
378
|
+
const cloudContainer = this.container.cloudContainer;
|
|
379
|
+
return (cloudContainer !== undefined) ? SQLiteDb_1.SQLiteDb.startCloudPrefetch(cloudContainer, this.dbFileName, opts) : undefined;
|
|
380
|
+
}
|
|
223
381
|
}
|
|
224
382
|
exports.ITwinWorkspaceDb = ITwinWorkspaceDb;
|
|
383
|
+
/** file extension for local WorkspaceDbs */
|
|
384
|
+
ITwinWorkspaceDb.fileExt = "itwin-workspace";
|
|
225
385
|
/**
|
|
226
386
|
* An editable [[WorkspaceDb]]. This is used by administrators for creating and modifying `WorkspaceDb`s.
|
|
227
387
|
* For cloud-backed containers, the write token must be obtained before this class may be used. Only one user at at time
|
|
228
388
|
* may be editing.
|
|
229
|
-
* @
|
|
389
|
+
* @internal
|
|
230
390
|
*/
|
|
231
391
|
class EditableWorkspaceDb extends ITwinWorkspaceDb {
|
|
232
|
-
constructor() {
|
|
233
|
-
super(...arguments);
|
|
234
|
-
this._isCloudOpen = false;
|
|
235
|
-
}
|
|
236
392
|
static validateResourceName(name) {
|
|
237
|
-
|
|
393
|
+
ITwinWorkspaceContainer.noLeadingOrTrailingSpaces(name, "resource name");
|
|
238
394
|
if (name.length > 1024)
|
|
239
395
|
throw new Error("resource name too long");
|
|
240
396
|
}
|
|
@@ -244,14 +400,7 @@ class EditableWorkspaceDb extends ITwinWorkspaceDb {
|
|
|
244
400
|
throw new Error("value is too large");
|
|
245
401
|
}
|
|
246
402
|
open() {
|
|
247
|
-
this.sqliteDb.openDb(this.
|
|
248
|
-
}
|
|
249
|
-
close() {
|
|
250
|
-
if (this._isCloudOpen) {
|
|
251
|
-
// this.db.nativeDb.flushCloudUpload(); TODO: add back when available
|
|
252
|
-
this._isCloudOpen = false;
|
|
253
|
-
}
|
|
254
|
-
super.close();
|
|
403
|
+
this.sqliteDb.openDb(this.dbFileName, core_bentley_1.OpenMode.ReadWrite, this.container.cloudContainer);
|
|
255
404
|
}
|
|
256
405
|
getFileModifiedTime(localFileName) {
|
|
257
406
|
return Math.round(fs.statSync(localFileName).mtimeMs);
|
|
@@ -261,24 +410,38 @@ class EditableWorkspaceDb extends ITwinWorkspaceDb {
|
|
|
261
410
|
stmt.bindString(1, rscName);
|
|
262
411
|
bind === null || bind === void 0 ? void 0 : bind(stmt);
|
|
263
412
|
const rc = stmt.step();
|
|
264
|
-
if (core_bentley_1.DbResult.BE_SQLITE_DONE !== rc)
|
|
265
|
-
|
|
413
|
+
if (core_bentley_1.DbResult.BE_SQLITE_DONE !== rc) {
|
|
414
|
+
if (core_bentley_1.DbResult.BE_SQLITE_CONSTRAINT_PRIMARYKEY === rc)
|
|
415
|
+
throw new core_common_1.IModelError(rc, `resource "${rscName}" already exists`);
|
|
416
|
+
throw new core_common_1.IModelError(rc, `workspace [${sql}]`);
|
|
417
|
+
}
|
|
266
418
|
});
|
|
267
419
|
this.sqliteDb.saveChanges();
|
|
268
420
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
421
|
+
async createDb(version) {
|
|
422
|
+
if (!this.container.cloudContainer) {
|
|
423
|
+
EditableWorkspaceDb.createEmpty(this.dbFileName);
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
// currently the only way to create a workspaceDb in a cloud container is to create a temporary workspaceDb and upload it.
|
|
427
|
+
const tempDbFile = (0, path_1.join)(IModelHost_1.KnownLocations.tmpdir, `empty.${ITwinWorkspaceDb.fileExt}`);
|
|
428
|
+
if (fs.existsSync(tempDbFile))
|
|
429
|
+
fs.rmSync(tempDbFile);
|
|
430
|
+
EditableWorkspaceDb.createEmpty(tempDbFile);
|
|
431
|
+
this.dbFileName = ITwinWorkspaceContainer.makeDbFileName(this.dbName, version);
|
|
432
|
+
await imodeljs_native_1.CloudSqlite.uploadDb(this.container.cloudContainer, { localFileName: tempDbFile, dbName: this.dbFileName });
|
|
433
|
+
fs.rmSync(tempDbFile);
|
|
434
|
+
}
|
|
435
|
+
this.open();
|
|
279
436
|
}
|
|
280
|
-
|
|
281
|
-
|
|
437
|
+
/** Create a new, empty, EditableWorkspaceDb for importing Workspace resources. */
|
|
438
|
+
static createEmpty(fileName) {
|
|
439
|
+
const db = new SQLiteDb_1.SQLiteDb();
|
|
440
|
+
IModelJsFs_1.IModelJsFs.recursiveMkDirSync((0, path_1.dirname)(fileName));
|
|
441
|
+
db.createDb(fileName);
|
|
442
|
+
db.executeSQL("CREATE TABLE strings(id TEXT PRIMARY KEY NOT NULL,value TEXT)");
|
|
443
|
+
db.executeSQL("CREATE TABLE blobs(id TEXT PRIMARY KEY NOT NULL,value BLOB)");
|
|
444
|
+
db.closeDb(true);
|
|
282
445
|
}
|
|
283
446
|
/** Add a new string resource to this WorkspaceDb.
|
|
284
447
|
* @param rscName The name of the string resource.
|
|
@@ -326,7 +489,7 @@ class EditableWorkspaceDb extends ITwinWorkspaceDb {
|
|
|
326
489
|
getBlobWriter(rscName) {
|
|
327
490
|
return this.sqliteDb.withSqliteStatement("SELECT rowid from blobs WHERE id=?", (stmt) => {
|
|
328
491
|
stmt.bindString(1, rscName);
|
|
329
|
-
const blobWriter =
|
|
492
|
+
const blobWriter = SQLiteDb_1.SQLiteDb.createBlobIO();
|
|
330
493
|
blobWriter.open(this.sqliteDb.nativeDb, { tableName: "blobs", columnName: "value", row: stmt.getValueInteger(0), writeable: true });
|
|
331
494
|
return blobWriter;
|
|
332
495
|
});
|
|
@@ -349,7 +512,7 @@ class EditableWorkspaceDb extends ITwinWorkspaceDb {
|
|
|
349
512
|
fileExt = fileExt.slice(1);
|
|
350
513
|
this.sqliteDb.nativeDb.embedFile({ name: rscName, localFileName, date: this.getFileModifiedTime(localFileName), fileExt });
|
|
351
514
|
}
|
|
352
|
-
/** Replace an existing file resource with the contents of
|
|
515
|
+
/** Replace an existing file resource with the contents of another local file.
|
|
353
516
|
* @param rscName The name of the file resource.
|
|
354
517
|
* @param localFileName The name of a local file to be read.
|
|
355
518
|
* @throws if rscName does not exist
|