From 2226490f0304fd5152864042a0c5272d8b1ac499 Mon Sep 17 00:00:00 2001 From: DimaCrafter Date: Thu, 23 Feb 2023 21:55:23 +0400 Subject: [PATCH] Added experimental DB model syntax --- db.d.ts | 31 +++++++++++-- db.js | 128 +++++++++++++++++++++++++++++++++++++++------------ package.json | 2 +- 3 files changed, 127 insertions(+), 34 deletions(-) diff --git a/db.d.ts b/db.d.ts index f6bceac..2ef8540 100644 --- a/db.d.ts +++ b/db.d.ts @@ -18,14 +18,39 @@ interface DatabaseDriverStatic { import { EventEmitter } from 'events' export abstract class DatabaseDriver extends EventEmitter { public _name: string; + protected constructor (); public abstract constructor (options: object); public abstract connect (): Promise; - public abstract getModel (basePath: string, name: string): ModelType; + public abstract getModel (basePath: string, name: string, schema: any): ModelType; emit (event: 'disconnected'): void; } -type ExtractModelType = Instance extends DatabaseDriver ? ModelType : never; -export function registerDriver (driver: DriverClassType, driverName: string): DatabaseDriverStatic, ExtractModelType> & DriverClassType; +type ExtractModelType = Instance extends DatabaseDriver ? ModelType : never; +export function registerDriver (driver: DriverClass, driverName: string): DatabaseDriverStatic, ExtractModelType> & DriverClass; export function connect (configKey: string, options?: ConnectionOptions): DatabaseConnection; + +interface ModelFieldInfo { + type: 'string' | 'text' | 'int' | 'enum' +} + +class ModelField { + readonly info: ModelFieldInfo; + get required (): this; + default (value: T): this; +} + +class ModelString extends ModelField {} +export function string (length?: number): ModelString; + +class ModelText extends ModelField { + get json (): this; +} +export function text (): ModelText; + +class ModelInt extends ModelField {} +export function int (): ModelInt; + +class ModelEnum extends ModelField {} +export function enumerable (...values: V): ModelEnum; diff --git a/db.js b/db.js index dcc5ea5..a7ab485 100644 --- a/db.js +++ b/db.js @@ -1,38 +1,27 @@ +const Path = require('path'); const { emitError } = require('./errors'); const config = require('./config'); const log = require('./log'); -const Path = require('path'); - -function connect (connector, attempt = 0) { - connector.connect().then( - () => { - log.success(`Connected to ${connector._name} database`); - attempt = 0; - }, - error => { - log.error(`Connection to ${connector._name} database was failed`, error); - emitError({ - isSystem: true, - type: 'DatabaseConnectionError', - name: connector._name, - error - }); - - setTimeout(() => { - log.info(`Reconnecting to ${connector._name}... Attempt #${attempt}`); - connect(connector, attempt + 1); - }, 5000); - } - ); - connector.on('disconnected', () => { - log.error(`Disconnected from ${connector._name} database`); +async function connect (connector, attempt = 0) { + try { + await connector.connect(); + log.success(`Connected to ${connector._name} database`); + attempt = 0; + } catch (error) { + log.error(`Connection to ${connector._name} database was failed`, error); + emitError({ + isSystem: true, + type: 'DatabaseConnectionError', + name: connector._name, + error + }); setTimeout(() => { log.info(`Reconnecting to ${connector._name}... Attempt #${attempt}`); connect(connector, attempt + 1); }, 5000); - }) + } } function maintainConnector (connector, dbConfig) { @@ -44,10 +33,25 @@ function maintainConnector (connector, dbConfig) { if (prop in connector) return connector[prop]; // Here `prop` is model name - try { - return connector.getModel(MODELS_BASE_PATH, prop); - } catch (err) { + const modelPath = Path.join(MODELS_BASE_PATH, prop + '.js'); + if (!existsSync(modelPath)) { log.warn(`Database model "${prop}" not found for "${dbConfig._name}" configuration`); + return; + } + + try { + // todo: schema types + const schema = require(modelPath); + for (const field in schema) { + const descriptor = schema[field]; + if (descriptor instanceof ModelField) { + schema[field] = descriptor.info; + } + } + + return connector.getModel(MODELS_BASE_PATH, prop, schema); + } catch (error) { + log.error(`Cannot load "${prop}" model for "${dbConfig._name}" configuration`, error); } } }); @@ -56,9 +60,23 @@ function maintainConnector (connector, dbConfig) { const connections = {}; const drivers = {}; exports.registerDriver = (DriverClass, driverName) => { + for (const key in config.db) { + if (key == driverName || key.startsWith(driverName + '.')) { + const dbConfig = config.db[key]; + // todo! write docs + if (dbConfig.template) continue; + + const connector = new DriverClass(dbConfig); + connector._self = DriverClass; + connector._name = key; + dbConfig._name = key; + connections[key] = maintainConnector(connector, dbConfig); + } + } + DriverClass.connect = (configKey, options) => { if (options && !options.identifier && !options.name) { - return log.warn('Templated connection to database must have `identifier` field'); + return log.warn('Templated database connection must have `identifier` field'); } // Key of configuration in config.db object @@ -106,4 +124,54 @@ exports.connect = (configKey, options) => { } const { EventEmitter } = require('events'); +const { existsSync } = require('fs'); exports.DatabaseDriver = class DatabaseDriver extends EventEmitter {} + +class ModelField { + info = {}; + constructor (type) { + this.info.type = type; + this.info.required = false; + } + + get required () { + this.info.required = true; + return this; + } + + default (value) { + this.info.default = value; + return this; + } +} + +class ModelInt extends ModelField { + constructor () { super('int'); } +} +exports.int = () => new ModelInt(); + +class ModelString extends ModelField { + constructor (length) { + super('string'); + this.info.length = length; + } +} +exports.string = length => new ModelString(length); + +class ModelText extends ModelField { + constructor () { super('text'); } + + get json () { + this.info.json = true; + return this; + } +} +exports.text = () => new ModelText(); + +class ModelEnum extends ModelField { + constructor (values) { + super('enum'); + this.info.values = values; + } +} +exports.enumerable = (...values) => new ModelEnum(values); diff --git a/package.json b/package.json index 78fb5b6..cd37347 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dc-api-core", - "version": "0.3.3-1", + "version": "0.3.4", "description": "Simple API core for your projects", "author": "DimaCrafter",