1
0
mirror of https://gitlab.com/comunic/comunicapiv2 synced 2024-11-29 16:56:29 +00:00
comunicapiv2/src/helpers/DatabaseHelper.ts

361 lines
7.3 KiB
TypeScript

import { createConnection, Connection } from "mysql";
import { conf } from "./ConfigHelper";
/**
* Database helper
*
* @author Pierre HUBERT
*/
export enum JoinType {
NORMAL,
LEFT
}
export interface JoinTableInfo {
table: string,
tableAlias ?: string,
condition: string
}
export interface QueryInformation {
table: string,
tableAlias?: string,
joinType ?: JoinType,
joins ?: Array<JoinTableInfo>,
fields ?: Array<string>,
where ?: Object,
customWhere ?: string,
customWhereArgs ?: Array<string>,
groupBy ?: string,
order ?: string,
limit ?: number,
}
export interface UpdateInformation {
table: string,
set: Object,
customWhere ?: string,
customWhereArgs ?: Array<string>,
where ?: Object,
}
export interface CountQueryInformation {
table: string,
where ?: Object,
customWhere ?: string,
customWhereArgs ?: Array<string>
}
export class DatabaseHelper {
private static connection : Connection;
/**
* Connect to database
*/
static async connect() {
this.connection = createConnection({
host: conf().database.host,
user: conf().database.user,
password: conf().database.password,
database: conf().database.dbName
});
await new Promise((resolve, reject) => {
this.connection.connect(err => {
if(err) {
console.error("Could not connect to database !");
console.error(err);
reject(err);
return;
}
console.info("Connected to database.");
resolve();
});
})
}
/**
* Get an instance of the connection to the database
*/
static getConnection() : Connection {
return this.connection;
}
/**
* Query the database (SELECT)
*
* @param info Information about the query
*/
static async Query(info: QueryInformation) : Promise<Array<any>> {
// Prepare SQL request
let request = "SELECT ";
// Requested fields
request += info.fields ? info.fields.join(",") : "*";
request += " FROM " + info.table;
if(info.tableAlias)
request += " " + info.tableAlias + " ";
// Joins condition
if(info.joins) {
if(info.joinType == JoinType.LEFT)
request += " LEFT ";
info.joins.forEach(join => {
request += " JOIN " + join.table + (join.tableAlias ? " " + join.tableAlias : "") + " ON " + join.condition
});
}
let args = [];
// Add where arguments
if(info.where) {
request += " WHERE ";
for(const k in info.where) {
if(!info.where.hasOwnProperty(k))
continue;
const v = info.where[k].toString();
request += k;
request += v.startsWith("%") || v.endsWith("%") ? " LIKE " : " = "
request += "? AND "
args.push(v);
};
// Remove the last (useless) AND
request = request.substr(0, request.length - 4)
}
// Add custom WHERE clause
if(info.customWhere) {
if(!info.where)
request += " WHERE " + info.customWhere + " ";
else
request += " AND (" + info.customWhere + ")";
if(info.customWhereArgs)
info.customWhereArgs.forEach((e) => args.push(e));
}
// Group by clause (if any)
if(info.groupBy) {
request += " GROUP BY " + info.groupBy + " ";
}
// Order (if any)
if(info.order)
request += " ORDER BY " + info.order + " ";
// Limit (if any)
if(info.limit)
request += " LIMIT " + info.limit;
// Execute request
return await new Promise((resolve, reject) => {
this.connection.query( request, args, (err, result, fields) => {
if(err) {
reject(err);
return;
}
resolve(result);
});
});
}
/**
* Query a single row on the database
*
* @param info Information about the request
* @returns First matching row / null if none found
*/
static async QueryRow(info : QueryInformation) : Promise<any | null> {
info.limit = 1;
const result = await this.Query(info);
if(result.length == 0)
return null;
return result[0];
}
/**
* Insert an new entry into the database
*
* @param info Information about the entry
* @returns The ID of the inserted column (if any)
*/
static async InsertRow(table : string, values : any) : Promise<number> {
return new Promise((resolve, reject) => {
this.connection.query("INSERT INTO " + table + " SET ?", values, (err, results, f) => {
if(err)
reject(err);
else
resolve(results.insertId);
});
});
}
/**
* Perform update on the database
*
* @param info Information about the request
* @returns The number of affected rows
*/
static async UpdateRows(info : UpdateInformation) : Promise<number> {
let sql = "UPDATE " + info.table + " SET ";
let args = [];
// Process updates
let isFirst = true;
for (const key in info.set) {
if (info.set.hasOwnProperty(key)) {
const value = info.set[key];
if(!isFirst)
sql += ", ";
else
isFirst = false;
sql += key + " = ? "
args.push(value);
}
}
// Process conditions
isFirst = true;
if(info.where) {
sql += " WHERE ";
for (const key in info.where) {
if (info.where.hasOwnProperty(key)) {
const value = info.where[key];
if(!isFirst)
sql += " AND ";
else
isFirst = false;
sql += key + " = ? "
args.push(value);
}
}
}
// Security : block unconditionned updates
else if(!info.customWhere)
throw Error("Error : Updates without conditions are blocked for security!");
// Process custom conditions
if(info.customWhere) {
if(info.where)
sql += " AND (" + info.customWhere + ") ";
else
sql += " WHERE " + info.customWhere + " ";
if(info.customWhereArgs)
info.customWhereArgs.forEach(e => args.push(e));
}
// Execute request
return await new Promise((resolve, reject) => {
this.connection.query(sql, args, (err, results, f) => {
if(err){
reject(err);
return;
}
resolve(results.affectedRows);
})
});
}
/**
* Delete entries from a table
*
* @param table Target table
* @param where Where arguments
*/
static async DeleteRows(table: string, where: any) {
let whereArgs = "";
let args = [];
// Process conditions
for (const key in where) {
if (where.hasOwnProperty(key)) {
const value = where[key];
whereArgs += (whereArgs == "" ? "" : " AND ") + key + " = ?";
args.push(value);
}
}
if(whereArgs == "")
throw Error("Error : table could accidentally get purged!");
return new Promise((resolve, reject) => {
this.connection.query("DELETE FROM " + table + " WHERE " + whereArgs, args, (err, r, f) => {
if(err) reject(err)
else resolve();
});
})
}
/**
* Perform a COUNT query on the database
*
* @param info Information about the count query
*/
static async Count(info: CountQueryInformation) : Promise<number> {
let sql = "SELECT COUNT(*) as count FROM " + info.table;
let args = [];
if(info.where) {
sql += " WHERE ";
for (const key in info.where) {
if (info.where.hasOwnProperty(key)) {
const value = info.where[key];
sql += "AND " + key + " = ? ";
args.push(value);
}
}
sql = sql.replace("WHERE AND", "WHERE");
}
if(info.customWhere) {
if(info.where)
sql += " AND (" + info.customWhere + ")";
else
sql += "WHERE " + info.customWhere;
if(info.customWhereArgs)
info.customWhereArgs.forEach(e => args.push(e));
}
return await new Promise((r, e) => {
this.connection.query(sql, args, (err, results, f) => {
if(err){
e(err);
return;
}
r(results[0].count);
})
});
}
}