Files
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

230 lines
7.6 KiB
JavaScript

/*
* (c) Copyright Ascensio System SIA 2010-2024
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
'use strict';
const connectorUtilities = require('./connectorUtilities');
const db = require('dmdb');
const config = require('config');
const configSql = config.get('services.CoAuthoring.sql');
const cfgDbHost = configSql.get('dbHost');
const cfgDbPort = configSql.get('dbPort');
const cfgDbUser = configSql.get('dbUser');
const cfgDbPass = configSql.get('dbPass');
const cfgConnectionLimit = configSql.get('connectionlimit');
const cfgTableResult = configSql.get('tableResult');
const cfgDamengExtraOptions = config.util.cloneDeep(configSql.get('damengExtraOptions'));
const forceClosingCountdownMs = 2000;
// dmdb driver separates PoolAttributes and ConnectionAttributes.
// For some reason if you use pool you must define connection attributes in connectString, they are not included in config object, and pool.getConnection() can't configure it.
const poolHostInfo = `dm://${cfgDbUser}:${cfgDbPass}@${cfgDbHost}:${cfgDbPort}`;
const connectionOptions = Object.entries(cfgDamengExtraOptions)
.map(option => option.join('='))
.join('&');
let pool = null;
const poolConfig = {
// String format dm://username:password@host:port[?prop1=val1[&prop2=val2]]
connectString: `${poolHostInfo}${connectionOptions.length > 0 ? '?' : ''}${connectionOptions}`,
poolMax: cfgConnectionLimit,
poolMin: 0
};
function readLob(lob) {
return new Promise((resolve, reject) => {
let blobData = Buffer.alloc(0);
let totalLength = 0;
lob.on('data', chunk => {
totalLength += chunk.length;
blobData = Buffer.concat([blobData, chunk], totalLength);
});
lob.on('error', err => {
reject(err);
});
lob.on('end', () => {
resolve(blobData);
});
});
}
async function formatResult(result) {
const res = [];
if (result?.rows && result?.metaData) {
for (let i = 0; i < result.rows.length; ++i) {
const row = result.rows[i];
const out = {};
for (let j = 0; j < result.metaData.length; ++j) {
const columnName = result.metaData[j].name;
if (row[j]?.on) {
const buf = await readLob(row[j]);
out[columnName] = buf.toString('utf8');
} else {
out[columnName] = row[j];
}
}
res.push(out);
}
}
return res;
}
function sqlQuery(ctx, sqlCommand, callbackFunction, opt_noModifyRes = false, opt_noLog = false, opt_values = []) {
return executeQuery(ctx, sqlCommand, opt_values, opt_noModifyRes, opt_noLog).then(
result => callbackFunction?.(null, result),
error => callbackFunction?.(error)
);
}
async function executeQuery(ctx, sqlCommand, values = [], noModifyRes = false, noLog = false) {
let connection = null;
try {
if (!pool) {
pool = await db.createPool(poolConfig);
}
connection = await pool.getConnection();
const result = await connection.execute(sqlCommand, values, {resultSet: false});
let output = result;
if (!noModifyRes) {
if (result?.rows) {
output = await formatResult(result);
} else if (result?.rowsAffected) {
output = {affectedRows: result.rowsAffected};
} else {
output = {rows: [], affectedRows: 0};
}
}
return output;
} catch (error) {
if (!noLog) {
ctx.logger.warn('sqlQuery error sqlCommand: %s: %s', sqlCommand.slice(0, 50), error.stack);
}
throw error;
} finally {
connection?.close();
}
}
function closePool() {
return pool.close(forceClosingCountdownMs);
}
function addSqlParameter(val, values) {
values.push({val});
return `:${values.length}`;
}
function concatParams(val1, val2) {
return `CONCAT(COALESCE(${val1}, ''), COALESCE(${val2}, ''))`;
}
async function getTableColumns(ctx, tableName) {
const values = [];
const sqlParam = addSqlParameter(tableName.toUpperCase(), values);
const result = await executeQuery(ctx, `SELECT column_name FROM DBA_TAB_COLUMNS WHERE table_name = ${sqlParam};`, values);
return result.map(row => {
return {column_name: row.column_name.toLowerCase()};
});
}
async function upsert(ctx, task) {
task.completeDefaults();
const dateNow = new Date();
const values = [];
let cbInsert = task.callback;
if (task.callback) {
const userCallback = new connectorUtilities.UserCallback();
userCallback.fromValues(task.userIndex, task.callback);
cbInsert = userCallback.toSQLInsert();
}
const p0 = addSqlParameter(task.tenant, values);
const p1 = addSqlParameter(task.key, values);
const p2 = addSqlParameter(task.status, values);
const p3 = addSqlParameter(task.statusInfo, values);
const p4 = addSqlParameter(dateNow, values);
const p5 = addSqlParameter(task.userIndex, values);
const p6 = addSqlParameter(task.changeId, values);
const p7 = addSqlParameter(cbInsert, values);
const p8 = addSqlParameter(task.baseurl, values);
const p9 = addSqlParameter(dateNow, values);
let sqlCommand = `MERGE INTO ${cfgTableResult} USING dual ON (tenant = ${p0} AND id = ${p1}) `;
sqlCommand += `WHEN NOT MATCHED THEN INSERT (tenant, id, status, status_info, last_open_date, user_index, change_id, callback, baseurl) `;
sqlCommand += `VALUES (${p0}, ${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8}) `;
sqlCommand += `WHEN MATCHED THEN UPDATE SET last_open_date = ${p9}`;
if (task.callback) {
const p10 = addSqlParameter(JSON.stringify(task.callback), values);
sqlCommand += `, callback = CONCAT(callback , '${connectorUtilities.UserCallback.prototype.delimiter}{"userIndex":' , (user_index + 1) , ',"callback":', ${p10}, '}')`;
}
if (task.baseurl) {
const p11 = addSqlParameter(task.baseurl, values);
sqlCommand += `, baseurl = ${p11}`;
}
sqlCommand += ', user_index = user_index + 1';
sqlCommand += ';';
sqlCommand += `SELECT user_index FROM ${cfgTableResult} WHERE tenant = ${p0} AND id = ${p1};`;
const out = {};
const result = await executeQuery(ctx, sqlCommand, values);
if (result?.length > 0) {
const first = result[0];
out.isInsert = task.userIndex === first.user_index;
out.insertId = first.user_index;
}
return out;
}
module.exports = {
sqlQuery,
closePool,
addSqlParameter,
concatParams,
getTableColumns,
upsert
};