@bee.js/node
Version:
A JavaScript framework for making Node.js API´s
360 lines (302 loc) • 9.42 kB
JavaScript
const mysql = require("mysql2/promise"); //https://evertpot.com/executing-a-mysql-query-in-nodejs/
const beeTools = require("../../tools/beeTools");
const parseArray = function (param) {
switch (typeof param) {
case "string":
return param.split(",").map((a) => a.trim());
case "object":
return Object.values(param);
default:
return param || [];
}
};
const quote = function (word, type = this.configs.databases.default.type) {
let escape;
switch (type) {
case "mysql":
escape = "``";
break;
case "mssql":
escape = "[]";
break;
default:
escape = type + type;
break;
}
return escape[0] + word + escape[1];
};
const stringBind = function (_string, params = {}) {
for (let param of Object.keys(params))
_string = _string.replace(":" + param, `'${params[param]}'`); // TODO fazer replace global (Expressao regular) quando a palavra a ser substituida for iniciada com o caractere :
return _string;
};
const modelSqlSelectFields = function (model, onlyFields = []) {
let fields = [];
model.fields
? (fields = parseArray(model.fields))
: Object.keys(model.schema).map((field) => {
if (
model.schema[field].out === false ||
(onlyFields.length && !onlyFields.includes(field))
)
return;
switch (model.schema[field].type) {
case "uuid":
case "guid":
fields.push(`BIN_TO_UUID(${model.table}.${field}) as ${field}`);
break;
default:
fields.push(`${model.table}.${field}`);
}
});
return fields;
};
const modelSqlWhere = function (
model,
vals,
middlewareParams,
callsWhere = []
) {
const q = quote;
const keys = parseArray(model.indexes?.keys || "");
let SQL = "";
let gWhere = model.globalWhere ? q(model.globalWhere, "()") : "";
gWhere =
gWhere && middlewareParams ? stringBind(gWhere, middlewareParams) : "";
if (typeof vals !== "object") vals = [vals];
vals.map((val, i) =>
keys.map((k) => {
switch (
model.schema[k].type //TODO encapsule
) {
case "guid":
case "uuid":
vals[i] = `UUID_TO_BIN('${val}')`;
break;
default:
vals[i] = `'${val}'`;
}
})
);
switch (vals.length) {
case 0:
SQL += gWhere ? ` WHERE ${gWhere}` : "";
break;
case 1:
SQL += ` WHERE ${model.table}.${keys[0]} = ${vals[0]} ${
gWhere ? "AND " + gWhere : ""
}`;
break;
default:
SQL +=
typeof vals == "object"
? ` WHERE ${model.table}.${keys[0]} IN('${vals.join("','")}') ${
gWhere ? "AND " + gWhere : ""
}`
: ` WHERE ${model.table}.${keys[0]} IN(${vals}) ${
gWhere ? "AND " + gWhere : ""
}`;
}
if (callsWhere.length) SQL += `WHERE ${callsWhere.join(" AND ")}`;
return SQL;
};
module.exports = {
escape,
quote,
stringBind,
parseArray,
modelSqlWhere,
modelSqlSelectFields,
createPool: function (configs) {
// TODO criar aviso de error para falta de configs.db. Criar no hive().create
// TODO pegar dinamicamnete database utilizada
let db = configs.databases.default;
let pool;
switch (db.type) {
case "mssql": // TODO implementar na versao >=1.2
return false;
case "mongo": // TODO implementar na versao >=1.2
return false;
default:
pool = mysql.createPool({
acquireTimeout: 10000, // Tempo limite para tentar adquirir uma nova conexão
connectTimeout: 10000, // Tempo limite para conectar ao banco de dados
idleTimeout: 60000, // Tempo que uma conexão pode ficar inativa antes de ser fechada
reconnect: true,
enableKeepAlive: true,
...db,
host: db.host,
user: db.user,
password: db.pass,
database: db.name,
waitForConnections: db.waitForConnections || true,
connectionLimit: db.connectionLimit || 10,
queueLimit: db.queueLimit || 0,
});
if (db.ping)
setInterval(async () => {
console.log("ping db");
try {
const connection = await pool.getConnection();
await connection.ping();
connection.release();
} catch (error) {
console.error("MySQL ping error:", { error });
}
}, db.ping);
return pool;
}
},
sqlSelect: function (model, ids = [], params = {}) {
//console.log(ids)
let SQL = [];
let vals = Array.isArray(ids) ? ids : ids.toString().split(",");
let where = modelSqlWhere(
model,
vals,
params.middlewareParams,
params.where
);
let joins = params.joins && params.joins.length ? joins.join(" ") : "";
let order =
params.orderBy && params.orderBy.length
? ` ORDER BY ${params.orderBy}`
: "";
let limit =
params.limit && params.limit.length ? ` LIMIT ${params.limit}` : "";
let fields = modelSqlSelectFields(model, params.fields);
SQL.push(`SELECT ${fields.join(", ")} `);
SQL.push(`FROM ${quote(model.table)} `);
SQL.push(joins);
SQL.push(where);
SQL.push(order);
SQL.push(limit);
return SQL;
},
sqlInsertUpdate(model, data, onlyFields = [], statement = "INSERT") {
const q = quote;
let result = {};
let fields = [];
let values = [];
let binds = [];
let insertValues = [];
data = data[0] ? data : [data];
const sanitizeString = (val) => {
return typeof val === "string" ? val.replace(/\0/g, "") : val;
};
const normalizeDate = (val) => {
if (!val || val === "" || isNaN(new Date(val).getTime())) return null;
return val;
};
Object.keys(data).map((key) => {
for (let field in model.schema) {
field = { ...model.schema[field], name: field };
if (field.dbControl) continue;
if (!field.ai && onlyFields.length && !onlyFields.includes(field.name))
continue;
let value = data[key][field.name];
let type = model.schema[field.name].type;
if (statement == "INSERT" && field.ai)
switch (type) {
case "guid":
case "uuid":
value = beeTools.guid().toString();
result.insertBin = beeTools.guidToBin(value);
result.insertUUID = value;
break;
}
if (value === undefined) continue;
switch (type) {
case "guid":
case "uuid":
binds.push(value);
value = `UUID_TO_BIN(?)`;
break;
case "date":
case "datetime":
binds.push(normalizeDate(value));
value = "?";
break;
default:
binds.push(value);
value = "?";
}
if (field.onInput) value = `${field.onInput}(${value})`; //example md5()
if (statement === "INSERT") {
if (key == 0) fields.push(q(field.name));
values.push(value);
} else {
values.push(q(field.name) + "=" + value);
}
}
if (statement === "INSERT") {
insertValues.push(values.join(","));
values = [];
}
});
let SQL =
statement === "INSERT"
? `INSERT INTO ${model.table} (${fields.join(
","
)}) VALUES (${insertValues.join("),(")})`
: `UPDATE ${model.table} SET ${values.join(",")}`;
return { result, SQL, binds };
},
save: (model, key = null) => {
//TODO, tirar daqui.
var fields = Object.keys(model.schema);
var body = [];
//key = 1 //test
for (let i = 0; i <= req.body.length - 1; i++) {
var values = [];
for (field of fields) {
let out = model.schema[field].out || a;
let value = (req.body[i] || req.body)[out];
//TODO inserir validacoes
value = ["guid", "binary"].includes(model.schema[field].type)
? value
: q(value, "''");
values.push({ field: field, out: out, value: value });
}
body.push(values);
}
if (key !== null) {
body = body[0]
.filter((a) => a.value)
.map((a) => (a = a.field + `=${a.value}`))
.join();
db.query(
`UPDATE ${model.table} SET ${body} WHERE ${model.fieldKey} = ${key}`
);
} else {
body = body.map((a) => a.map((a) => `${a.value}`)).join("),(");
db.query(
`INSERT INTO ${model.table} (${Object.keys(
model.schema
)}) VALUES(${body})`
);
}
},
sqlUpdate: (model, keys) => {
//TODO
return db.query(
`UPDATE ${model.table} SET TODO WHERE ${table.fieldKey} = (${key})`
);
},
sqlDelete: (model, keys) => {
//TODO
return db.query(
`DELETE FROM ${model.table} WHERE ${table.fieldKey} = (${key})`
);
},
forceData(data, req = {}) {
// TODO coocar funcao para pegar apenas campos do middlaware marcados para INSERT e UPDATE.
// Se nao campos do tipo IDUser_Profile poderá ser forçado pelo middleware invés do post form.
return Object.assign({}, data, req.token || {}, req.middleware || {});
},
formatOut(data, model) {
//TODO
if (!model) return data;
return data;
},
};