mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 20:30:33 +00:00
add server to monorepo.
This commit is contained in:
281
packages/server/src/models/Metable.ts
Normal file
281
packages/server/src/models/Metable.ts
Normal file
@@ -0,0 +1,281 @@
|
||||
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;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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));
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user