diff --git a/backends/sqlite.js b/backends/sqlite.js index 28df37de..81a649b3 100644 --- a/backends/sqlite.js +++ b/backends/sqlite.js @@ -3,308 +3,308 @@ const sqlite3 = require('sqlite3').verbose(); const debug = false; class sqliteDatabase { - constructor({ db_path }) { - this.db_path = db_path; - this.init_type_map(); - } + constructor({ db_path }) { + this.db_path = db_path; + this.init_type_map(); + } - connect(db_path) { - if (db_path) { - this.db_path = db_path; - } - return new Promise(resolve => { - this.conn = new sqlite3.Database(this.db_path, () => { - if (debug) { - this.conn.on('trace', (trace) => console.log(trace)); - } - resolve(); - }); - }); - } + connect(db_path) { + if (db_path) { + this.db_path = db_path; + } + return new Promise(resolve => { + this.conn = new sqlite3.Database(this.db_path, () => { + if (debug) { + this.conn.on('trace', (trace) => console.log(trace)); + } + resolve(); + }); + }); + } - async migrate() { - for (let doctype in frappe.modules) { - // check if controller module - if (frappe.modules[doctype].Meta) { - if (await this.table_exists(doctype)) { - await this.alter_table(doctype); - } else { - await this.create_table(doctype); - } + async migrate() { + for (let doctype in frappe.modules) { + // check if controller module + if (frappe.modules[doctype].Meta) { + if (await this.table_exists(doctype)) { + await this.alter_table(doctype); + } else { + await this.create_table(doctype); + } - } - } - await this.commit(); - } + } + } + await this.commit(); + } - async create_table(doctype) { - let meta = frappe.get_meta(doctype); - let columns = []; - let values = []; + async create_table(doctype) { + let meta = frappe.get_meta(doctype); + let columns = []; + let values = []; - for (let df of meta.get_valid_fields({ with_children: false })) { - if (this.type_map[df.fieldtype]) { - columns.push(this.get_column_definition(df)); - if (df.default) { - values.push(df.default); - } - } - } + for (let df of meta.get_valid_fields({ with_children: false })) { + if (this.type_map[df.fieldtype]) { + columns.push(this.get_column_definition(df)); + if (df.default) { + values.push(df.default); + } + } + } - const query = `CREATE TABLE IF NOT EXISTS ${frappe.slug(doctype)} ( + const query = `CREATE TABLE IF NOT EXISTS ${frappe.slug(doctype)} ( ${columns.join(", ")})`; - return await this.run(query, values); - } + return await this.run(query, values); + } - close() { - this.conn.close(); - } + close() { + this.conn.close(); + } - get_column_definition(df) { - return `${df.fieldname} ${this.type_map[df.fieldtype]} ${df.reqd ? "not null" : ""} ${df.default ? "default ?" : ""}` - } + get_column_definition(df) { + return `${df.fieldname} ${this.type_map[df.fieldtype]} ${df.reqd ? "not null" : ""} ${df.default ? "default ?" : ""}` + } - async alter_table(doctype) { - // get columns - let table_columns = (await this.sql(`PRAGMA table_info(${doctype})`)).map(d => d.name); - let meta = frappe.get_meta(doctype); - let values = []; + async alter_table(doctype) { + // get columns + let table_columns = (await this.sql(`PRAGMA table_info(${doctype})`)).map(d => d.name); + let meta = frappe.get_meta(doctype); + let values = []; - for (let df of meta.get_valid_fields({ with_children: false })) { - if (!table_columns.includes(df.fieldname) && this.type_map[df.fieldtype]) { - values = [] - if (df.default) { - values.push(df.default); - } - await this.run(`ALTER TABLE ${frappe.slug(doctype)} ADD COLUMN ${this.get_column_definition(df)}`, values); - } - } - } + for (let df of meta.get_valid_fields({ with_children: false })) { + if (!table_columns.includes(df.fieldname) && this.type_map[df.fieldtype]) { + values = [] + if (df.default) { + values.push(df.default); + } + await this.run(`ALTER TABLE ${frappe.slug(doctype)} ADD COLUMN ${this.get_column_definition(df)}`, values); + } + } + } - async get(doctype, name, fields = '*') { - // load parent - let doc = await this.get_one(doctype, name, fields); + async get(doctype, name, fields = '*') { + // load parent + let doc = await this.get_one(doctype, name, fields); - // load children - let table_fields = frappe.get_meta(doctype).get_table_fields(); - for (let field of table_fields) { - doc[fieldname] = await this.get_all({ doctype: field.childtype, fields: ["*"], filters: { parent: doc.name } }); - } - return doc; - } + // load children + let table_fields = frappe.get_meta(doctype).get_table_fields(); + for (let field of table_fields) { + doc[fieldname] = await this.get_all({ doctype: field.childtype, fields: ["*"], filters: { parent: doc.name } }); + } + return doc; + } - get_one(doctype, name, fields = '*') { - if (fields instanceof Array) { - fields = fields.join(", "); - } + get_one(doctype, name, fields = '*') { + if (fields instanceof Array) { + fields = fields.join(", "); + } - return new Promise((resolve, reject) => { - this.conn.get(`select ${fields} from ${frappe.slug(doctype)} + return new Promise((resolve, reject) => { + this.conn.get(`select ${fields} from ${frappe.slug(doctype)} where name = ?`, name, - (err, row) => { - resolve(row || {}); - }); - }); - } + (err, row) => { + resolve(row || {}); + }); + }); + } - async insert(doctype, doc) { - // insert parent - await this.insert_one(doctype, doc); + async insert(doctype, doc) { + // insert parent + await this.insert_one(doctype, doc); - // insert children - let table_fields = frappe.get_meta(doctype).get_table_fields(); - for (let field of table_fields) { - for (let child of (doc[field.fieldname] || [])) { - await this.insert_one(field.childtype, child); - } - } - } + // insert children + let table_fields = frappe.get_meta(doctype).get_table_fields(); + for (let field of table_fields) { + for (let child of (doc[field.fieldname] || [])) { + await this.insert_one(field.childtype, child); + } + } + } - async insert_one(doctype, doc) { - let fields = this.get_keys(doctype); - let placeholders = fields.map(d => '?').join(', '); + async insert_one(doctype, doc) { + let fields = this.get_keys(doctype); + let placeholders = fields.map(d => '?').join(', '); - return await this.run(`insert into ${frappe.slug(doctype)} + return await this.run(`insert into ${frappe.slug(doctype)} (${Object.keys(doc).join(", ")}) values (${placeholders})`, this.get_formatted_values(fields, doc)); - } + } - async update(doctype, doc) { - // updates parent and child, similar to insert, but writing separately for clarity + async update(doctype, doc) { + // updates parent and child, similar to insert, but writing separately for clarity - // update parent - await this.update_one(doctype, doc); + // update parent + await this.update_one(doctype, doc); - // update children - let table_fields = frappe.get_meta(doctype).get_table_fields(); - for (let field of table_fields) { - for (let child of (doc[field.fieldname] || [])) { - await this.update_one(field.childtype, child); - } - } - } + // update children + let table_fields = frappe.get_meta(doctype).get_table_fields(); + for (let field of table_fields) { + for (let child of (doc[field.fieldname] || [])) { + await this.update_one(field.childtype, child); + } + } + } - async update_one(doctype, doc) { - let fields = this.get_keys(doctype); - let assigns = fields.map(field => `${field.fieldname} = ?`); - let values = this.get_formatted_values(fields, doc); + async update_one(doctype, doc) { + let fields = this.get_keys(doctype); + let assigns = fields.map(field => `${field.fieldname} = ?`); + let values = this.get_formatted_values(fields, doc); - // additional name for where clause - values.push(doc.name); + // additional name for where clause + values.push(doc.name); - return await this.run(`update ${frappe.slug(doctype)} + return await this.run(`update ${frappe.slug(doctype)} set ${assigns.join(", ")} where name=?`, values); - } + } - get_keys(doctype) { - return frappe.get_meta(doctype).get_valid_fields({ with_children: false }); - } + get_keys(doctype) { + return frappe.get_meta(doctype).get_valid_fields({ with_children: false }); + } - get_formatted_values(fields, doc) { - let values = fields.map(field => { - let value = doc[field.fieldname]; - if (value instanceof Date) { - return value.toISOString(); - } else { - return value; - } - }); - return values; - } + get_formatted_values(fields, doc) { + let values = fields.map(field => { + let value = doc[field.fieldname]; + if (value instanceof Date) { + return value.toISOString(); + } else { + return value; + } + }); + return values; + } - async delete(doctype, name) { - return await this.run(`delete from ${frappe.slug(doctype)} where name=?`, name); - } + async delete(doctype, name) { + return await this.run(`delete from ${frappe.slug(doctype)} where name=?`, name); + } - get_all({ doctype, fields, filters, start, limit, order_by = 'modified', order = 'desc' } = {}) { - if (!fields) { - fields = frappe.get_meta(doctype).get_keyword_fields(); - } - return new Promise((resolve, reject) => { - let conditions = this.get_filter_conditions(filters); + get_all({ doctype, fields, filters, start, limit, order_by = 'modified', order = 'desc' } = {}) { + if (!fields) { + fields = frappe.get_meta(doctype).get_keyword_fields(); + } + return new Promise((resolve, reject) => { + let conditions = this.get_filter_conditions(filters); - this.conn.all(`select ${fields.join(", ")} + this.conn.all(`select ${fields.join(", ")} from ${frappe.slug(doctype)} ${conditions.conditions ? "where" : ""} ${conditions.conditions} ${order_by ? ("order by " + order_by) : ""} ${order_by ? (order || "asc") : ""} ${limit ? ("limit " + limit) : ""} ${start ? ("offset " + start) : ""}`, conditions.values, - (err, rows) => { - if (err) { - reject(err); - } else { - resolve(rows); - } - }); - }); - } + (err, rows) => { + if (err) { + reject(err); + } else { + resolve(rows); + } + }); + }); + } - get_filter_conditions(filters) { - // {"status": "Open"} => `status = "Open"` - // {"status": "Open", "name": ["like", "apple%"]} - // => `status="Open" and name like "apple%" - let conditions = []; - let values = []; - for (let key in filters) { - const value = filters[key]; - if (value instanceof Array) { - // if its like, we should add the wildcard "%" if the user has not added - if (value[0].toLowerCase() === 'like' && !value[1].includes('%')) { - value[1] = `%${value[1]}%`; - } - conditions.push(`${key} ${value[0]} ?`); - values.push(value[1]); - } else { - conditions.push(`${key} = ?`); - values.push(value); - } - } - return { - conditions: conditions.length ? conditions.join(" and ") : "", - values: values - }; - } + get_filter_conditions(filters) { + // {"status": "Open"} => `status = "Open"` + // {"status": "Open", "name": ["like", "apple%"]} + // => `status="Open" and name like "apple%" + let conditions = []; + let values = []; + for (let key in filters) { + const value = filters[key]; + if (value instanceof Array) { + // if its like, we should add the wildcard "%" if the user has not added + if (value[0].toLowerCase() === 'like' && !value[1].includes('%')) { + value[1] = `%${value[1]}%`; + } + conditions.push(`${key} ${value[0]} ?`); + values.push(value[1]); + } else { + conditions.push(`${key} = ?`); + values.push(value); + } + } + return { + conditions: conditions.length ? conditions.join(" and ") : "", + values: values + }; + } - run(query, params) { - return new Promise((resolve, reject) => { - this.conn.run(query, params, (err) => { - if (err) { - console.log(err); - reject(err); - } else { - resolve(); - } - }); - }); - } + run(query, params) { + return new Promise((resolve, reject) => { + this.conn.run(query, params, (err) => { + if (err) { + console.log(err); + reject(err); + } else { + resolve(); + } + }); + }); + } - sql(query, params) { - return new Promise((resolve) => { - this.conn.all(query, params, (err, rows) => { - resolve(rows); - }); - }); - } + sql(query, params) { + return new Promise((resolve) => { + this.conn.all(query, params, (err, rows) => { + resolve(rows); + }); + }); + } - async commit() { - try { - await this.run('commit'); - } catch (e) { - if (e.errno !== 1) { - throw e; - } - } - } + async commit() { + try { + await this.run('commit'); + } catch (e) { + if (e.errno !== 1) { + throw e; + } + } + } - async get_value(doctype, filters, fieldname = 'name') { - if (typeof filters === 'string') { - filters = { name: filters }; - } + async get_value(doctype, filters, fieldname = 'name') { + if (typeof filters === 'string') { + filters = { name: filters }; + } - let row = await this.get_all({ - doctype: doctype, - fields: [fieldname], - filters: filters, - start: 0, - limit: 1 - }); - return row.length ? row[0][fieldname] : null; - } + let row = await this.get_all({ + doctype: doctype, + fields: [fieldname], + filters: filters, + start: 0, + limit: 1 + }); + return row.length ? row[0][fieldname] : null; + } - async table_exists(table) { - const name = await this.sql(`SELECT name FROM sqlite_master WHERE type='table' AND name='${table}'`); - return (name && name.length) ? true : false; - } + async table_exists(table) { + const name = await this.sql(`SELECT name FROM sqlite_master WHERE type='table' AND name='${table}'`); + return (name && name.length) ? true : false; + } - init_type_map() { - this.type_map = { - 'Currency': 'real' - , 'Int': 'integer' - , 'Float': 'real' - , 'Percent': 'real' - , 'Check': 'integer' - , 'Small Text': 'text' - , 'Long Text': 'text' - , 'Code': 'text' - , 'Text Editor': 'text' - , 'Date': 'text' - , 'Datetime': 'text' - , 'Time': 'text' - , 'Text': 'text' - , 'Data': 'text' - , 'Link': 'text' - , 'Dynamic Link': 'text' - , 'Password': 'text' - , 'Select': 'text' - , 'Read Only': 'text' - , 'Attach': 'text' - , 'Attach Image': 'text' - , 'Signature': 'text' - , 'Color': 'text' - , 'Barcode': 'text' - , 'Geolocation': 'text' - } - } + init_type_map() { + this.type_map = { + 'Currency': 'real' + , 'Int': 'integer' + , 'Float': 'real' + , 'Percent': 'real' + , 'Check': 'integer' + , 'Small Text': 'text' + , 'Long Text': 'text' + , 'Code': 'text' + , 'Text Editor': 'text' + , 'Date': 'text' + , 'Datetime': 'text' + , 'Time': 'text' + , 'Text': 'text' + , 'Data': 'text' + , 'Link': 'text' + , 'Dynamic Link': 'text' + , 'Password': 'text' + , 'Select': 'text' + , 'Read Only': 'text' + , 'Attach': 'text' + , 'Attach Image': 'text' + , 'Signature': 'text' + , 'Color': 'text' + , 'Barcode': 'text' + , 'Geolocation': 'text' + } + } } diff --git a/model/document.js b/model/document.js index fd88f3fd..7bd2b934 100644 --- a/model/document.js +++ b/model/document.js @@ -29,7 +29,7 @@ module.exports = class BaseDocument { // set value and trigger change async set(fieldname, value) { this[fieldname] = await this.validate_field(fieldname, value); - await this.trigger('change', {doc: this, fieldname: fieldname, value: value}); + await this.trigger('change', { doc: this, fieldname: fieldname, value: value }); } set_name() { @@ -70,9 +70,9 @@ module.exports = class BaseDocument { } } - async validate_field (key, value) { + async validate_field(key, value) { let field = this.meta.get_field(key); - if (field && field.fieldtype=='Select') { + if (field && field.fieldtype == 'Select') { return this.meta.validate_select(field, value); } return value; @@ -80,7 +80,7 @@ module.exports = class BaseDocument { get_valid_dict() { let doc = {}; - for(let field of this.meta.get_valid_fields()) { + for (let field of this.meta.get_valid_fields()) { doc[field.fieldname] = this.get(field.fieldname); } return doc; diff --git a/model/index.js b/model/index.js index f9295eab..1ae38470 100644 --- a/model/index.js +++ b/model/index.js @@ -1,9 +1,9 @@ module.exports = { - common_fields: [ + common_fields: [ { fieldname: 'name', fieldtype: 'Data', reqd: 1 } - ], + ], parent_fields: [ { fieldname: 'owner', fieldtype: 'Link', reqd: 1, options: 'User' diff --git a/model/meta.js b/model/meta.js index 9a3af731..eaeb0e56 100644 --- a/model/meta.js +++ b/model/meta.js @@ -8,7 +8,7 @@ module.exports = class BaseMeta extends BaseDocument { this.list_options = { fields: ['name', 'modified'] }; - if (this.setup_meta) { + if (this.setup_meta) { this.setup_meta(); } } @@ -21,14 +21,14 @@ module.exports = class BaseMeta extends BaseDocument { } } return this._field_map[fieldname]; - } + } - get_table_fields() { - if (!this._table_fields) { - this._table_fields = this.fields.filter(field => field.fieldtype==='Table'); - } - return this._table_fields; - } + get_table_fields() { + if (!this._table_fields) { + this._table_fields = this.fields.filter(field => field.fieldtype === 'Table'); + } + return this._table_fields; + } on(key, fn) { if (!this.event_handlers[key]) { @@ -46,7 +46,7 @@ module.exports = class BaseMeta extends BaseDocument { return this[fieldname]; } - get_valid_fields({with_children = true} = {}) { + get_valid_fields({ with_children = true } = {}) { if (!this._valid_fields) { this._valid_fields = []; @@ -60,29 +60,29 @@ module.exports = class BaseMeta extends BaseDocument { } if (this.is_child) { - // child fields - for (let field of frappe.model.child_fields) { + // child fields + for (let field of frappe.model.child_fields) { if (frappe.db.type_map[field.fieldtype] && !doctype_fields.includes(field.fieldname)) { this._valid_fields.push(field); } } } else { - // parent fields - for (let field of frappe.model.parent_fields) { + // parent fields + for (let field of frappe.model.parent_fields) { if (frappe.db.type_map[field.fieldtype] && !doctype_fields.includes(field.fieldname)) { this._valid_fields.push(field); } } - } + } // doctype fields for (let field of this.fields) { - let include = frappe.db.type_map[field.fieldtype]; + let include = frappe.db.type_map[field.fieldtype]; - // include tables if (with_children = True) - if (!include && with_children) { - include = field.fieldtype === 'Table'; - } + // include tables if (with_children = True) + if (!include && with_children) { + include = field.fieldtype === 'Table'; + } if (include) { this._valid_fields.push(field); }