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