mirror of
https://github.com/antos-rde/antosdk-apps.git
synced 2024-09-07 21:16:39 +02:00
447 lines
18 KiB
JavaScript
447 lines
18 KiB
JavaScript
|
|
var OS;
|
|
(function (OS) {
|
|
let API;
|
|
(function (API) {
|
|
class SQLiteDBCore {
|
|
constructor(path) {
|
|
if (!SQLiteDBCore.REGISTY) {
|
|
SQLiteDBCore.REGISTY = {};
|
|
}
|
|
this.db_file = path.asFileHandle();
|
|
if (SQLiteDBCore.REGISTY[this.db_file.path]) {
|
|
this.db_file = SQLiteDBCore.REGISTY[this.db_file.path];
|
|
}
|
|
else {
|
|
SQLiteDBCore.REGISTY[this.db_file.path] = this.db_file;
|
|
}
|
|
}
|
|
pwd() {
|
|
return "pkg://SQLiteDB/".asFileHandle();
|
|
}
|
|
fileinfo() {
|
|
return this.db_file.info;
|
|
}
|
|
/**
|
|
* init and create the db file if it does not exist
|
|
*/
|
|
init() {
|
|
return new Promise(async (ok, reject) => {
|
|
try {
|
|
if (this.db_file.ready) {
|
|
return ok(true);
|
|
}
|
|
let request = {
|
|
action: 'init',
|
|
args: {
|
|
db_source: this.db_file.path,
|
|
}
|
|
};
|
|
let _result = await this.call(request);
|
|
_result = await this.db_file.onready();
|
|
if (!this.db_file || !this.db_file.ready || this.db_file.info.type !== "file") {
|
|
throw __("DB file meta-data is invalid: {0}", this.db_file.path).__();
|
|
}
|
|
ok(true);
|
|
}
|
|
catch (e) {
|
|
reject(__e(e));
|
|
}
|
|
});
|
|
}
|
|
call(request) {
|
|
return new Promise(async (ok, reject) => {
|
|
request.args.db_source = this.db_file.path;
|
|
let cmd = {
|
|
path: this.pwd().path + "/api/api.lua",
|
|
parameters: request
|
|
};
|
|
let data = await API.apigateway(cmd, false);
|
|
if (!data.error) {
|
|
ok(data.result);
|
|
}
|
|
else {
|
|
reject(API.throwe(__("SQLiteDB server call error: {0}", data.error)));
|
|
}
|
|
});
|
|
}
|
|
request(rq) {
|
|
return new Promise(async (ok, reject) => {
|
|
try {
|
|
if (!this.db_file.ready) {
|
|
let _ = await this.init();
|
|
}
|
|
let result = await this.call(rq);
|
|
ok(result);
|
|
}
|
|
catch (e) {
|
|
reject(__e(e));
|
|
}
|
|
});
|
|
}
|
|
select(filter) {
|
|
let rq = {
|
|
action: 'select',
|
|
args: {
|
|
filter
|
|
}
|
|
};
|
|
return this.request(rq);
|
|
}
|
|
delete_records(filter) {
|
|
let rq = {
|
|
action: 'delete_records',
|
|
args: {
|
|
filter
|
|
}
|
|
};
|
|
return this.request(rq);
|
|
}
|
|
drop_table(table_name) {
|
|
let rq = {
|
|
action: 'drop_table',
|
|
args: { table_name }
|
|
};
|
|
return this.request(rq);
|
|
}
|
|
list_tables() {
|
|
let rq = {
|
|
action: 'list_table',
|
|
args: {}
|
|
};
|
|
return this.request(rq);
|
|
}
|
|
create_table(table, scheme) {
|
|
let rq = {
|
|
action: 'create_table',
|
|
args: {
|
|
table_name: table,
|
|
scheme
|
|
}
|
|
};
|
|
return this.request(rq);
|
|
}
|
|
get_table_scheme(table_name) {
|
|
let rq = {
|
|
action: 'table_scheme',
|
|
args: {
|
|
table_name
|
|
}
|
|
};
|
|
return this.request(rq);
|
|
}
|
|
insert(table_name, record, pk) {
|
|
let rq = {
|
|
action: 'insert',
|
|
args: {
|
|
table_name,
|
|
record,
|
|
pk
|
|
}
|
|
};
|
|
return this.request(rq);
|
|
}
|
|
update(table_name, record, pk) {
|
|
let rq = {
|
|
action: 'update',
|
|
args: {
|
|
table_name,
|
|
record,
|
|
pk
|
|
}
|
|
};
|
|
return this.request(rq);
|
|
}
|
|
last_insert_id() {
|
|
let rq = {
|
|
action: 'last_insert_id',
|
|
args: {}
|
|
};
|
|
return this.request(rq);
|
|
}
|
|
}
|
|
let VFS;
|
|
(function (VFS) {
|
|
/**
|
|
* SQLite VFS handle for database accessing
|
|
*
|
|
* A Sqlite file handle shall be in the following formats:
|
|
* * `sqlite://remote/path/to/file.db` refers to the entire databale (`remote/path/to/file.db` is relative to the home folder)
|
|
* - read operation, will list all available tables
|
|
* - write operations will create table
|
|
* - rm operation will delete table
|
|
* - meta operation will return file info
|
|
* - other operations are not supported
|
|
* * `sqlite://remote/path/to/file.db@table_name` refers to the table `table_name` in the database
|
|
* - meta operation will return fileinfo with table scheme information
|
|
* - read operation will read all records by filter defined by the filter as parameters
|
|
* - write operations will insert a new record
|
|
* - rm operation will delete records by filter as parameters
|
|
* - other operations are not supported
|
|
* - `sqlite://remote/path/to/file.db@table_name@id` refers to a records in `table_name` with ID `id`
|
|
* - read operation will read the current record
|
|
* - write operation will update current record
|
|
* - rm operation will delete current record
|
|
* - other operations are not supported
|
|
*
|
|
* Example of filter:
|
|
* ```ts
|
|
* {
|
|
* table_name:'contacts';
|
|
* where: {
|
|
* id$gte: 10,
|
|
* user: "dany'",
|
|
* $or: {
|
|
* 'user.email': "test@mail.com",
|
|
* age$lte: 30,
|
|
* $and: {
|
|
* 'user.birth$ne': 1986,
|
|
* age$not_between: [20,30],
|
|
* name$not_like: "%LE"
|
|
* }
|
|
* }
|
|
* },
|
|
* fields: ['name as n', 'id', 'email'],
|
|
* order: ['user.name$asc', "id$desc"],
|
|
* joins: {
|
|
* cid: 'Category.id',
|
|
* did: 'Country.id',
|
|
* uid: "User.id"
|
|
* }
|
|
*}
|
|
* ```
|
|
* This will generate the followings expressions:
|
|
* - `( self.name as n,self.id,self.email )` for fields
|
|
* - condition:
|
|
* ```
|
|
* (
|
|
* ( contacts.id >= 10 ) AND
|
|
* ( contacts.user = 'dany''' ) AND
|
|
* (
|
|
* ( user.email = 'test@mail.com' ) OR
|
|
* ( contacts.age <= 30 ) OR
|
|
* (
|
|
* ( user.birth != 1986 ) AND
|
|
* ( contacts.age NOT BETWEEN 20 AND 30 ) AND
|
|
* ( contacts.name NOT LIKE '%LE' )
|
|
* )
|
|
* )
|
|
* )
|
|
* ```
|
|
* - order: `user.name ASC,contacts.id DESC`
|
|
* - joining:
|
|
* ```
|
|
* INNER JOIN Category ON contacts.cid = Category.id
|
|
* INNER JOIN Country ON contacts.did = Country.id
|
|
* INNER JOIN Country ON contacts.did = Country.id
|
|
* ```
|
|
*
|
|
* @class SqliteFileHandle
|
|
* @extends {BaseFileHandle}
|
|
*/
|
|
class SqliteFileHandle extends VFS.BaseFileHandle {
|
|
/**
|
|
* Set a file path to the current file handle
|
|
*
|
|
*
|
|
* @param {string} p
|
|
* @returns {void}
|
|
* @memberof SqliteFileHandle
|
|
*/
|
|
setPath(p) {
|
|
let arr = p.split("@");
|
|
super.setPath(arr[0]);
|
|
if (arr.length > 3) {
|
|
throw new Error(__("Invalid file path").__());
|
|
}
|
|
this.path = p;
|
|
this._table_name = arr[1];
|
|
this._id = arr[2] ? parseInt(arr[2]) : undefined;
|
|
this._handle = new SQLiteDBCore(`home://${this.genealogy.join("/")}`);
|
|
}
|
|
/**
|
|
* Read database file meta-data
|
|
*
|
|
* Return file info on the target database file, if the table_name is specified
|
|
* return also the table scheme
|
|
*
|
|
* @returns {Promise<RequestResult>}
|
|
* @memberof SqliteFileHandle
|
|
*/
|
|
meta() {
|
|
return new Promise(async (resolve, reject) => {
|
|
try {
|
|
await this._handle.init();
|
|
let d = {
|
|
result: {
|
|
file: this._handle.fileinfo(),
|
|
schema: undefined
|
|
}, error: false
|
|
};
|
|
if (this._table_name) {
|
|
const data = await this._handle.get_table_scheme(this._table_name);
|
|
if (data.length == 0) {
|
|
d.result.schema = undefined;
|
|
}
|
|
else {
|
|
d.result.schema = {
|
|
fields: [],
|
|
types: {},
|
|
pk: undefined
|
|
};
|
|
d.result.schema.fields = data.map(e => e.name);
|
|
for (let v of data) {
|
|
d.result.schema.types[v.name] = v.type;
|
|
if (v.pk) {
|
|
d.result.schema.pk = v.name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return resolve(d);
|
|
}
|
|
catch (e) {
|
|
return reject(__e(e));
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Query the database based on the provided info
|
|
*
|
|
* If no table is provided, return list of tables in the
|
|
* data base.
|
|
* If the current table is specified:
|
|
* - if the record id is specfied return the record
|
|
* - otherwise, return the records in the table using the specified filter
|
|
*
|
|
* @protected
|
|
* @param {any} t filter type
|
|
* @returns {Promise<any>}
|
|
* @memberof SqliteFileHandle
|
|
*/
|
|
_rd(user_data) {
|
|
return new Promise(async (resolve, reject) => {
|
|
try {
|
|
if (this._table_name && !this.info.schema) {
|
|
throw new Error(__("Table `{0}` does not exists in database: {1}", this._table_name, this.path).__());
|
|
}
|
|
if (!this._table_name) {
|
|
// return list of tables in form of data base file handles in ready mode
|
|
let list = await this._handle.list_tables();
|
|
const map = {};
|
|
for (let v of list) {
|
|
map[v.name] = `${this.path}@${v.name}`.asFileHandle();
|
|
}
|
|
this.cache = map;
|
|
resolve(map);
|
|
}
|
|
else {
|
|
// return all the data in the table set by the filter
|
|
// if this is a table, return the filtered records
|
|
// otherwise, it is a record, fetch only that record
|
|
let filter = user_data;
|
|
if (!filter || this._id) {
|
|
filter = {};
|
|
}
|
|
filter.table_name = this._table_name;
|
|
if (this._id) {
|
|
filter.where = { id: this._id };
|
|
}
|
|
let data = await this._handle.select(filter);
|
|
if (this._id) {
|
|
this.cache = data[0];
|
|
}
|
|
else {
|
|
this.cache = data;
|
|
}
|
|
resolve(this.cache);
|
|
}
|
|
}
|
|
catch (e) {
|
|
return reject(__e(e));
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Write commit file cache to the remote database
|
|
*
|
|
* @protected
|
|
* @param {string} t is table name, used only when create table
|
|
* @returns {Promise<RequestResult>}
|
|
* @memberof SqliteFileHandle
|
|
*/
|
|
_wr(t) {
|
|
return new Promise(async (resolve, reject) => {
|
|
try {
|
|
if (!this.cache) {
|
|
throw new Error(__("No data to submit to remote database, please check the `cache` field").__());
|
|
}
|
|
await this.onready();
|
|
if (this._id && this._table_name) {
|
|
this.cache.id = this._id;
|
|
const ret = await this._handle.update(this._table_name, this.cache, this.info.schema.pk);
|
|
resolve({ result: ret, error: false });
|
|
return;
|
|
}
|
|
if (this._table_name) {
|
|
const ret = await this._handle.insert(this._table_name, this.cache, this.info.schema.pk);
|
|
resolve({ result: ret, error: false });
|
|
return;
|
|
}
|
|
// create a new table with the scheme provided in the cache
|
|
let r = await this._handle.create_table(t, this.cache);
|
|
resolve({ result: r, error: false });
|
|
}
|
|
catch (e) {
|
|
return reject(__e(e));
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Delete data from remote database
|
|
*
|
|
* @protected
|
|
* @param {any} user_data is table name, for delete table, otherwise, filter object for deleting records
|
|
* @returns {Promise<RequestResult>}
|
|
* @memberof SqliteFileHandle
|
|
*/
|
|
_rm(user_data) {
|
|
return new Promise(async (resolve, reject) => {
|
|
try {
|
|
if (this._table_name && !this.info.schema) {
|
|
throw new Error(__("Table `{0}` does not exists in database: {1}", this._table_name, this.path).__());
|
|
}
|
|
if (!this._table_name) {
|
|
let table_name = user_data;
|
|
if (!table_name) {
|
|
throw new Error(__("No table specified for dropping").__());
|
|
}
|
|
let ret = await this._handle.drop_table(table_name);
|
|
resolve({ result: ret, error: false });
|
|
// delete the table
|
|
}
|
|
else {
|
|
let filter = user_data;
|
|
// delete the records in the table using the filter
|
|
if (!filter || this._id) {
|
|
filter = {};
|
|
}
|
|
filter.table_name = this._table_name;
|
|
if (this._id) {
|
|
filter.where = { id: this._id };
|
|
}
|
|
let ret = await this._handle.delete_records(filter);
|
|
resolve({ result: ret, error: false });
|
|
}
|
|
}
|
|
catch (e) {
|
|
return reject(__e(e));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
VFS.register("^sqlite$", SqliteFileHandle);
|
|
})(VFS = API.VFS || (API.VFS = {}));
|
|
})(API = OS.API || (OS.API = {}));
|
|
})(OS || (OS = {}));
|