@itwin/core-backend
Version:
iTwin.js backend components
209 lines • 8.99 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module NativeApp
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.NativeAppStorage = void 0;
const node_path_1 = require("node:path");
const core_bentley_1 = require("@itwin/core-bentley");
const core_common_1 = require("@itwin/core-common");
const ECDb_1 = require("./ECDb");
const IModelHost_1 = require("./IModelHost");
const IModelJsFs_1 = require("./IModelJsFs");
const NativeHost_1 = require("./NativeHost");
// cspell:ignore ecdb
/**
* A local file stored in the [[NativeHost.appSettingsCacheDir]] for storing key/value pairs.
* @public
*/
class NativeAppStorage {
_ecdb;
id;
static _ext = ".settings-db";
static _storages = new Map();
static _init = false;
constructor(_ecdb, id) {
this._ecdb = _ecdb;
this.id = id;
}
/** Set the value for a key */
setData(key, value) {
const rc = this._ecdb.withPreparedSqliteStatement("INSERT INTO app_setting(key,type,val)VALUES(?,?,?) ON CONFLICT(key) DO UPDATE SET type=excluded.type,val=excluded.val", (stmt) => {
let valType = (value === undefined || value === null) ? "null" : typeof value;
if (valType === "object" && (value instanceof Uint8Array))
valType = "Uint8Array";
switch (valType) {
case "null":
case "number":
case "string":
case "boolean":
case "Uint8Array":
break;
default:
throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, `Unsupported type ${valType} for value for key='${key}`);
}
stmt.bindValue(1, key);
stmt.bindValue(2, valType);
stmt.bindValue(3, value);
return stmt.step();
});
if (rc !== core_bentley_1.DbResult.BE_SQLITE_DONE)
throw new core_common_1.IModelError(rc, "SQLite error");
this._ecdb.saveChanges();
}
/** Get the value for a key from this Storage. If key is not present or is null, return undefined. */
getData(key) {
return this._ecdb.withPreparedSqliteStatement("SELECT type,val FROM app_setting WHERE key=?", (stmt) => {
stmt.bindValue(1, key);
if (core_bentley_1.DbResult.BE_SQLITE_ROW !== stmt.step())
return undefined;
const valType = stmt.getValueString(0);
switch (valType) {
case "number":
return stmt.getValueDouble(1);
case "string":
return stmt.getValueString(1);
case "boolean":
return Boolean(stmt.getValueInteger(1));
case "Uint8Array":
return stmt.getValueBlob(1);
case "null":
return undefined;
}
throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, `Unsupported type in cache ${valType}`);
});
}
/** return the type of the value for a key, or undefined if not present. */
getValueType(key) {
return this._ecdb.withSqliteStatement("SELECT type FROM app_setting WHERE key=?", (stmt) => {
stmt.bindValue(1, key);
return stmt.step() === core_bentley_1.DbResult.BE_SQLITE_ROW ? stmt.getValueString(0) : undefined;
});
}
/** return `true` if the key is present, but has a null value. */
hasNullValue(key) {
return this.getValueType(key) === "null";
}
/** Get the value for a key as a string. If it is not present, or not of type string, return undefined */
getString(key) {
const val = this.getData(key);
return typeof val === "string" ? val : undefined;
}
/** Get the value for a key as a number. If it is not present, or not of type number, return undefined */
getNumber(key) {
const val = this.getData(key);
return typeof val === "number" ? val : undefined;
}
/** Get the value for a key as a boolean. If it is not present, or not of type boolean, return undefined */
getBoolean(key) {
const val = this.getData(key);
return typeof val === "boolean" ? val : undefined;
}
/** Get the value for a key as a Uint8Array. If it is not present, or not of type Uint8Array, return undefined */
getUint8Array(key) {
const val = this.getData(key);
return val instanceof Uint8Array ? val : undefined;
}
/** Get all key names in this Storage */
getKeys() {
const keys = new Array();
this._ecdb.withPreparedSqliteStatement("SELECT key FROM app_setting", (stmt) => {
while (core_bentley_1.DbResult.BE_SQLITE_ROW === stmt.step()) {
keys.push(stmt.getValueString(0));
}
});
return keys;
}
/** Remove a key/value pair from this Storage */
removeData(key) {
const rc = this._ecdb.withPreparedSqliteStatement("DELETE FROM app_setting WHERE key=?", (stmt) => {
stmt.bindValue(1, key);
return stmt.step();
});
if (rc !== core_bentley_1.DbResult.BE_SQLITE_DONE) {
throw new core_common_1.IModelError(rc, "SQLite error");
}
}
/** Remove all key/value pairs */
removeAll() {
const rc = this._ecdb.withPreparedSqliteStatement("DELETE FROM app_setting", (stmt) => {
return stmt.step();
});
if (rc !== core_bentley_1.DbResult.BE_SQLITE_DONE) {
throw new core_common_1.IModelError(rc, "SQLite error");
}
}
/** Close this Storage. */
close(deleteFile = false) {
const storageFile = (0, node_path_1.join)(NativeHost_1.NativeHost.appSettingsCacheDir, this.id);
this._ecdb.saveChanges();
this._ecdb.closeDb();
this._ecdb = undefined;
if (deleteFile)
IModelJsFs_1.IModelJsFs.removeSync(storageFile);
NativeAppStorage._storages.delete(this.id);
}
static init(ecdb) {
return ecdb.withPreparedSqliteStatement("CREATE TABLE app_setting(key PRIMARY KEY,type,val);", (stmt) => {
return stmt.step();
});
}
/** find and open storage by its name. */
static find(name) {
const storage = this._storages.get(name);
if (undefined === storage)
throw new core_common_1.IModelError(core_bentley_1.IModelStatus.FileNotFound, `Storage ${name} not open`);
return storage;
}
/** Close all opened Storages.
* @internal
*/
static closeAll() {
this._storages.forEach((value) => value.close());
this._storages.clear();
}
/** @internal */
static getStorageNames() {
return IModelJsFs_1.IModelJsFs.readdirSync(NativeHost_1.NativeHost.appSettingsCacheDir).filter((name) => name.endsWith(this._ext));
}
/** Open or find a Storage by name. */
static open(name) {
if (!this._init) {
IModelHost_1.IModelHost.onBeforeShutdown.addOnce(() => this.closeAll());
this._init = true;
}
if (name.includes("\x00"))
throw new Error("Storage name contains illegal characters");
const fileName = name + this._ext;
if (!IModelJsFs_1.IModelJsFs.existsSync(NativeHost_1.NativeHost.appSettingsCacheDir))
IModelJsFs_1.IModelJsFs.recursiveMkDirSync(NativeHost_1.NativeHost.appSettingsCacheDir);
const storageFile = (0, node_path_1.join)(NativeHost_1.NativeHost.appSettingsCacheDir, fileName);
if ((0, node_path_1.relative)(NativeHost_1.NativeHost.appSettingsCacheDir, storageFile).startsWith(".."))
throw new Error("Storage name should not be a path");
try {
return this.find(fileName); // see if it's already open
}
catch {
const ecdb = new ECDb_1.ECDb();
if (IModelJsFs_1.IModelJsFs.existsSync(storageFile)) {
ecdb.openDb(storageFile, ECDb_1.ECDbOpenMode.ReadWrite);
}
else {
ecdb.createDb(storageFile);
const rc = this.init(ecdb);
if (rc !== core_bentley_1.DbResult.BE_SQLITE_DONE)
throw new core_common_1.IModelError(rc, "SQLite error");
ecdb.saveChanges();
}
const storage = new NativeAppStorage(ecdb, fileName);
this._storages.set(fileName, storage);
return storage;
}
}
}
exports.NativeAppStorage = NativeAppStorage;
//# sourceMappingURL=NativeAppStorage.js.map
;