Fix missing fields by default and fix model construction flow
This commit is contained in:
parent
4b6829bfd6
commit
c0dd48d064
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wms-core",
|
||||
"version": "0.19.0",
|
||||
"version": "0.19.1",
|
||||
"description": "Node web framework",
|
||||
"repository": "git@gitlab.com:ArisuOngaku/wms-core.git",
|
||||
"author": "Alice Gaudon <alice@gaudon.pro>",
|
||||
|
@ -1,6 +1,8 @@
|
||||
import config from "config";
|
||||
import {v4 as uuid} from "uuid";
|
||||
import Log from "./models/Log";
|
||||
import ModelFactory from "./db/ModelFactory";
|
||||
import {bufferToUUID} from "./Utils";
|
||||
|
||||
export default class Logger {
|
||||
private static logLevel: LogLevelKeys = <LogLevelKeys>config.get<string>('log_level');
|
||||
@ -71,19 +73,15 @@ export default class Logger {
|
||||
}
|
||||
}).join(' ');
|
||||
|
||||
const log = new Log({});
|
||||
log.setLevel(level);
|
||||
log.message = computedMsg;
|
||||
log.setError(error);
|
||||
|
||||
let logID = Buffer.alloc(16);
|
||||
uuid({}, logID);
|
||||
log.setLogID(logID);
|
||||
|
||||
const shouldSaveToDB = levelIndex <= LogLevel[this.dbLogLevel];
|
||||
|
||||
let output = `[${level}] `;
|
||||
let pad = output.length;
|
||||
if (levelIndex <= LogLevel[this.dbLogLevel]) output += `${log.getLogID()} - `;
|
||||
const pad = output.length;
|
||||
|
||||
const logID = Buffer.alloc(16);
|
||||
uuid({}, logID);
|
||||
if (shouldSaveToDB) output += `${logID} - `;
|
||||
|
||||
output += computedMsg.replace(/\n/g, '\n' + ' '.repeat(pad));
|
||||
|
||||
switch (level) {
|
||||
@ -106,14 +104,19 @@ export default class Logger {
|
||||
break;
|
||||
}
|
||||
|
||||
if (levelIndex <= LogLevel[this.dbLogLevel]) {
|
||||
if (shouldSaveToDB) {
|
||||
const log = ModelFactory.get(Log).make({});
|
||||
log.setLevel(level);
|
||||
log.message = computedMsg;
|
||||
log.setError(error);
|
||||
log.setLogID(logID);
|
||||
log.save().catch(err => {
|
||||
if (!silent && err.message.indexOf('ECONNREFUSED') < 0) {
|
||||
console.error({save_err: err, error});
|
||||
}
|
||||
});
|
||||
}
|
||||
return log.getLogID();
|
||||
return bufferToUUID(logID);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
12
src/Utils.ts
12
src/Utils.ts
@ -35,3 +35,15 @@ export function cryptoRandomDictionary(size: number, dictionary: string): string
|
||||
}
|
||||
|
||||
export type Type<T> = { new(...args: any[]): T };
|
||||
|
||||
export function bufferToUUID(buffer: Buffer): string {
|
||||
const chars = buffer.toString('hex');
|
||||
let out = '';
|
||||
let i = 0;
|
||||
for (const l of [8, 4, 4, 4, 12]) {
|
||||
if (i > 0) out += '-';
|
||||
out += chars.substr(i, l);
|
||||
i += l;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
@ -24,13 +24,14 @@ export default class MagicLink extends Model implements AuthProof {
|
||||
return config.get<number>('magic_link.validity_period') * 1000;
|
||||
}
|
||||
|
||||
private session_id?: string;
|
||||
private email?: string;
|
||||
private token?: string;
|
||||
private action_type?: string;
|
||||
private original_url?: string;
|
||||
private generated_at?: Date;
|
||||
private authorized?: boolean;
|
||||
public readonly id?: number = undefined;
|
||||
private session_id?: string = undefined;
|
||||
private email?: string = undefined;
|
||||
private token?: string = undefined;
|
||||
private action_type?: string = undefined;
|
||||
private original_url?: string = undefined;
|
||||
private generated_at?: Date = undefined;
|
||||
private authorized?: boolean = undefined;
|
||||
|
||||
constructor(data: any) {
|
||||
super(data);
|
||||
|
@ -12,10 +12,11 @@ export default class User extends Model {
|
||||
return config.get<boolean>('approval_mode') && MysqlConnectionManager.hasMigration(AddApprovedFieldToUsersTable);
|
||||
}
|
||||
|
||||
public name?: string;
|
||||
public is_admin!: boolean;
|
||||
public created_at?: Date;
|
||||
public updated_at?: Date;
|
||||
public readonly id?: number = undefined;
|
||||
public name?: string = undefined;
|
||||
public is_admin: boolean = false;
|
||||
public created_at?: Date = undefined;
|
||||
public updated_at?: Date = undefined;
|
||||
|
||||
public readonly emails = new ManyModelRelation(this, ModelFactory.get(UserEmail), {
|
||||
localKey: 'id',
|
||||
@ -26,8 +27,6 @@ export default class User extends Model {
|
||||
|
||||
public constructor(data: any) {
|
||||
super(data);
|
||||
if (this.approved === undefined) this.approved = false;
|
||||
if (this.is_admin === undefined) this.is_admin = false;
|
||||
}
|
||||
|
||||
protected init(): void {
|
||||
|
@ -2,5 +2,5 @@ import ModelComponent from "../../db/ModelComponent";
|
||||
import User from "./User";
|
||||
|
||||
export default class UserApprovedComponent extends ModelComponent<User> {
|
||||
public approved!: boolean;
|
||||
public approved: boolean = false;
|
||||
}
|
@ -6,10 +6,11 @@ import {OneModelRelation} from "../../db/ModelRelation";
|
||||
import ModelFactory from "../../db/ModelFactory";
|
||||
|
||||
export default class UserEmail extends Model {
|
||||
public user_id?: number;
|
||||
public readonly email!: string;
|
||||
private main!: boolean;
|
||||
public created_at?: Date;
|
||||
public readonly id?: number = undefined;
|
||||
public user_id?: number = undefined;
|
||||
public readonly email?: string = undefined;
|
||||
private main?: boolean = undefined;
|
||||
public created_at?: Date = undefined;
|
||||
|
||||
public readonly user = new OneModelRelation<UserEmail, User>(this, ModelFactory.get(User), {
|
||||
localKey: 'user_id',
|
||||
@ -36,7 +37,7 @@ export default class UserEmail extends Model {
|
||||
}
|
||||
|
||||
public isMain(): boolean {
|
||||
return this.main;
|
||||
return Boolean(this.main);
|
||||
}
|
||||
|
||||
public setMain() {
|
||||
|
@ -29,19 +29,16 @@ export default abstract class Model {
|
||||
return ModelFactory.get(this).paginate(request, perPage, query);
|
||||
}
|
||||
|
||||
protected readonly _factory!: ModelFactory<any>;
|
||||
protected readonly _factory: ModelFactory<any>;
|
||||
private readonly _components: ModelComponent<any>[] = [];
|
||||
private readonly _validators: { [key: string]: Validator<any> } = {};
|
||||
|
||||
[key: string]: any;
|
||||
|
||||
public constructor(data: any) {
|
||||
public constructor(factory: ModelFactory<any>) {
|
||||
if (!factory || !(factory instanceof ModelFactory)) throw new Error('Cannot instantiate model directly.');
|
||||
this._factory = factory;
|
||||
this.init();
|
||||
this.updateWithData(data);
|
||||
}
|
||||
|
||||
public setFactory(factory: ModelFactory<any>) {
|
||||
(this as any)._factory = factory;
|
||||
}
|
||||
|
||||
protected abstract init(): void;
|
||||
@ -66,7 +63,7 @@ export default abstract class Model {
|
||||
throw new Error(`Component ${type.name} was not initialized for this ${this.constructor.name}.`);
|
||||
}
|
||||
|
||||
private updateWithData(data: any) {
|
||||
public updateWithData(data: any) {
|
||||
for (const property of this._properties) {
|
||||
if (data[property] !== undefined) {
|
||||
this[property] = data[property];
|
||||
@ -128,7 +125,7 @@ export default abstract class Model {
|
||||
}
|
||||
}
|
||||
let query = this._factory.update(data);
|
||||
for (const indexField of this.getPrimaryKeyFields()) {
|
||||
for (const indexField of this._factory.getPrimaryKeyFields()) {
|
||||
query = query.where(indexField, this[indexField]);
|
||||
}
|
||||
await query.execute(connection);
|
||||
@ -154,7 +151,7 @@ export default abstract class Model {
|
||||
|
||||
public async exists(): Promise<boolean> {
|
||||
let query = this._factory.select('1');
|
||||
for (const indexField of this.getPrimaryKeyFields()) {
|
||||
for (const indexField of this._factory.getPrimaryKeyFields()) {
|
||||
query = query.where(indexField, this[indexField]);
|
||||
}
|
||||
return (await query.limit(1).execute()).results.length > 0;
|
||||
@ -164,7 +161,7 @@ export default abstract class Model {
|
||||
if (!(await this.exists())) throw new Error('This model instance doesn\'t exist in DB.');
|
||||
|
||||
let query = this._factory.delete();
|
||||
for (const indexField of this.getPrimaryKeyFields()) {
|
||||
for (const indexField of this._factory.getPrimaryKeyFields()) {
|
||||
query = query.where(indexField, this[indexField]);
|
||||
}
|
||||
await query.execute();
|
||||
|
@ -25,22 +25,21 @@ export default class ModelFactory<T extends Model> {
|
||||
this.modelType = modelType;
|
||||
}
|
||||
|
||||
|
||||
public addComponent(modelComponentFactory: ModelComponentFactory<T>) {
|
||||
this.components.push(modelComponentFactory);
|
||||
}
|
||||
|
||||
public make(data: any): T {
|
||||
const model = new this.modelType(data);
|
||||
const model = new this.modelType(this, data);
|
||||
for (const component of this.components) {
|
||||
model.addComponent(new component(model));
|
||||
}
|
||||
model.setFactory(this);
|
||||
model.updateWithData(data);
|
||||
return model;
|
||||
}
|
||||
|
||||
public get table(): string {
|
||||
return this.constructor.name
|
||||
return this.modelType.name
|
||||
.replace(/(?:^|\.?)([A-Z])/g, (x, y) => '_' + y.toLowerCase())
|
||||
.replace(/^_/, '')
|
||||
+ 's';
|
||||
|
@ -1,14 +1,16 @@
|
||||
import Model from "../db/Model";
|
||||
import {LogLevel, LogLevelKeys} from "../Logger";
|
||||
import {bufferToUUID} from "../Utils";
|
||||
|
||||
export default class Log extends Model {
|
||||
private level?: number;
|
||||
public message?: string;
|
||||
private log_id?: Buffer;
|
||||
private error_name?: string;
|
||||
private error_message?: string;
|
||||
private error_stack?: string;
|
||||
private created_at?: Date;
|
||||
public readonly id?: number = undefined;
|
||||
private level?: number = undefined;
|
||||
public message?: string = undefined;
|
||||
private log_id?: Buffer = undefined;
|
||||
private error_name?: string = undefined;
|
||||
private error_message?: string = undefined;
|
||||
private error_stack?: string = undefined;
|
||||
private created_at?: Date = undefined;
|
||||
|
||||
protected init(): void {
|
||||
this.setValidation('level').defined();
|
||||
@ -29,16 +31,7 @@ export default class Log extends Model {
|
||||
}
|
||||
|
||||
public getLogID(): string | null {
|
||||
if (!this.log_id) return null;
|
||||
const chars = this.log_id!.toString('hex');
|
||||
let out = '';
|
||||
let i = 0;
|
||||
for (const l of [8, 4, 4, 4, 12]) {
|
||||
if (i > 0) out += '-';
|
||||
out += chars.substr(i, l);
|
||||
i += l;
|
||||
}
|
||||
return out;
|
||||
return this.log_id ? bufferToUUID(this.log_id!) : null;
|
||||
}
|
||||
|
||||
public setLogID(buffer: Buffer) {
|
||||
|
@ -1,22 +1,22 @@
|
||||
import MysqlConnectionManager from "../src/db/MysqlConnectionManager";
|
||||
import Model from "../src/db/Model";
|
||||
import Validator from "../src/db/Validator";
|
||||
import {MIGRATIONS} from "./_migrations";
|
||||
import ModelFactory from "../src/db/ModelFactory";
|
||||
|
||||
class FakeDummyModel extends Model {
|
||||
public name?: string;
|
||||
public date?: Date;
|
||||
public date_default?: Date;
|
||||
public id?: number = undefined;
|
||||
public name?: string = undefined;
|
||||
public date?: Date = undefined;
|
||||
public date_default?: Date = undefined;
|
||||
|
||||
protected init(): void {
|
||||
this.addProperty<string>('name', new Validator().acceptUndefined().between(3, 256));
|
||||
this.addProperty<Date>('date', new Validator());
|
||||
this.addProperty<Date>('date_default', new Validator());
|
||||
this.setValidation('name').acceptUndefined().between(3, 256);
|
||||
}
|
||||
}
|
||||
|
||||
beforeAll(async (done) => {
|
||||
MysqlConnectionManager.registerMigrations(MIGRATIONS);
|
||||
ModelFactory.register(FakeDummyModel);
|
||||
await MysqlConnectionManager.prepare();
|
||||
done();
|
||||
});
|
||||
@ -28,13 +28,15 @@ afterAll(async (done) => {
|
||||
|
||||
describe('Model', () => {
|
||||
it('should have a proper table name', async () => {
|
||||
expect(FakeDummyModel.table).toBe('fake_dummy_models');
|
||||
expect(new FakeDummyModel({}).table).toBe('fake_dummy_models');
|
||||
const factory = ModelFactory.get(FakeDummyModel);
|
||||
expect(factory.table).toBe('fake_dummy_models');
|
||||
expect(factory.make({}).table).toBe('fake_dummy_models');
|
||||
});
|
||||
|
||||
it('should insert and retrieve properly', async () => {
|
||||
await MysqlConnectionManager.query(`DROP TABLE IF EXISTS ${FakeDummyModel.table}`);
|
||||
await MysqlConnectionManager.query(`CREATE TABLE ${FakeDummyModel.table}(
|
||||
const factory = ModelFactory.get(FakeDummyModel);
|
||||
await MysqlConnectionManager.query(`DROP TABLE IF EXISTS ${(factory.table)}`);
|
||||
await MysqlConnectionManager.query(`CREATE TABLE ${(factory.table)}(
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
name VARCHAR(256),
|
||||
date DATETIME,
|
||||
@ -43,11 +45,12 @@ describe('Model', () => {
|
||||
)`);
|
||||
|
||||
const date = new Date(569985);
|
||||
let instance: FakeDummyModel | null = new FakeDummyModel({
|
||||
let instance: FakeDummyModel | null = factory.make({
|
||||
name: 'name1',
|
||||
date: date,
|
||||
});
|
||||
|
||||
console.log(instance)
|
||||
await instance.save();
|
||||
expect(instance.id).toBe(1);
|
||||
expect(instance.name).toBe('name1');
|
||||
|
Loading…
Reference in New Issue
Block a user