291 lines
37 KiB
JavaScript
291 lines
37 KiB
JavaScript
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||
|
});
|
||
|
};
|
||
|
import MysqlConnectionManager, { query } from "./MysqlConnectionManager";
|
||
|
import Validator from "./Validator";
|
||
|
import Query from "./Query";
|
||
|
import Pagination from "../Pagination";
|
||
|
export default class Model {
|
||
|
constructor(data) {
|
||
|
this.properties = [];
|
||
|
this.relations = {};
|
||
|
this.defineProperty('id', new Validator());
|
||
|
this.defineProperties();
|
||
|
this.updateWithData(data);
|
||
|
}
|
||
|
static getById(id) {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
const cachedModel = ModelCache.get(this.table, id);
|
||
|
if ((cachedModel === null || cachedModel === void 0 ? void 0 : cachedModel.constructor) === this) {
|
||
|
return cachedModel;
|
||
|
}
|
||
|
const models = yield this.models(this.select().where('id', id).first());
|
||
|
return models.length > 0 ? models[0] : null;
|
||
|
});
|
||
|
}
|
||
|
static paginate(request, perPage = 20) {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
let page = request.params.page ? parseInt(request.params.page) : 1;
|
||
|
let query = this.select().limit(perPage, (page - 1) * perPage).withTotalRowCount();
|
||
|
if (request.params.sortBy) {
|
||
|
const dir = request.params.sortDirection;
|
||
|
query = query.sortBy(request.params.sortBy, dir === 'ASC' || dir === 'DESC' ? dir : undefined);
|
||
|
}
|
||
|
else {
|
||
|
query = query.sortBy('id');
|
||
|
}
|
||
|
const models = yield this.models(query);
|
||
|
// @ts-ignore
|
||
|
models.pagination = new Pagination(models, page, perPage, models.totalCount);
|
||
|
return models;
|
||
|
});
|
||
|
}
|
||
|
static select(...fields) {
|
||
|
return Query.select(this.table, ...fields);
|
||
|
}
|
||
|
static update(data) {
|
||
|
return Query.update(this.table, data);
|
||
|
}
|
||
|
static delete() {
|
||
|
return Query.delete(this.table);
|
||
|
}
|
||
|
static models(query) {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
const results = yield query.execute();
|
||
|
const models = [];
|
||
|
const factory = this.getFactory();
|
||
|
for (const result of results.results) {
|
||
|
const cachedModel = ModelCache.get(this.table, result.id);
|
||
|
if (cachedModel && cachedModel.constructor === this) {
|
||
|
cachedModel.updateWithData(result);
|
||
|
models.push(cachedModel);
|
||
|
}
|
||
|
else {
|
||
|
models.push(factory(result));
|
||
|
}
|
||
|
}
|
||
|
// @ts-ignore
|
||
|
models.totalCount = results.foundRows;
|
||
|
return models;
|
||
|
});
|
||
|
}
|
||
|
static loadRelation(models, relation, model, localField) {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
const loadMap = {};
|
||
|
const ids = models.map(m => {
|
||
|
m.relations[relation] = null;
|
||
|
if (m[localField])
|
||
|
loadMap[m[localField]] = v => m.relations[relation] = v;
|
||
|
return m[localField];
|
||
|
}).filter(id => id);
|
||
|
for (const v of yield model.models(model.select().whereIn('id', ids))) {
|
||
|
loadMap[v.id](v);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
static getFactory(factory) {
|
||
|
if (factory === undefined) {
|
||
|
factory = this.FACTORY;
|
||
|
if (factory === undefined)
|
||
|
factory = data => new this(data);
|
||
|
}
|
||
|
return factory;
|
||
|
}
|
||
|
defineProperty(name, validator) {
|
||
|
if (validator === undefined)
|
||
|
validator = new Validator();
|
||
|
if (validator instanceof RegExp) {
|
||
|
const regexp = validator;
|
||
|
validator = new Validator().regexp(regexp);
|
||
|
}
|
||
|
const prop = new ModelProperty(name, validator);
|
||
|
this.properties.push(prop);
|
||
|
Object.defineProperty(this, name, {
|
||
|
get: () => prop.value,
|
||
|
set: (value) => prop.value = value,
|
||
|
});
|
||
|
}
|
||
|
updateWithData(data) {
|
||
|
this.id = data['id'];
|
||
|
for (const prop of this.properties) {
|
||
|
if (data[prop.name] !== undefined) {
|
||
|
this[prop.name] = data[prop.name];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
beforeSave(exists, connection) {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
});
|
||
|
}
|
||
|
afterSave() {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
});
|
||
|
}
|
||
|
save(connection, postHook) {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
yield this.validate(false, connection);
|
||
|
const exists = yield this.exists();
|
||
|
let needs_full_update = false;
|
||
|
if (connection) {
|
||
|
needs_full_update = yield this.saveTransaction(connection, exists, needs_full_update);
|
||
|
}
|
||
|
else {
|
||
|
needs_full_update = yield MysqlConnectionManager.wrapTransaction((connection) => __awaiter(this, void 0, void 0, function* () { return this.saveTransaction(connection, exists, needs_full_update); }));
|
||
|
}
|
||
|
const callback = () => __awaiter(this, void 0, void 0, function* () {
|
||
|
if (needs_full_update) {
|
||
|
this.updateWithData((yield this.constructor.select().where('id', this.id).first().execute()).results[0]);
|
||
|
}
|
||
|
if (!exists) {
|
||
|
this.cache();
|
||
|
}
|
||
|
yield this.afterSave();
|
||
|
});
|
||
|
if (connection) {
|
||
|
postHook(callback);
|
||
|
}
|
||
|
else {
|
||
|
yield callback();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
saveTransaction(connection, exists, needs_full_update) {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
// Before save
|
||
|
yield this.beforeSave(exists, connection);
|
||
|
if (exists && this.hasOwnProperty('updated_at')) {
|
||
|
this.updated_at = new Date();
|
||
|
}
|
||
|
const props = [];
|
||
|
const values = [];
|
||
|
if (exists) {
|
||
|
for (const prop of this.properties) {
|
||
|
if (prop.value !== undefined) {
|
||
|
props.push(prop.name + '=?');
|
||
|
values.push(prop.value);
|
||
|
}
|
||
|
else {
|
||
|
needs_full_update = true;
|
||
|
}
|
||
|
}
|
||
|
values.push(this.id);
|
||
|
yield query(`UPDATE ${this.table} SET ${props.join(',')} WHERE id=?`, values, connection);
|
||
|
}
|
||
|
else {
|
||
|
const props_holders = [];
|
||
|
for (const prop of this.properties) {
|
||
|
if (prop.value !== undefined) {
|
||
|
props.push(prop.name);
|
||
|
props_holders.push('?');
|
||
|
values.push(prop.value);
|
||
|
}
|
||
|
else {
|
||
|
needs_full_update = true;
|
||
|
}
|
||
|
}
|
||
|
const result = yield query(`INSERT INTO ${this.table} (${props.join(', ')}) VALUES(${props_holders.join(', ')})`, values, connection);
|
||
|
this.id = result.other.insertId;
|
||
|
}
|
||
|
return needs_full_update;
|
||
|
});
|
||
|
}
|
||
|
static get table() {
|
||
|
return this.name
|
||
|
.replace(/(?:^|\.?)([A-Z])/g, (x, y) => '_' + y.toLowerCase())
|
||
|
.replace(/^_/, '')
|
||
|
+ 's';
|
||
|
}
|
||
|
get table() {
|
||
|
// @ts-ignore
|
||
|
return this.constructor.table;
|
||
|
}
|
||
|
exists() {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
if (!this.id)
|
||
|
return false;
|
||
|
const result = yield query(`SELECT 1 FROM ${this.table} WHERE id=? LIMIT 1`, [
|
||
|
this.id,
|
||
|
]);
|
||
|
return result.results.length > 0;
|
||
|
});
|
||
|
}
|
||
|
delete() {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
if (!(yield this.exists()))
|
||
|
throw new Error('This model instance doesn\'t exist in DB.');
|
||
|
yield query(`DELETE FROM ${this.table} WHERE id=?`, [
|
||
|
this.id,
|
||
|
]);
|
||
|
ModelCache.forget(this);
|
||
|
this.id = undefined;
|
||
|
});
|
||
|
}
|
||
|
validate(onlyFormat = false, connection) {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
return yield Promise.all(this.properties.map(prop => prop.validate(onlyFormat, connection)));
|
||
|
});
|
||
|
}
|
||
|
cache() {
|
||
|
ModelCache.cache(this);
|
||
|
}
|
||
|
relation(name) {
|
||
|
if (this.relations[name] === undefined)
|
||
|
throw new Error('Model not loaded');
|
||
|
return this.relations[name];
|
||
|
}
|
||
|
}
|
||
|
class ModelProperty {
|
||
|
constructor(name, validator) {
|
||
|
this.name = name;
|
||
|
this.validator = validator;
|
||
|
}
|
||
|
validate(onlyFormat, connection) {
|
||
|
return __awaiter(this, void 0, void 0, function* () {
|
||
|
return yield this.validator.execute(this.name, this.value, onlyFormat, connection);
|
||
|
});
|
||
|
}
|
||
|
get value() {
|
||
|
return this.val;
|
||
|
}
|
||
|
set value(val) {
|
||
|
this.val = val;
|
||
|
}
|
||
|
}
|
||
|
export class ModelCache {
|
||
|
static cache(instance) {
|
||
|
if (instance.id === undefined)
|
||
|
throw new Error('Cannot cache an instance with an undefined id.');
|
||
|
let tableCache = this.caches[instance.table];
|
||
|
if (!tableCache)
|
||
|
tableCache = this.caches[instance.table] = {};
|
||
|
if (!tableCache[instance.id])
|
||
|
tableCache[instance.id] = instance;
|
||
|
}
|
||
|
static forget(instance) {
|
||
|
if (instance.id === undefined)
|
||
|
throw new Error('Cannot forget an instance with an undefined id.');
|
||
|
let tableCache = this.caches[instance.table];
|
||
|
if (!tableCache)
|
||
|
return;
|
||
|
if (tableCache[instance.id])
|
||
|
delete tableCache[instance.id];
|
||
|
}
|
||
|
static all(table) {
|
||
|
return this.caches[table];
|
||
|
}
|
||
|
static get(table, id) {
|
||
|
const tableCache = this.all(table);
|
||
|
if (!tableCache)
|
||
|
return undefined;
|
||
|
return tableCache[id];
|
||
|
}
|
||
|
}
|
||
|
ModelCache.caches = {};
|
||
|
export const EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/;
|
||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTW9kZWwuanMiLCJzb3VyY2VSb290IjoiLi8iLCJzb3VyY2VzIjpbImRiL01vZGVsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUFBLE9BQU8sc0JBQXNCLEVBQUUsRUFBQyxLQUFLLEVBQUMsTUFBTSwwQkFBMEIsQ0FBQztBQUN2RSxPQUFPLFNBQVMsTUFBTSxhQUFhLENBQUM7QUFFcEMsT0FBTyxLQUFLLE1BQU0sU0FBUyxDQUFDO0FBRTVCLE9BQU8sVUFBVSxNQUFNLGVBQWUsQ0FBQztBQUV2QyxNQUFNLENBQUMsT0FBTyxPQUFnQixLQUFLO0lBbUYvQixZQUFtQixJQUFTO1FBTlQsZUFBVSxHQUF5QixFQUFFLENBQUM7UUFDeEMsY0FBUyxHQUFvQyxFQUFFLENBQUM7UUFNN0QsSUFBSSxDQUFDLGNBQWMsQ0FBUyxJQUFJLEVBQUUsSUFBSSxTQUFTLEVBQUUsQ0FBQyxDQUFDO1FBQ25ELElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1FBQ3hCLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDOUIsQ0FBQztJQXRGTSxNQUFNLENBQU8sT0FBTyxDQUFrQixFQUFVOztZQUNuRCxNQUFNLFdBQVcsR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDbkQsSUFBSSxDQUFBLFdBQVcsYUFBWCxXQUFXLHVCQUFYLFdBQVcsQ0FBRSxXQUFXLE1BQUssSUFBSSxFQUFFO2dCQUNuQyxPQUFVLFdBQVcsQ0FBQzthQUN6QjtZQUVELE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBSSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO1lBQzNFLE9BQU8sTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO1FBQ2hELENBQUM7S0FBQTtJQUVNLE1BQU0sQ0FBTyxRQUFRLENBQWtCLE9BQWdCLEVBQUUsVUFBa0IsRUFBRTs7WUFDaEYsSUFBSSxJQUFJLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDbkUsSUFBSSxLQUFLLEdBQVUsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDLEdBQUcsT0FBTyxDQUFDLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUMxRixJQUFJLE9BQU8sQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFO2dCQUN2QixNQUFNLEdBQUcsR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQztnQkFDekMsS0FBSyxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsR0FBRyxLQUFLLEtBQUssSUFBSSxHQUFHLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDO2FBQ2xHO2lCQUFNO2dCQUNILEtBQUssR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO2FBQzlCO1lBQ0QsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFJLEtBQUssQ0FBQyxDQUFDO1lBQzNDLGFBQWE7WUFDYixNQUFNLENBQUMsVUFBVSxHQUFHLElBQUksVUFBVSxDQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUM3RSxPQUFPLE1BQU0sQ0FBQztRQUNsQixDQUFDO0tBQUE7SUFFUyxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsTUFBZ0I7UUFDdkMsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsR0FBRyxNQUFNLENBQUMsQ0FBQztJQUMvQyxDQUFDO0lBRVMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUE0QjtRQUNoRCxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsQ0FBQztJQUMxQyxDQUFDO0lBRVMsTUFBTSxDQUFDLE1BQU07UUFDbkIsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNwQyxDQUFDO0lBRVMsTUFBTSxDQUFPLE1BQU0sQ0FBa0IsS0FBWTs7WUFDdkQsTUFBTSxPQUFPLEdBQUcsTUFBTSxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDdEMsTUFBTSxNQUFNLEdBQVEsRUFBRSxDQUFDO1lBQ3ZCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxVQUFVLEVBQUssQ0FBQztZQUNyQyxLQUFLLE1BQU0sTUFBTSxJQUFJLE9BQU8sQ0FBQyxPQUFPLEVBQUU7Z0JBQ2xDLE1BQU0sV0FBVyxHQUFHLFVBQVUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUM7Z0JBQzFELElBQUksV0FBVyxJQUFJLFdBQVcsQ0FBQyxXQUFXLEtBQUssSUFBSSxFQUFFO29CQUNqRCxXQUFXLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO29CQUNuQyxNQUFNLENBQUMsSUFBSSxDQUFJLFdBQVcsQ0FBQyxDQUFDO2lCQUMvQjtxQkFBTTtvQkFDSCxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO2lCQUNoQzthQUNKO1lBQ0QsYUFBYTtZQUNiLE1BQU0sQ0FBQyxVQUFVLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQztZQUN0QyxPQUFPLE1BQU0sQ0FBQztRQUNsQixDQUFDO0tBQUE7SUFFTSxNQUFNLENBQU8sWUFBWSxDQUFrQixNQUFXLEVBQUUsUUFBZ0IsRUFBRSxLQUFlLEVBQUUsVUFBa0I7O1lBQ2hILE1BQU0sT0FBTyxHQUF3QyxFQUFFLENBQUM7WUFDeEQsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRTtnQkFDdkIsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzdCLElBQUksQ0FBQyxDQUFDLFVBQVUsQ0FBQztvQkFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDM0UsT
|