UNPKG

n8n-nodes-base

Version:

Base nodes of n8n

303 lines 10.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.description = exports.properties = void 0; exports.execute = execute; const di_1 = require("@n8n/di"); const alasql_1 = __importDefault(require("alasql")); const n8n_core_1 = require("n8n-core"); const n8n_workflow_1 = require("n8n-workflow"); const utilities_1 = require("../../../../../utils/utilities"); const descriptions_1 = require("../../helpers/descriptions"); const utils_1 = require("../../helpers/utils"); const alasql = alasql_1.default; function disableAlasqlFileAccess() { const disabledFunction = () => { throw new Error('File access operations are disabled for security reasons'); }; // Block ALL FROM handlers that can read files or external resources if (alasql.from) { const fromHandlers = [ 'FILE', 'JSON', 'JSONL', 'NDJSON', 'TXT', 'CSV', 'TAB', 'TSV', 'XLS', 'XLSX', 'ODS', 'XML', 'GEXF', 'HTML', 'TABLETOP', 'METEOR', ]; fromHandlers.forEach((handler) => { alasql.from[handler] = disabledFunction; }); } // Block ALL INTO handlers that can write files if (alasql.into) { const intoHandlers = [ 'FILE', 'JSON', 'TXT', 'CSV', 'TAB', 'TSV', 'SQL', 'XLS', 'XLSXML', 'XLSX', 'HTML', ]; const intoObj = alasql.into; intoHandlers.forEach((handler) => { intoObj[handler] = disabledFunction; }); } // Block ALL file-based database engines if (alasql.engines) { const engines = [ 'FILE', 'FILESTORAGE', 'LOCALSTORAGE', 'INDEXEDDB', 'SQLITE', 'JSON', 'TXT', 'CSV', 'XLSX', 'XLS', ]; const enginesObj = alasql.engines; engines.forEach((engine) => { enginesObj[engine] = disabledFunction; }); } // Block file system utility functions if (alasql.utils) { alasql.utils.loadFile = disabledFunction; alasql.utils.loadBinaryFile = disabledFunction; alasql.utils.saveFile = disabledFunction; alasql.utils.removeFile = disabledFunction; alasql.utils.deleteFile = disabledFunction; alasql.utils.fileExists = disabledFunction; } // Block fn handlers if present if (alasql.fn) { const fnHandlers = ['FILE', 'JSON', 'TXT', 'CSV', 'XLSX', 'XLS', 'LOAD', 'SAVE']; fnHandlers.forEach((handler) => { alasql.fn[handler] = disabledFunction; }); } } exports.properties = [ descriptions_1.numberInputsProperty, { displayName: 'Query', name: 'query', type: 'string', default: 'SELECT * FROM input1 LEFT JOIN input2 ON input1.name = input2.id', noDataExpression: true, description: 'Input data available as tables with corresponding number, e.g. input1, input2', hint: 'Supports <a href="https://github.com/alasql/alasql/wiki/Supported-SQL-statements" target="_blank">most</a> of the SQL-99 language', required: true, typeOptions: { rows: 5, editor: 'sqlEditor', }, }, { displayName: 'Options', name: 'options', type: 'collection', placeholder: 'Add option', default: {}, options: [ { displayName: 'Empty Query Result', name: 'emptyQueryResult', type: 'options', description: 'What to return if the query executed successfully but returned no results', options: [ { name: 'Success', value: 'success', }, { name: 'Empty Result', value: 'empty', }, ], default: 'empty', }, ], displayOptions: { show: { '@version': [3.2], }, }, }, ]; const displayOptions = { show: { mode: ['combineBySql'], }, }; exports.description = (0, utilities_1.updateDisplayOptions)(displayOptions, exports.properties); const prepareError = (node, error) => { let message = ''; if (typeof error === 'string') { message = error; } else { message = error.message; } throw new n8n_workflow_1.NodeOperationError(node, error, { message: 'Issue while executing query', description: message, itemIndex: 0, }); }; async function executeSelectWithMappedPairedItems(node, inputsData, query, returnSuccessItemIfEmpty) { const returnData = []; const db = new alasql.Database(node.id); try { for (let i = 0; i < inputsData.length; i++) { const inputData = inputsData[i]; db.exec(`CREATE TABLE input${i + 1}`); db.tables[`input${i + 1}`].data = inputData.map((entry) => ({ ...entry.json, pairedItem: entry.pairedItem, })); } } catch (error) { throw new n8n_workflow_1.NodeOperationError(node, error, { message: 'Issue while creating table from', description: error.message, itemIndex: 0, }); } try { const result = db.exec((0, utils_1.modifySelectQuery)(query, inputsData.length)); for (const item of result) { if (Array.isArray(item)) { returnData.push.apply(returnData, item.map(utils_1.rowToExecutionData)); } else if (typeof item === 'object') { returnData.push((0, utils_1.rowToExecutionData)(item)); } } if (!returnData.length && returnSuccessItemIfEmpty) { returnData.push({ json: { success: true } }); } } catch (error) { prepareError(node, error); } finally { delete alasql.databases[node.id]; } return [returnData]; } async function execute(inputsData) { disableAlasqlFileAccess(); const node = this.getNode(); const returnData = []; const pairedItem = []; const options = this.getNodeParameter('options', 0, {}); let query = this.getNodeParameter('query', 0); for (const resolvable of (0, utilities_1.getResolvables)(query)) { query = query.replace(resolvable, this.evaluateExpression(resolvable, 0)); } const isSelectQuery = node.typeVersion >= 3.1 ? query.toLowerCase().startsWith('select') : false; const returnSuccessItemIfEmpty = node.typeVersion <= 3.1 ? true : options.emptyQueryResult === 'success'; if (isSelectQuery) { try { return await executeSelectWithMappedPairedItems(node, inputsData, query, returnSuccessItemIfEmpty); } catch (error) { di_1.Container.get(n8n_core_1.ErrorReporter).error(error, { extra: { nodeName: node.name, nodeType: node.type, nodeVersion: node.typeVersion, workflowId: this.getWorkflow().id, }, }); } } const db = new alasql.Database(node.id); try { for (let i = 0; i < inputsData.length; i++) { const inputData = inputsData[i]; inputData.forEach((item, index) => { if (item.pairedItem === undefined) { item.pairedItem = index; } if (typeof item.pairedItem === 'number') { pairedItem.push({ item: item.pairedItem, input: i, }); return; } if (Array.isArray(item.pairedItem)) { const pairedItems = item.pairedItem .filter((p) => p !== undefined) .map((p) => (typeof p === 'number' ? { item: p } : p)) .map((p) => { return { item: p.item, input: i, }; }); pairedItem.push.apply(pairedItem, pairedItems); return; } pairedItem.push({ item: item.pairedItem.item, input: i, }); }); db.exec(`CREATE TABLE input${i + 1}`); db.tables[`input${i + 1}`].data = inputData.map((entry) => entry.json); } } catch (error) { throw new n8n_workflow_1.NodeOperationError(node, error, { message: 'Issue while creating table from', description: error.message, itemIndex: 0, }); } try { const result = db.exec(query); for (const item of result) { if (Array.isArray(item)) { returnData.push.apply(returnData, item.map((json) => ({ json, pairedItem }))); } else if (typeof item === 'object') { returnData.push({ json: item, pairedItem }); } } if (!returnData.length && returnSuccessItemIfEmpty) { returnData.push({ json: { success: true }, pairedItem }); } } catch (error) { prepareError(node, error); } finally { delete alasql.databases[node.id]; } return [returnData]; } //# sourceMappingURL=combineBySql.js.map