UNPKG

n8n-nodes-base

Version:

Base nodes of n8n

239 lines 9.52 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.findAppendRange = exports.nextExcelColumn = exports.parseAddress = exports.checkRange = exports.CELL_REGEX = void 0; exports.prepareOutput = prepareOutput; exports.updateByDefinedValues = updateByDefinedValues; exports.updateByAutoMaping = updateByAutoMaping; const n8n_workflow_1 = require("n8n-workflow"); const utilities_1 = require("../../../../../utils/utilities"); exports.CELL_REGEX = /([a-zA-Z]{1,10})([0-9]{0,10})/; function prepareOutput(node, responseData, config) { const returnData = []; const { rawData, keyRow, firstDataRow, columnsRow, updatedRows } = { keyRow: 0, firstDataRow: 1, columnsRow: undefined, updatedRows: undefined, ...config, }; if (!rawData) { let values = responseData.values; if (values === null) { throw new n8n_workflow_1.NodeOperationError(node, 'Operation did not return data'); } let columns = []; if (columnsRow?.length) { columns = columnsRow; values = [columns, ...values]; } else { columns = values[keyRow]; } if (updatedRows) { values = values.filter((_, index) => updatedRows.includes(index)); } for (let rowIndex = firstDataRow; rowIndex < values.length; rowIndex++) { if (rowIndex === keyRow) continue; const data = {}; for (let columnIndex = 0; columnIndex < columns.length; columnIndex++) { data[columns[columnIndex]] = values[rowIndex][columnIndex]; } const executionData = this.helpers.constructExecutionMetaData((0, utilities_1.wrapData)({ ...data }), { itemData: { item: rowIndex }, }); returnData.push(...executionData); } } else { const itemData = (0, utilities_1.generatePairedItemData)(this.getInputData().length); const executionData = this.helpers.constructExecutionMetaData((0, utilities_1.wrapData)({ [config.dataProperty || 'data']: responseData }), { itemData }); returnData.push(...executionData); } return returnData; } // update values of spreadsheet when update mode is 'define' function updateByDefinedValues(itemslength, sheetData, updateAllOccurences) { const [columns, ...originalValues] = sheetData; const updateValues = originalValues; const updatedRowsIndexes = new Set(); const appendData = []; for (let itemIndex = 0; itemIndex < itemslength; itemIndex++) { const columnToMatchOn = this.getNodeParameter('columnToMatchOn', itemIndex); const valueToMatchOn = this.getNodeParameter('valueToMatchOn', itemIndex); const definedFields = this.getNodeParameter('fieldsUi.values', itemIndex, []); const columnToMatchOnIndex = columns.indexOf(columnToMatchOn); const rowIndexes = []; if (updateAllOccurences) { for (const [index, row] of originalValues.entries()) { if (row[columnToMatchOnIndex] === valueToMatchOn || Number(row[columnToMatchOnIndex]) === Number(valueToMatchOn)) { rowIndexes.push(index); } } } else { const rowIndex = originalValues.findIndex((row) => row[columnToMatchOnIndex] === valueToMatchOn || Number(row[columnToMatchOnIndex]) === Number(valueToMatchOn)); if (rowIndex !== -1) { rowIndexes.push(rowIndex); } } if (!rowIndexes.length) { const appendItem = {}; appendItem[columnToMatchOn] = valueToMatchOn; for (const entry of definedFields) { appendItem[entry.column] = entry.fieldValue; } appendData.push(appendItem); continue; } for (const rowIndex of rowIndexes) { for (const entry of definedFields) { const columnIndex = columns.indexOf(entry.column); if (rowIndex === -1) continue; updateValues[rowIndex][columnIndex] = entry.fieldValue; //add rows index and shift by 1 to account for header row updatedRowsIndexes.add(rowIndex + 1); } } } const updatedData = [columns, ...updateValues]; const updatedRows = [0, ...Array.from(updatedRowsIndexes)]; const summary = { updatedData, appendData, updatedRows }; return summary; } // update values of spreadsheet when update mode is 'autoMap' function updateByAutoMaping(items, sheetData, columnToMatchOn, updateAllOccurences = false) { const [columns, ...values] = sheetData; const matchColumnIndex = columns.indexOf(columnToMatchOn); const matchValuesMap = values.map((row) => row[matchColumnIndex]); const updatedRowsIndexes = new Set(); const appendData = []; for (const { json } of items) { const columnValue = json[columnToMatchOn]; if (columnValue === undefined) continue; const rowIndexes = []; if (updateAllOccurences) { matchValuesMap.forEach((value, index) => { if (value === columnValue || Number(value) === Number(columnValue)) { rowIndexes.push(index); } }); } else { const rowIndex = matchValuesMap.findIndex((value) => value === columnValue || Number(value) === Number(columnValue)); if (rowIndex !== -1) rowIndexes.push(rowIndex); } if (!rowIndexes.length) { appendData.push(json); continue; } const updatedRow = []; for (const columnName of columns) { const updateValue = json[columnName] === undefined ? null : json[columnName]; updatedRow.push(updateValue); } for (const rowIndex of rowIndexes) { values[rowIndex] = updatedRow; //add rows index and shift by 1 to account for header row updatedRowsIndexes.add(rowIndex + 1); } } const updatedData = [columns, ...values]; const updatedRows = [0, ...Array.from(updatedRowsIndexes)]; const summary = { updatedData, appendData, updatedRows }; return summary; } const checkRange = (node, range) => { const rangeRegex = /^[A-Z]+:[A-Z]+$/i; if (rangeRegex.test(range)) { throw new n8n_workflow_1.NodeOperationError(node, `Specify the range more precisely e.g. A1:B5, generic ranges like ${range} are not supported`); } }; exports.checkRange = checkRange; /** * Parses strings like A1:B5 to Sheet!A1:B5 into detailed range information * If the range does not have an end, it will be assumed to be the same as the start. E.g. A1 will be parsed as A1:A1 */ const parseAddress = (addressOrRange) => { // remove sheet name const range = addressOrRange.replace(/^.+!/, ''); const [rangeFrom, rangeTo] = range.split(':'); const cellFrom = rangeFrom.match(exports.CELL_REGEX) ?? []; if (cellFrom.length < 2) { throw new Error(`Failed to parse range: ${addressOrRange}`); } const cellTo = (rangeTo ?? rangeFrom)?.match(exports.CELL_REGEX) ?? []; if (cellTo.length < 2) { throw new Error(`Failed to parse range: ${addressOrRange}`); } return { cellFrom: { value: rangeFrom, column: cellFrom[1], row: cellFrom[2], }, cellTo: { value: rangeTo ?? rangeFrom, column: cellTo[1], row: cellTo[2], }, }; }; exports.parseAddress = parseAddress; /** * Finds a next column in the sequence of columns in Excel * Example: * A -> B * Z -> AA */ const nextExcelColumn = (col, offset = 1) => { if (offset < 0) { throw new Error(`Invalid offset: ${offset}`); } if (offset === 0) { return col; } const toNumber = (s) => { return s.split('').reduce((acc, c) => acc * 26 + (c.charCodeAt(0) - 64), 0); }; const toLetters = (n) => { if (n <= 26) { return String.fromCharCode(64 + n); } else { const rem = ((n - 1) % 26) + 1; const div = Math.floor((n - 1) / 26); return toLetters(div) + String.fromCharCode(64 + rem); } }; const num = toNumber(col); return toLetters(num + offset); }; exports.nextExcelColumn = nextExcelColumn; /** * Accepts a used range and finds a new area under the used range. * Changes the new area based on the number of columns and rows inserted. * Example: * A1:B2 -> A3:B4 */ const findAppendRange = (usedRange, { cols, rows }) => { const { cellFrom, cellTo } = (0, exports.parseAddress)(usedRange); const isEmptyTable = cellFrom.value === cellTo.value; // if table is empty we don't want to skip the first row const rowOffset = isEmptyTable ? 0 : 1; const startingCell = { column: cellFrom.column, row: Number(cellTo.row) + rowOffset, }; const from = `${startingCell.column}${startingCell.row}`; const nextColumn = (0, exports.nextExcelColumn)(startingCell.column, Math.max(cols - 1, 0)); const to = `${nextColumn}${Number(startingCell.row) + Math.max(rows - 1, 0)}`; return `${from}:${to}`; }; exports.findAppendRange = findAppendRange; //# sourceMappingURL=utils.js.map