Skip to content

Commit

Permalink
Merge branch 'main' into release
Browse files Browse the repository at this point in the history
# Conflicts:
#	doc/struct.md
  • Loading branch information
flancer64 committed Dec 20, 2021
2 parents 022d356 + f865079 commit 68ea0d3
Show file tree
Hide file tree
Showing 15 changed files with 161 additions and 151 deletions.
38 changes: 35 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,41 @@
# @teqfw/db

TeqFW: DB connectivity.
|CAUTION: TeqFW is an unstable, fast-growing project w/o backward compatibility. Use it at your own risk.|
|---|

TeqFW: DB connectivity based on [knex](https://knexjs.org/) package.

## Install

## Backend connector
```shell
$ npm i @teqfw/db --save
```

Backend connector uses [knex](https://knexjs.org/) library.
## Namespace

This plugin uses `TeqFw_Db` namespace.

## `./cfg/local.json`

[DTO](src/Back/Dto/Config/Local.mjs) for `@teqfw/db` node.

```json
{
"@teqfw/db": {
"client": "mysql2|pg|...",
"connection": {
"database": "duplo",
"filename": "/.../db.sqlite",
"flags": ["for", "SQLite"],
"host": "127.0.0.1",
"password": "...",
"port": 3210,
"socketPath": "/path/to/socket",
"user": "name"
},
"searchPath": ["PostgreSQL client allows you to set the initial search path"],
"useNullAsDefault": true,
"version": "When you use the PostgreSQL adapter to connect a non-standard database."
}
}
```
12 changes: 9 additions & 3 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
# @teqfw/db: releases

# 0.3.1
## 0.4.0

* Improve CRUD engine.
* Add SQLite support to config.
* Add `TeqFw_Db_Back_Dto_Dem_Entity_Relation_Action` for foreign keys actions (restrict, delete, ...).

## 0.3.1

* Fix `readSet` in `TeqFw_Db_Back_RDb_CrudEngine`.

# 0.3.0
## 0.3.0

* Cast function for DTOs.
* DB utils refactoring.
* DEM (Domain Entities Model) support.
* CRUD engine basics.

# 0.2.0
## 0.2.0

* use `@teqfw/di.replace` in `./teqfw.json` as an object and not as an array;
* (re)create DB structure with DEM declaration;
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@teqfw/db",
"version": "0.3.1",
"version": "0.4.0",
"description": "TeqFW: DB connectivity.",
"scripts": {
"test": "./node_modules/mocha/bin/mocha --recursive './test/mod/**/*.test.mjs'"
Expand Down Expand Up @@ -29,7 +29,7 @@
},
"homepage": "https://github.com/teqfw/db#readme",
"dependencies": {
"@teqfw/core": "*",
"@teqfw/web": "*",
"knex": "*"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions src/Back/Api/RDb/ICrudEngine.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default class TeqFw_Db_Back_Api_RDb_ICrudEngine {
*
* @param {TeqFw_Db_Back_RDb_ITrans} trx DB transaction for data processing
* @param {TeqFw_Db_Back_RDb_Meta_IEntity} meta meta data for related entity
* @param {Object|Array} data data to process
* @param {Object|Array} [data] data to process
* @return {Promise<Object>} object with primary key data ({key1: ..., key2: ..., ...})
*/
async create(trx, meta, data) {};
Expand Down Expand Up @@ -38,7 +38,7 @@ export default class TeqFw_Db_Back_Api_RDb_ICrudEngine {
*
* @param {TeqFw_Db_Back_RDb_ITrans} trx DB transaction for data processing
* @param {TeqFw_Db_Back_RDb_Meta_IEntity} meta meta data for related entity
* @param {Object|Array} key key values (primary or unique)
* @param {number|string|boolean|Array|Object} key JS primitive for simple PK or object/array for complex PK or unique key
* @return {Promise<Object|null>}
*/
async readOne(trx, meta, key) {};
Expand Down
9 changes: 8 additions & 1 deletion src/Back/Dto/Config/Local.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ export default class TeqFw_Db_Back_Dto_Config_Local {
* @type {string[]}
*/
searchPath;
/**
* SQLite: replace undefined keys with NULL instead of DEFAULT.
*
* @type {boolean}
*/
useNullAsDefault;
/**
* When you use the PostgreSQL adapter to connect a non-standard database.
* @type {string}
Expand All @@ -29,7 +35,7 @@ export default class TeqFw_Db_Back_Dto_Config_Local {
*/
export class Factory {
constructor(spec) {
const {castArrayOfStr, castString} = spec['TeqFw_Core_Shared_Util_Cast'];
const {castArrayOfStr, castString, castBoolean} = spec['TeqFw_Core_Shared_Util_Cast'];
/** @type {TeqFw_Db_Back_Dto_Config_Local_Connection.Factory} */
const fConn = spec['TeqFw_Db_Back_Dto_Config_Local_Connection#Factory$'];

Expand All @@ -42,6 +48,7 @@ export class Factory {
res.client = castString(data?.client);
res.connection = fConn.create(data?.connection);
res.searchPath = castArrayOfStr(data?.searchPath);
res.useNullAsDefault = castBoolean(data?.useNullAsDefault);
res.version = castString(data?.version);
return res;
}
Expand Down
15 changes: 6 additions & 9 deletions src/Back/Dto/Dem/Entity/Relation.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ const NS = 'TeqFw_Db_Back_Dto_Dem_Entity_Relation';

// MODULE'S CLASSES
export default class TeqFw_Db_Back_Dto_Dem_Entity_Relation {
/** @type {TeqFw_Db_Back_Dto_Dem_Entity_Relation_Action} */
action;
/** @type {string[]} */
attrs;
/** @type {string} */
name;
/** @type {TeqFw_Db_Back_Enum_Dem_Type_Action} */
onDelete;
/** @type {TeqFw_Db_Back_Enum_Dem_Type_Action} */
onUpdate;
/** @type {TeqFw_Db_Back_Dto_Dem_Entity_Relation_Ref} */
ref;
}
Expand All @@ -24,22 +22,21 @@ export default class TeqFw_Db_Back_Dto_Dem_Entity_Relation {
*/
export class Factory {
constructor(spec) {
const {castArray, castEnum, castString} = spec['TeqFw_Core_Shared_Util_Cast'];
/** @type {typeof TeqFw_Db_Back_Enum_Dem_Type_Action} */
const ACTION = spec['TeqFw_Db_Back_Enum_Dem_Type_Action#'];
const {castArray, castString} = spec['TeqFw_Core_Shared_Util_Cast'];
/** @type {TeqFw_Db_Back_Dto_Dem_Entity_Relation_Ref.Factory} */
const fRef = spec['TeqFw_Db_Back_Dto_Dem_Entity_Relation_Ref#Factory$'];
/** @type {TeqFw_Db_Back_Dto_Dem_Entity_Relation_Action.Factory} */
const fAction = spec['TeqFw_Db_Back_Dto_Dem_Entity_Relation_Action#Factory$'];

/**
* @param {TeqFw_Db_Back_Dto_Dem_Entity_Relation|null} data
* @return {TeqFw_Db_Back_Dto_Dem_Entity_Relation}
*/
this.create = function (data = null) {
const res = new TeqFw_Db_Back_Dto_Dem_Entity_Relation();
res.action = fAction.create(data?.action);
res.attrs = castArray(data?.attrs);
res.name = castString(data?.name);
res.onDelete = castEnum(data?.onDelete, ACTION);
res.onUpdate = castEnum(data?.onUpdate, ACTION);
res.ref = fRef.create(data?.ref);
return res;
}
Expand Down
42 changes: 42 additions & 0 deletions src/Back/Dto/Dem/Entity/Relation/Action.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* DTO for DEM 'entity/relation/action'.
*/
// MODULE'S VARS
const NS = 'TeqFw_Db_Back_Dto_Dem_Entity_Relation_Action';

// MODULE'S CLASSES
export default class TeqFw_Db_Back_Dto_Dem_Entity_Relation_Action {
/** @type {TeqFw_Db_Back_Enum_Dem_Type_Action} */
delete;
/** @type {TeqFw_Db_Back_Enum_Dem_Type_Action} */
update;
}

/**
* Factory to create new DTO instances.
* @memberOf TeqFw_Db_Back_Dto_Dem_Entity_Relation_Action
*/
export class Factory {
constructor(spec) {
/** @type {TeqFw_Core_Shared_Util_Cast.castEnum|function} */
const castEnum = spec['TeqFw_Core_Shared_Util_Cast.castEnum'];
/** @type {typeof TeqFw_Db_Back_Enum_Dem_Type_Action} */
const ACTION = spec['TeqFw_Db_Back_Enum_Dem_Type_Action#'];

/**
* @param {TeqFw_Db_Back_Dto_Dem_Entity_Relation_Action|null} data
* @return {TeqFw_Db_Back_Dto_Dem_Entity_Relation_Action}
*/
this.create = function (data = null) {
const res = new TeqFw_Db_Back_Dto_Dem_Entity_Relation_Action();
res.delete = castEnum(data?.delete, ACTION);
res.update = castEnum(data?.update, ACTION);
return res;
}
}
}

// finalize code components for this es6-module
Object.freeze(TeqFw_Db_Back_Dto_Dem_Entity_Relation_Action);
Object.defineProperty(Factory, 'name', {value: `${NS}.${Factory.constructor.name}`});

20 changes: 14 additions & 6 deletions src/Back/RDb/Connect.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export default class TeqFw_Db_Back_RDb_Connect {
// DEFINE WORKING VARS / PROPS
/** @type {Knex} */
let _knex;
let _db;
/** @type {string} */
let _info;

// DEFINE INSTANCE METHODS
/**
Expand All @@ -34,13 +35,20 @@ export default class TeqFw_Db_Back_RDb_Connect {
* @returns {Promise<void>}
*/
this.init = async function (cfg) {
_db = cfg.connection.database + '@' + cfg.connection.host;
const user = cfg.connection.user;
const filename = cfg?.connection?.filename;
if (filename) {
_info = `'${filename}'`;
} else {
const db = cfg?.connection?.database;
const host = cfg?.connection?.host;
const user = cfg?.connection?.user;
_info = `'${db}@${host}' as '${user}'`;
}
try {
_knex = await knex(cfg);
_logger.info(`Setup connection to DB '${_db}' as '${user}'.`);
_logger.info(`Setup connection to DB ${_info}.`);
} catch (e) {
_logger.error(`Cannot setup connection to DB '${_db}' as '${user}'. Error: ${e}`);
_logger.error(`Cannot setup connection to DB ${_info}. Error: ${e}`);
throw e;
}
};
Expand Down Expand Up @@ -93,7 +101,7 @@ export default class TeqFw_Db_Back_RDb_Connect {
} else {
// close all connections
_knex.destroy();
_logger.info(`Connections to '${_db}' are closed.`);
_logger.info(`Connections to ${_info} are closed.`);
resolve();
}
}
Expand Down
43 changes: 33 additions & 10 deletions src/Back/RDb/CrudEngine.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ export default class TeqFw_Db_Back_RDb_CrudEngine {
const query = trx.createQuery();
query.table(table);
const record = {};
for (const attr of attrs)
if (data[attr] !== undefined) record[attr] = data[attr];
if (data)
for (const attr of attrs)
if (data[attr] !== undefined) record[attr] = data[attr];
query.insert(record);
if (trx.isPostgres()) {
query.returning(meta.getPrimaryKey());
Expand All @@ -28,8 +29,8 @@ export default class TeqFw_Db_Back_RDb_CrudEngine {
if (pk.length === 1) { // simple PK
res[pk[0]] = rs[0];
} else { // complex PK
for (const key of pk)
res[key] = data[key];
if (data)
for (const key of pk) res[key] = data[key];
}
} else if (trx.isPostgres()) {
if (Array.isArray(rs) && (typeof rs[0] === 'object')) Object.assign(res, rs[0]);
Expand Down Expand Up @@ -62,17 +63,39 @@ export default class TeqFw_Db_Back_RDb_CrudEngine {
}

this.readOne = async function (trx, meta, key) {
// DEFINE INNER FUNCTIONS
/**
* @param {TeqFw_Db_Back_RDb_Meta_IEntity} meta meta data for related entity
* @param {*} key
*/
function composeWhere(meta, key) {
const res = {};
if (
(typeof key === 'number') ||
(typeof key === 'string') ||
(typeof key === 'boolean')
) {
// simple key
const [pk] = meta.getPrimaryKey();
res[pk] = key;
} else {
// complex key
const attrs = meta.getAttrNames();
const keyParts = Array.isArray(key) ? Object.fromEntries(key) : key;
for (const one of Object.keys(keyParts))
if (attrs.includes(one)) res[one] = key[one];
}
return res;
}

// MAIN FUNCTIONALITY
let res = null;
const table = trx.getTableName(meta);
const attrs = meta.getAttrNames();
/** @type {Knex.QueryBuilder} */
const query = trx.createQuery();
query.table(table);
// check key values according to allowed attributes and set record filter
const where = {};
const keyParts = Array.isArray(key) ? Object.fromEntries(key) : key;
for (const one of Object.keys(keyParts))
if (attrs.includes(one)) where[one] = key[one];
const where = composeWhere(meta, key);
query.where(where);
query.limit(2); // should be one only item, limit if search key is not unique
// const sql = query.toString();
Expand All @@ -90,7 +113,7 @@ export default class TeqFw_Db_Back_RDb_CrudEngine {
/** @type {Knex.QueryBuilder} */
const query = trx.createQuery();
query.table(table);
query.where(where);
if (where) query.where(where);
if (order) query.orderBy(order);
if (offset) query.offset(offset);
if (limit) query.limit(limit);
Expand Down
2 changes: 1 addition & 1 deletion src/Back/RDb/Meta/IEntity.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default class TeqFw_Db_Back_RDb_Meta_IEntity {
/**
* Create entity DTO from given data.
* @param [data]
* @return {Object}
* @return {*}
*/
createDto(data) {}

Expand Down
5 changes: 4 additions & 1 deletion src/Back/RDb/Schema.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ export default class TeqFw_Db_Back_RDb_Schema {

constructor(spec) {
// EXTRACT DEPS
/** @type {TeqFw_Core_Shared_Logger} */
const _logger = spec['TeqFw_Core_Shared_Logger$'];
/** @type {TeqFw_Db_Back_RDb_Schema_A_Convert} */
const _aConvert = spec['TeqFw_Db_Back_RDb_Schema_A_Convert$'];
/** @type {TeqFw_Db_Back_RDb_Schema_A_Order} */
const _aOrder = spec['TeqFw_Db_Back_RDb_Schema_A_Order$'];
/** @type {TeqFw_Db_Back_RDb_Schema_A_Builder} */
const _builder = spec['TeqFw_Db_Back_RDb_Schema_A_Builder$'];


// DEFINE WORKING VARS / PROPS
/** @type {TeqFw_Db_Back_Dto_Dem} */
let _dem;
Expand All @@ -29,9 +30,11 @@ export default class TeqFw_Db_Back_RDb_Schema {
const dem = _dem;
/** @type {TeqFw_Db_Back_Dto_Dem_Entity[]} */
const entities = await _aOrder.exec({dem});
_logger.info(`Total ${entities.length} entities are in DEM.`);
for (const entity of entities) {
const tbl = await _aConvert.exec({entity, cfg: _cfg});
_builder.addTable(schema, tbl, knex);
_logger.info(`Table '${tbl.name}' is created.`);
}
// perform operations
// const sql = schema.toString();
Expand Down
4 changes: 2 additions & 2 deletions src/Back/RDb/Schema/A/Convert.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ export default class TeqFw_Db_Back_RDb_Schema_A_Convert {
const prefix = (cfg?.prefix) ?? '';
db.itsTable = normName(`${prefix}${dem.ref.path}`);
db.itsColumns = dem.ref.attrs;
if (dem.onDelete) db.onDelete = mapAction[dem.onDelete];
if (dem.onUpdate) db.onUpdate = mapAction[dem.onUpdate];
if (dem?.action?.delete) db.onDelete = mapAction[dem.action.delete];
if (dem?.action?.update) db.onUpdate = mapAction[dem.action.update];
tbl.relations.push(db);
}

Expand Down
Loading

0 comments on commit 68ea0d3

Please sign in to comment.