mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 21:00:31 +00:00
WIP Metadata class.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import bookshelf from './bookshelf';
|
||||
|
||||
const Account = bookshelf.Model.extend({
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
@@ -11,6 +10,17 @@ const Account = bookshelf.Model.extend({
|
||||
* Timestamp columns.
|
||||
*/
|
||||
hasTimestamps: ['created_at', 'updated_at'],
|
||||
|
||||
/**
|
||||
* Account model may belongs to account type.
|
||||
*/
|
||||
type() {
|
||||
return this.belongsTo('AccountType', 'account_type_id');
|
||||
},
|
||||
|
||||
balances() {
|
||||
return this.hasMany('AccountBalance', 'accounnt_id');
|
||||
}
|
||||
});
|
||||
|
||||
export default bookshelf.model('Account', Account);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import bookshelf from './bookshelf';
|
||||
|
||||
const AccountBalance = bookshelf.Model.extend({
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
tableName: 'account_balance',
|
||||
|
||||
/**
|
||||
* Timestamp columns.
|
||||
*/
|
||||
hasTimestamps: false,
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
account() {
|
||||
return this.belongsTo('Account', 'account_id');
|
||||
},
|
||||
});
|
||||
|
||||
export default bookshelf.model('AccountBalance', AccountBalance);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import bookshelf from './bookshelf';
|
||||
|
||||
const AccountType = bookshelf.Model.extend({
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
tableName: 'accounts',
|
||||
|
||||
/**
|
||||
* Timestamp columns.
|
||||
*/
|
||||
hasTimestamps: false,
|
||||
|
||||
/**
|
||||
* Account type may has many associated accounts.
|
||||
*/
|
||||
accounts() {
|
||||
return this.hasMany('Account', 'account_type_id');
|
||||
},
|
||||
});
|
||||
|
||||
export default bookshelf.model('AccountType', AccountType);
|
||||
|
||||
38
server/src/models/Auth.js
Normal file
38
server/src/models/Auth.js
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
export default class Auth {
|
||||
/**
|
||||
* Retrieve the authenticated user.
|
||||
*/
|
||||
static get user() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authenticated user.
|
||||
* @param {User} user
|
||||
*/
|
||||
static setAuthenticatedUser(user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the authenticated user ID.
|
||||
*/
|
||||
static userId() {
|
||||
if (!this.user) {
|
||||
return false;
|
||||
}
|
||||
return this.user.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user is logged or not.
|
||||
*/
|
||||
static isLogged() {
|
||||
return !!this.user;
|
||||
}
|
||||
|
||||
static loggedOut() {
|
||||
this.user = null;
|
||||
}
|
||||
}
|
||||
290
server/src/models/Metable.js
Normal file
290
server/src/models/Metable.js
Normal file
@@ -0,0 +1,290 @@
|
||||
import knex from '@/database/knex';
|
||||
// import cache from 'memory-cache';
|
||||
|
||||
// Metadata
|
||||
export default {
|
||||
METADATA_GROUP: 'default',
|
||||
KEY_COLUMN: 'key',
|
||||
VALUE_COLUMN: 'value',
|
||||
TYPE_COLUMN: 'type',
|
||||
|
||||
extraColumns: [],
|
||||
metadata: [],
|
||||
shouldReload: true,
|
||||
extraMetadataQuery: () => {},
|
||||
|
||||
/**
|
||||
* Set the value column key to query from.
|
||||
* @param {String} name -
|
||||
*/
|
||||
setKeyColumnName(name) {
|
||||
this.KEY_COLUMN = name;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the key column name to query from.
|
||||
* @param {String} name -
|
||||
*/
|
||||
setValueColumnName(name) {
|
||||
this.VALUE_COLUMN = name;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set extra columns to be added to the rows.
|
||||
* @param {Array} columns -
|
||||
*/
|
||||
setExtraColumns(columns) {
|
||||
this.extraColumns = columns;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the cache namespace.
|
||||
*/
|
||||
getCacheNamespace() {
|
||||
const { metadataCacheNamespace: cacheName } = this;
|
||||
return typeof cacheName === 'function'
|
||||
? cacheName() : cacheName;
|
||||
},
|
||||
|
||||
/**
|
||||
* Metadata database query.
|
||||
* @param {Object} query -
|
||||
* @param {String} groupName -
|
||||
*/
|
||||
whereQuery(query, key) {
|
||||
const groupName = this.METADATA_GROUP;
|
||||
|
||||
if (groupName) {
|
||||
query.where('group', groupName);
|
||||
}
|
||||
if (key) {
|
||||
if (Array.isArray(key)) {
|
||||
query.whereIn('key', key);
|
||||
} else {
|
||||
query.where('key', key);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads the metadata from the storage.
|
||||
* @param {String|Array} key -
|
||||
* @param {Boolean} force -
|
||||
*/
|
||||
async load(force = false) {
|
||||
if (this.shouldReload || force) {
|
||||
const metadataCollection = await this.query((query) => {
|
||||
this.whereQuery(query);
|
||||
this.extraMetadataQuery(query);
|
||||
}).fetchAll();
|
||||
|
||||
this.shouldReload = false;
|
||||
this.metadata = [];
|
||||
|
||||
const metadataArray = this.mapMetadataCollection(metadataCollection);
|
||||
metadataArray.forEach((metadata) => { this.metadata.push(metadata); });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches all the metadata that associate with the current group.
|
||||
*/
|
||||
async allMeta(force = false) {
|
||||
await this.load(force);
|
||||
return this.metadata;
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the given metadata key.
|
||||
* @param {String} key -
|
||||
* @return {object} - Metadata object.
|
||||
*/
|
||||
findMeta(key) {
|
||||
return this.metadata.find((meta) => meta.key === key);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch the metadata of the current group.
|
||||
* @param {*} key -
|
||||
*/
|
||||
async getMeta(key, defaultValue, force = false) {
|
||||
await this.load(force);
|
||||
|
||||
const metadata = this.findMeta(key);
|
||||
return metadata ? metadata.value : defaultValue || false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Markes the metadata to should be deleted.
|
||||
* @param {String} key -
|
||||
*/
|
||||
async removeMeta(key) {
|
||||
await this.load();
|
||||
const metadata = this.findMeta(key);
|
||||
|
||||
if (metadata) {
|
||||
metadata.markAsDeleted = true;
|
||||
}
|
||||
this.shouldReload = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all meta data of the given group.
|
||||
* @param {*} group
|
||||
*/
|
||||
removeAllMeta(group = 'default') {
|
||||
this.metdata.map((meta) => ({
|
||||
...(meta.group !== group) ? { markAsDeleted: true } : {},
|
||||
...meta,
|
||||
}));
|
||||
this.shouldReload = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the meta data to the stack.
|
||||
* @param {String} key -
|
||||
* @param {String} value -
|
||||
*/
|
||||
async setMeta(key, value, payload) {
|
||||
if (Array.isArray(key)) {
|
||||
const metadata = key;
|
||||
metadata.forEach((meta) => {
|
||||
this.setMeta(meta.key, meta.value);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await this.load();
|
||||
const metadata = this.findMeta(key);
|
||||
|
||||
if (metadata) {
|
||||
metadata.value = value;
|
||||
metadata.markAsUpdated = true;
|
||||
} else {
|
||||
this.metadata.push({
|
||||
value, key, ...payload, markAsInserted: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Saved the modified metadata.
|
||||
*/
|
||||
async saveMeta() {
|
||||
const inserted = this.metadata.filter((m) => (m.markAsInserted === true));
|
||||
const updated = this.metadata.filter((m) => (m.markAsUpdated === true));
|
||||
const deleted = this.metadata.filter((m) => (m.markAsDeleted === true));
|
||||
|
||||
const metadataDeletedKeys = deleted.map((m) => m.key);
|
||||
const metadataInserted = inserted.map((m) => this.mapMetadata(m, 'format'));
|
||||
const metadataUpdated = updated.map((m) => this.mapMetadata(m, 'format'));
|
||||
|
||||
const batchUpdate = (collection) => knex.transaction((trx) => {
|
||||
const queries = collection.map((tuple) => {
|
||||
const query = knex(this.tableName);
|
||||
this.whereQuery(query, tuple.key);
|
||||
this.extraMetadataQuery(query);
|
||||
return query.update(tuple).transacting(trx);
|
||||
});
|
||||
return Promise.all(queries).then(trx.commit).catch(trx.rollback);
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
knex.insert(metadataInserted).into(this.tableName),
|
||||
batchUpdate(metadataUpdated),
|
||||
metadataDeletedKeys.length > 0
|
||||
? this.query('whereIn', this.KEY_COLUMN, metadataDeletedKeys).destroy({
|
||||
require: true,
|
||||
}) : null,
|
||||
]);
|
||||
this.shouldReload = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Purge all the cached metadata in the memory.
|
||||
*/
|
||||
purgeMetadata() {
|
||||
this.metadata = [];
|
||||
this.shouldReload = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses the metadata value.
|
||||
* @param {String} value -
|
||||
* @param {String} valueType -
|
||||
*/
|
||||
parseMetaValue(value, valueType) {
|
||||
let parsedValue;
|
||||
|
||||
switch (valueType) {
|
||||
case 'integer':
|
||||
parsedValue = parseInt(value, 10);
|
||||
break;
|
||||
case 'float':
|
||||
parsedValue = parseFloat(value);
|
||||
break;
|
||||
case 'boolean':
|
||||
parsedValue = Boolean(value);
|
||||
break;
|
||||
case 'json':
|
||||
parsedValue = JSON.parse(parsedValue);
|
||||
break;
|
||||
default:
|
||||
parsedValue = value;
|
||||
break;
|
||||
}
|
||||
return parsedValue;
|
||||
},
|
||||
|
||||
/**
|
||||
* Format the metadata before saving to the database.
|
||||
* @param {String|Number|Boolean} value -
|
||||
* @param {String} valueType -
|
||||
* @return {String|Number|Boolean} -
|
||||
*/
|
||||
formatMetaValue(value, valueType) {
|
||||
let parsedValue;
|
||||
|
||||
switch (valueType) {
|
||||
case 'number':
|
||||
parsedValue = `${value}`;
|
||||
break;
|
||||
case 'boolean':
|
||||
parsedValue = value ? '1' : '0';
|
||||
break;
|
||||
case 'json':
|
||||
parsedValue = JSON.stringify(parsedValue);
|
||||
break;
|
||||
default:
|
||||
parsedValue = value;
|
||||
break;
|
||||
}
|
||||
return parsedValue;
|
||||
},
|
||||
|
||||
mapMetadata(attr, parseType = 'parse') {
|
||||
return {
|
||||
key: attr[this.KEY_COLUMN],
|
||||
value: (parseType === 'parse')
|
||||
? this.parseMetaValue(
|
||||
attr[this.VALUE_COLUMN],
|
||||
this.TYPE_COLUMN ? attr[this.TYPE_COLUMN] : false,
|
||||
)
|
||||
: this.formatMetaValue(
|
||||
attr[this.VALUE_COLUMN],
|
||||
this.TYPE_COLUMN ? attr[this.TYPE_COLUMN] : false,
|
||||
),
|
||||
...this.extraColumns.map((extraCol) => ({
|
||||
[extraCol]: attr[extraCol] || null,
|
||||
})),
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse the metadata collection.
|
||||
* @param {Array} collection -
|
||||
*/
|
||||
mapMetadataCollection(collection, parseType = 'parse') {
|
||||
return collection.map((model) => this.mapMetadata(model.attributes, parseType));
|
||||
},
|
||||
};
|
||||
@@ -1,7 +1,8 @@
|
||||
import bookshelf from './bookshelf';
|
||||
import Metable from './Metable';
|
||||
import Auth from './Auth';
|
||||
|
||||
const Setting = bookshelf.Model.extend({
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
@@ -11,6 +12,23 @@ const Setting = bookshelf.Model.extend({
|
||||
* Timestamp columns.
|
||||
*/
|
||||
hasTimestamps: false,
|
||||
|
||||
/**
|
||||
* Extra metadata query to query with the current authenticate user.
|
||||
* @param {Object} query
|
||||
*/
|
||||
extraMetadataQuery(query) {
|
||||
if (Auth.isLogged()) {
|
||||
query.where('user_id', Auth.userId());
|
||||
}
|
||||
},
|
||||
}, {
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
tableName: 'settings',
|
||||
|
||||
...Metable,
|
||||
});
|
||||
|
||||
export default bookshelf.model('Setting', Setting);
|
||||
|
||||
Reference in New Issue
Block a user