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