swaf/dist/db/Model.js

291 lines
37 KiB
JavaScript
Raw Normal View History

2020-04-22 15:52:17 +02:00
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTW9kZWwuanMiLCJzb3VyY2VSb290IjoiLi8iLCJzb3VyY2VzIjpbImRiL01vZGVsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUFBLE9BQU8sc0JBQXNCLEVBQUUsRUFBQyxLQUFLLEVBQUMsTUFBTSwwQkFBMEIsQ0FBQztBQUN2RSxPQUFPLFNBQVMsTUFBTSxhQUFhLENBQUM7QUFFcEMsT0FBTyxLQUFLLE1BQU0sU0FBUyxDQUFDO0FBRTVCLE9BQU8sVUFBVSxNQUFNLGVBQWUsQ0FBQztBQUV2QyxNQUFNLENBQUMsT0FBTyxPQUFnQixLQUFLO0lBbUYvQixZQUFtQixJQUFTO1FBTlQsZUFBVSxHQUF5QixFQUFFLENBQUM7UUFDeEMsY0FBUyxHQUFvQyxFQUFFLENBQUM7UUFNN0QsSUFBSSxDQUFDLGNBQWMsQ0FBUyxJQUFJLEVBQUUsSUFBSSxTQUFTLEVBQUUsQ0FBQyxDQUFDO1FBQ25ELElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1FBQ3hCLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDOUIsQ0FBQztJQXRGTSxNQUFNLENBQU8sT0FBTyxDQUFrQixFQUFVOztZQUNuRCxNQUFNLFdBQVcsR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDbkQsSUFBSSxDQUFBLFdBQVcsYUFBWCxXQUFXLHVCQUFYLFdBQVcsQ0FBRSxXQUFXLE1BQUssSUFBSSxFQUFFO2dCQUNuQyxPQUFVLFdBQVcsQ0FBQzthQUN6QjtZQUVELE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBSSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO1lBQzNFLE9BQU8sTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENB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