UNPKG

multibridge

Version:

A multi-database connection framework with centralized configuration

195 lines (194 loc) 8.29 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.executeQuery = executeQuery; const connectionManager_1 = require("../connections/connectionManager"); const mongodbHelper_1 = require("../helpers/mongodbHelper"); const cassandraHelper_1 = require("../helpers/cassandraHelper"); const loggers_1 = __importDefault(require("./loggers")); const envConfig_1 = require("../config/envConfig"); const tenantContext_1 = require("../context/tenantContext"); const errors_1 = require("./errors"); /** * Execute query with timeout */ async function executeWithTimeout(promise, timeoutMs, queryInfo) { if (timeoutMs <= 0) { return promise; } let timeoutId = null; const timeoutPromise = new Promise((_, reject) => { timeoutId = setTimeout(() => { reject(new errors_1.TimeoutError(`Query timeout after ${timeoutMs}ms: ${queryInfo}`, { timeoutMs, queryInfo, })); }, timeoutMs); }); try { const result = await Promise.race([promise, timeoutPromise]); // Clear timeout if promise resolved first if (timeoutId) { clearTimeout(timeoutId); } return result; } catch (error) { // Clear timeout on error if (timeoutId) { clearTimeout(timeoutId); } throw error; } } /** * Executes a query using the connection determined by the tenant context. * For PostgreSQL and MySQL, the native connection.query is used. * For MongoDB and Cassandra, corresponding helper functions are invoked. */ async function executeQuery(query, params) { const tenant = (0, tenantContext_1.getTenant)(); const tenantContext = tenant ? { appid: tenant.appid, orgid: tenant.orgid } : {}; try { const { connection, dbType } = await (0, connectionManager_1.getConnection)(); switch (dbType) { case "postgres": { const timeout = envConfig_1.envConfig.POSTGRES_QUERY_TIMEOUT_MS; let queryString; let queryParams; if (typeof query === "string") { queryString = query; queryParams = params; } else if (typeof query === "object" && "query" in query && typeof query.query === "string") { queryString = query.query; queryParams = params || query.params; } else { throw new errors_1.QueryError("PostgreSQL query must be a string or SQLQuery object", { ...tenantContext, query, }); } const queryPromise = connection.query(queryString, queryParams || []); const result = await executeWithTimeout(queryPromise, timeout, `PostgreSQL query: ${queryString.substring(0, 100)}`); loggers_1.default.debug("PostgreSQL query executed", { ...tenantContext, queryLength: queryString.length, }); return result; } case "mysql": { const timeout = envConfig_1.envConfig.MYSQL_QUERY_TIMEOUT_MS; let queryString; let queryParams; if (typeof query === "string") { queryString = query; queryParams = params; } else if (typeof query === "object" && "query" in query && typeof query.query === "string") { queryString = query.query; queryParams = params || query.params; } else { throw new errors_1.QueryError("MySQL query must be a string or SQLQuery object", { ...tenantContext, query, }); } const queryPromise = connection.query(queryString, queryParams); const result = await executeWithTimeout(queryPromise, timeout, `MySQL query: ${queryString.substring(0, 100)}`); loggers_1.default.debug("MySQL query executed", { ...tenantContext, queryLength: queryString.length, }); return result; } case "mongodb": { const timeout = envConfig_1.envConfig.MONGODB_QUERY_TIMEOUT_MS; let mongoQuery; if (typeof query === "object" && "collection" in query && "method" in query) { mongoQuery = query; } else { throw new errors_1.QueryError("MongoDB query must be an object with 'collection' and 'method' properties", { ...tenantContext, query, }); } const queryPromise = (0, mongodbHelper_1.executeMongoQuery)(connection.db, mongoQuery); const result = await executeWithTimeout(queryPromise, timeout, `MongoDB ${mongoQuery.collection}.${mongoQuery.method}()`); loggers_1.default.debug("MongoDB query executed", { ...tenantContext, collection: mongoQuery.collection, method: mongoQuery.method, }); return result; } case "cassandra": { const timeout = envConfig_1.envConfig.CASSANDRA_QUERY_TIMEOUT_MS; let queryString; let queryParams; if (typeof query === "string") { queryString = query; queryParams = params; } else if (typeof query === "object" && "query" in query && typeof query.query === "string") { queryString = query.query; queryParams = params || query.params; } else { throw new errors_1.QueryError("Cassandra query must be a string or CassandraQuery object", { ...tenantContext, query, }); } const queryPromise = (0, cassandraHelper_1.executeCassandraQuery)(connection, queryString, queryParams); const result = await executeWithTimeout(queryPromise, timeout, `Cassandra query: ${queryString.substring(0, 100)}`); loggers_1.default.debug("Cassandra query executed", { ...tenantContext, queryLength: queryString.length, }); return result; } default: throw new errors_1.QueryError(`Unsupported database type: ${dbType}`, { ...tenantContext, dbType, }); } } catch (error) { const queryInfo = typeof query === "string" ? query.substring(0, 100) : typeof query === "object" && "collection" in query ? `${query.collection}.${query.method}()` : "unknown query"; // Try to get dbType, but don't fail if getConnection fails let dbType = "unknown"; try { const connectionData = await (0, connectionManager_1.getConnection)(); dbType = connectionData.dbType; } catch { // Ignore - we're already in error handling } loggers_1.default.error(`Error executing query: ${error.message}`, { ...tenantContext, dbType, query: queryInfo, error: error.stack, }); // Re-throw with context if (error instanceof errors_1.QueryError || error instanceof errors_1.TimeoutError) { throw error; } throw new errors_1.QueryError(`Query execution failed: ${error.message}`, { ...tenantContext, query: queryInfo, originalError: error, }); } }