2017-05-10 21:51:43 +02:00
|
|
|
import { Log } from 'src/log.js';
|
|
|
|
import { Database } from 'src/database.js';
|
2017-05-14 23:51:45 +02:00
|
|
|
import { uuid } from 'src/uuid.js';
|
2017-05-10 21:51:43 +02:00
|
|
|
|
2017-05-08 00:20:34 +02:00
|
|
|
class BaseModel {
|
|
|
|
|
2017-05-10 21:51:43 +02:00
|
|
|
static tableName() {
|
|
|
|
throw new Error('Must be overriden');
|
|
|
|
}
|
|
|
|
|
2017-05-12 21:54:06 +02:00
|
|
|
static useUuid() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-18 21:58:01 +02:00
|
|
|
static itemType() {
|
|
|
|
throw new Error('Must be overriden');
|
|
|
|
}
|
|
|
|
|
|
|
|
static trackChanges() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-15 21:10:00 +02:00
|
|
|
static byId(items, id) {
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
|
|
if (items[i].id == id) return items[i];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-05-19 21:32:49 +02:00
|
|
|
static hasField(name) {
|
|
|
|
let fields = this.fieldNames();
|
|
|
|
return fields.indexOf(name) >= 0;
|
|
|
|
}
|
|
|
|
|
2017-05-18 21:58:01 +02:00
|
|
|
static fieldNames() {
|
|
|
|
return this.db().tableFieldNames(this.tableName());
|
|
|
|
}
|
|
|
|
|
2017-05-20 00:16:50 +02:00
|
|
|
static fields() {
|
|
|
|
return this.db().tableFields(this.tableName());
|
|
|
|
}
|
|
|
|
|
|
|
|
static new() {
|
|
|
|
let fields = this.fields();
|
|
|
|
let output = {};
|
|
|
|
for (let i = 0; i < fields.length; i++) {
|
|
|
|
let f = fields[i];
|
|
|
|
output[f.name] = f.default;
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2017-05-18 21:58:01 +02:00
|
|
|
static fromApiResult(apiResult) {
|
|
|
|
let fieldNames = this.fieldNames();
|
|
|
|
let output = {};
|
|
|
|
for (let i = 0; i < fieldNames.length; i++) {
|
|
|
|
let f = fieldNames[i];
|
|
|
|
output[f] = f in apiResult ? apiResult[f] : null;
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2017-05-18 22:31:40 +02:00
|
|
|
static modOptions(options) {
|
|
|
|
if (!options) {
|
|
|
|
options = {};
|
|
|
|
} else {
|
|
|
|
options = Object.assign({}, options);
|
|
|
|
}
|
|
|
|
if (!('trackChanges' in options)) options.trackChanges = true;
|
|
|
|
if (!('isNew' in options)) options.isNew = 'auto';
|
|
|
|
return options;
|
|
|
|
}
|
|
|
|
|
2017-05-19 21:12:09 +02:00
|
|
|
static load(id) {
|
|
|
|
return this.db().selectOne('SELECT * FROM ' + this.tableName() + ' WHERE id = ?', [id]);
|
|
|
|
}
|
|
|
|
|
2017-05-19 21:32:49 +02:00
|
|
|
static applyPatch(model, patch) {
|
|
|
|
model = Object.assign({}, model);
|
|
|
|
for (let n in patch) {
|
|
|
|
if (!patch.hasOwnProperty(n)) continue;
|
|
|
|
model[n] = patch[n];
|
|
|
|
}
|
|
|
|
return model;
|
|
|
|
}
|
|
|
|
|
2017-05-23 22:36:19 +02:00
|
|
|
static diffObjects(oldModel, newModel) {
|
|
|
|
let output = {};
|
|
|
|
for (let n in newModel) {
|
|
|
|
if (!newModel.hasOwnProperty(n)) continue;
|
|
|
|
if (!(n in oldModel) || newModel[n] !== oldModel[n]) {
|
|
|
|
output[n] = newModel[n];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2017-05-18 21:58:01 +02:00
|
|
|
static saveQuery(o, isNew = 'auto') {
|
2017-05-18 22:31:40 +02:00
|
|
|
if (isNew == 'auto') isNew = !o.id;
|
2017-05-20 00:16:50 +02:00
|
|
|
|
|
|
|
let temp = {}
|
|
|
|
let fieldNames = this.fieldNames();
|
|
|
|
for (let i = 0; i < fieldNames.length; i++) {
|
|
|
|
let n = fieldNames[i];
|
|
|
|
if (n in o) temp[n] = o[n];
|
|
|
|
}
|
|
|
|
o = temp;
|
|
|
|
|
2017-05-12 21:54:06 +02:00
|
|
|
let query = '';
|
2017-05-18 21:58:01 +02:00
|
|
|
let itemId = o.id;
|
2017-05-12 21:54:06 +02:00
|
|
|
|
2017-05-19 21:32:49 +02:00
|
|
|
if (!o.updated_time && this.hasField('updated_time')) {
|
|
|
|
o.updated_time = Math.round((new Date()).getTime() / 1000);
|
|
|
|
}
|
|
|
|
|
2017-05-11 22:14:01 +02:00
|
|
|
if (isNew) {
|
2017-05-19 21:12:09 +02:00
|
|
|
if (this.useUuid() && !o.id) {
|
2017-05-18 21:58:01 +02:00
|
|
|
o = Object.assign({}, o);
|
|
|
|
itemId = uuid.create();
|
|
|
|
o.id = itemId;
|
|
|
|
}
|
2017-05-19 21:32:49 +02:00
|
|
|
|
|
|
|
if (!o.created_time && this.hasField('created_time')) {
|
|
|
|
o.created_time = Math.round((new Date()).getTime() / 1000);
|
|
|
|
}
|
|
|
|
|
2017-05-12 21:54:06 +02:00
|
|
|
query = Database.insertQuery(this.tableName(), o);
|
2017-05-11 22:14:01 +02:00
|
|
|
} else {
|
2017-05-12 21:54:06 +02:00
|
|
|
let where = { id: o.id };
|
|
|
|
let temp = Object.assign({}, o);
|
|
|
|
delete temp.id;
|
|
|
|
query = Database.updateQuery(this.tableName(), temp, where);
|
2017-05-11 22:14:01 +02:00
|
|
|
}
|
2017-05-12 21:54:06 +02:00
|
|
|
|
2017-05-18 21:58:01 +02:00
|
|
|
query.id = itemId;
|
|
|
|
|
2017-05-20 00:16:50 +02:00
|
|
|
Log.info('Saving', o);
|
|
|
|
|
2017-05-18 21:58:01 +02:00
|
|
|
return query;
|
|
|
|
}
|
|
|
|
|
2017-05-18 22:31:40 +02:00
|
|
|
static save(o, options = null) {
|
|
|
|
options = this.modOptions(options);
|
|
|
|
|
|
|
|
let isNew = options.isNew == 'auto' ? !o.id : options.isNew;
|
2017-05-18 21:58:01 +02:00
|
|
|
let query = this.saveQuery(o, isNew);
|
|
|
|
|
|
|
|
return this.db().transaction((tx) => {
|
|
|
|
tx.executeSql(query.sql, query.params);
|
|
|
|
|
2017-05-18 22:31:40 +02:00
|
|
|
if (options.trackChanges && this.trackChanges()) {
|
2017-05-18 21:58:01 +02:00
|
|
|
// Cannot import this class the normal way due to cyclical dependencies between Change and BaseModel
|
|
|
|
// which are not handled by React Native.
|
|
|
|
const { Change } = require('src/models/change.js');
|
|
|
|
|
2017-05-19 21:12:09 +02:00
|
|
|
if (isNew) {
|
|
|
|
let change = Change.newChange();
|
|
|
|
change.type = Change.TYPE_CREATE;
|
|
|
|
change.item_id = query.id;
|
|
|
|
change.item_type = this.itemType();
|
|
|
|
|
|
|
|
let changeQuery = Change.saveQuery(change);
|
|
|
|
tx.executeSql(changeQuery.sql, changeQuery.params);
|
|
|
|
} else {
|
|
|
|
for (let n in o) {
|
|
|
|
if (!o.hasOwnProperty(n)) continue;
|
2017-05-24 23:09:58 +02:00
|
|
|
if (n == 'id') continue;
|
2017-05-19 21:12:09 +02:00
|
|
|
|
|
|
|
let change = Change.newChange();
|
|
|
|
change.type = Change.TYPE_UPDATE;
|
|
|
|
change.item_id = query.id;
|
|
|
|
change.item_type = this.itemType();
|
|
|
|
change.item_field = n;
|
|
|
|
|
|
|
|
let changeQuery = Change.saveQuery(change);
|
|
|
|
tx.executeSql(changeQuery.sql, changeQuery.params);
|
|
|
|
}
|
|
|
|
}
|
2017-05-18 21:58:01 +02:00
|
|
|
}
|
2017-05-21 21:55:01 +02:00
|
|
|
}).then((r) => {
|
2017-05-18 21:58:01 +02:00
|
|
|
o = Object.assign({}, o);
|
|
|
|
o.id = query.id;
|
|
|
|
return o;
|
2017-05-21 21:55:01 +02:00
|
|
|
}).catch((error) => {
|
|
|
|
Log.error('Cannot save model', error);
|
2017-05-18 21:58:01 +02:00
|
|
|
});
|
2017-05-10 21:51:43 +02:00
|
|
|
}
|
|
|
|
|
2017-05-18 22:31:40 +02:00
|
|
|
static delete(id, options = null) {
|
|
|
|
options = this.modOptions(options);
|
|
|
|
|
2017-05-16 22:25:19 +02:00
|
|
|
if (!id) {
|
|
|
|
Log.warn('Cannot delete object without an ID');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-18 22:31:40 +02:00
|
|
|
return this.db().exec('DELETE FROM ' + this.tableName() + ' WHERE id = ?', [id]).then(() => {
|
2017-05-19 21:12:09 +02:00
|
|
|
if (options.trackChanges && this.trackChanges()) {
|
|
|
|
const { Change } = require('src/models/change.js');
|
2017-05-18 22:31:40 +02:00
|
|
|
|
2017-05-19 21:12:09 +02:00
|
|
|
let change = Change.newChange();
|
|
|
|
change.type = Change.TYPE_DELETE;
|
|
|
|
change.item_id = id;
|
|
|
|
change.item_type = this.itemType();
|
2017-05-18 22:31:40 +02:00
|
|
|
|
2017-05-19 21:12:09 +02:00
|
|
|
return Change.save(change);
|
|
|
|
}
|
2017-05-18 22:31:40 +02:00
|
|
|
});
|
2017-05-16 22:25:19 +02:00
|
|
|
}
|
|
|
|
|
2017-05-10 21:51:43 +02:00
|
|
|
static db() {
|
2017-05-20 00:16:50 +02:00
|
|
|
if (!this.db_) throw new Error('Accessing database before it has been initialised');
|
|
|
|
return this.db_;
|
2017-05-10 21:51:43 +02:00
|
|
|
}
|
|
|
|
|
2017-05-08 00:20:34 +02:00
|
|
|
}
|
|
|
|
|
2017-06-06 22:01:43 +02:00
|
|
|
BaseModel.ITEM_TYPE_NOTE = 1;
|
|
|
|
BaseModel.ITEM_TYPE_FOLDER = 2;
|
|
|
|
BaseModel.tableInfo_ = null;
|
|
|
|
BaseModel.tableKeys_ = null;
|
|
|
|
BaseModel.db_ = null;
|
|
|
|
|
2017-05-08 00:20:34 +02:00
|
|
|
export { BaseModel };
|