Skip to content

Commit

Permalink
Added experimental DB model syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
DimaCrafter committed Feb 23, 2023
1 parent fdba6c3 commit 2226490
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 34 deletions.
31 changes: 28 additions & 3 deletions db.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,39 @@ interface DatabaseDriverStatic<DriverType, ModelType> {
import { EventEmitter } from 'events'
export abstract class DatabaseDriver<ModelType> extends EventEmitter {
public _name: string;
protected constructor ();
public abstract constructor (options: object);
public abstract connect (): Promise<void>;
public abstract getModel (basePath: string, name: string): ModelType;
public abstract getModel (basePath: string, name: string, schema: any): ModelType;

emit (event: 'disconnected'): void;
}

type ExtractModelType<DriverClassType> = Instance<DriverClassType> extends DatabaseDriver<infer ModelType> ? ModelType : never;
export function registerDriver<DriverClassType> (driver: DriverClassType, driverName: string): DatabaseDriverStatic<Instance<DriverClassType>, ExtractModelType<DriverClassType>> & DriverClassType;
type ExtractModelType<DriverClass> = Instance<DriverClass> extends DatabaseDriver<infer ModelType> ? ModelType : never;
export function registerDriver<DriverClass> (driver: DriverClass, driverName: string): DatabaseDriverStatic<Instance<DriverClass>, ExtractModelType<DriverClass>> & DriverClass;

export function connect (configKey: string, options?: ConnectionOptions): DatabaseConnection<any>;

interface ModelFieldInfo {
type: 'string' | 'text' | 'int' | 'enum'
}

class ModelField<T> {
readonly info: ModelFieldInfo;
get required (): this;
default (value: T): this;
}

class ModelString extends ModelField<string> {}
export function string (length?: number): ModelString;

class ModelText extends ModelField<string> {
get json (): this;
}
export function text (): ModelText;

class ModelInt extends ModelField<number> {}
export function int (): ModelInt;

class ModelEnum<V> extends ModelField<V[number]> {}
export function enumerable<T extends string, V extends T[]> (...values: V): ModelEnum<typeof values>;
128 changes: 98 additions & 30 deletions db.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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);
}
}
});
Expand All @@ -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
Expand Down Expand Up @@ -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);
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down

0 comments on commit 2226490

Please sign in to comment.