n8n-python-hari2
Version:
214 lines • 7.93 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PythonFunction = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const child_process_1 = require("child_process");
const path = require("path");
const fs = require("fs");
const tempy = require("tempy");
class PythonFunction {
constructor() {
this.description = {
displayName: 'Python Function',
name: 'pythonFunction',
icon: 'fa:code',
group: ['transform'],
version: 1,
description: 'Run custom Python 3.10 code which gets executed once and allows you to add, remove, change and replace items',
defaults: {
name: 'PythonFunction',
color: '#4B8BBE',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'pythonEnvVars',
required: false,
},
],
properties: [
{
displayName: 'Python Code',
name: 'functionCode',
typeOptions: {
alwaysOpenEditWindow: true,
rows: 10,
},
type: 'string',
default: `
# Code here will run only once, no matter how many input items there are.
# More info and help: https://github.com/naskio/n8n-nodes-python
return items
`,
description: 'The Python code to execute.',
noDataExpression: true,
},
],
};
}
async execute() {
var _a;
let items = this.getInputData();
items = JSON.parse(JSON.stringify(items));
const functionCode = this.getNodeParameter('functionCode', 0);
let pythonEnvVars = {};
try {
pythonEnvVars = parseEnvFile(String(((_a = (await this.getCredentials('pythonEnvVars'))) === null || _a === void 0 ? void 0 : _a.envFileContent) || ''));
}
catch (_) {
}
let scriptPath = '';
let jsonPath = '';
try {
scriptPath = await getTemporaryScriptPath(functionCode);
jsonPath = await getTemporaryJsonFilePath(unwrapJsonField(items));
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Could not generate temporary files: ${error.message}`);
}
try {
const execResults = await execPythonSpawn(scriptPath, jsonPath, pythonEnvVars, this.sendMessageToUI);
const { error: returnedError, exitCode, items: returnedItems, } = execResults;
items = wrapJsonField(returnedItems);
if (returnedError !== undefined) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `exitCode: ${exitCode} ${(returnedError === null || returnedError === void 0 ? void 0 : returnedError.message) || ''}`);
}
if (items === undefined) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No data got returned. Always return an Array of items!');
}
if (!Array.isArray(items)) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Always an Array of items has to be returned!');
}
for (const item of items) {
if (item.json === undefined) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'All returned items have to contain a property named "json"!');
}
if (typeof item.json !== 'object') {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'The json-property has to be an object!');
}
if (item.binary !== undefined) {
if (Array.isArray(item.binary) || typeof item.binary !== 'object') {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'The binary-property has to be an object!');
}
}
}
}
catch (error) {
if (this.continueOnFail()) {
items = [{ json: { error: error.message } }];
}
else {
const stackLines = error.stack.split('\n');
if (stackLines.length > 0) {
const lineParts = stackLines[1].split(':');
if (lineParts.length > 2) {
const lineNumber = lineParts.splice(-2, 1);
if (!isNaN(lineNumber)) {
error.message = `${error.message} [Line ${lineNumber}]`;
}
}
}
throw error;
}
}
return this.prepareOutputData(items);
}
}
exports.PythonFunction = PythonFunction;
function parseShellOutput(outputStr) {
return JSON.parse(outputStr);
}
function execPythonSpawn(scriptPath, jsonPath, envVars, stdoutListener) {
const returnData = {
error: undefined,
exitCode: 0,
stderr: '',
stdout: '',
};
return new Promise((resolve, reject) => {
const child = (0, child_process_1.spawn)('python3', [scriptPath, '--json_path', jsonPath, '--env_vars', JSON.stringify(envVars)], {
cwd: process.cwd(),
});
child.stdout.on('data', data => {
returnData.stdout += data.toString();
if (stdoutListener) {
stdoutListener(data.toString());
}
});
child.stderr.on('data', data => {
returnData.stderr += data.toString();
});
child.on('error', (error) => {
returnData.error = error;
resolve(returnData);
});
child.on('close', code => {
returnData.exitCode = code || 0;
if (!code) {
returnData.items = parseShellOutput(returnData.stderr);
}
else {
returnData.error = new Error(returnData.stderr);
}
resolve(returnData);
});
});
}
function parseEnvFile(envFileContent) {
if (!envFileContent || envFileContent === '') {
return {};
}
const envLines = envFileContent.split('\n');
const envVars = {};
for (const line of envLines) {
const parts = line.split('=');
if (parts.length === 2) {
envVars[parts[0]] = parts[1];
}
}
return envVars;
}
function formatCodeSnippet(code) {
return code
.replace(/\n/g, '\n\t')
.replace(/\r/g, '\n\t')
.replace(/\r\n\t/g, '\n\t')
.replace(/\r\n/g, '\n\t');
}
function getScriptCode(codeSnippet) {
const css = fs.readFileSync(path.resolve(__dirname, 'script.template.py'), 'utf8') || '';
return css.replace('pass', formatCodeSnippet(codeSnippet));
}
async function getTemporaryScriptPath(codeSnippet) {
const tmpPath = tempy.file({ extension: 'py' });
const codeStr = getScriptCode(codeSnippet);
fs.writeFileSync(tmpPath, codeStr);
return tmpPath;
}
async function getTemporaryJsonFilePath(data) {
const tmpPath = tempy.file({ extension: 'json' });
const jsonStr = JSON.stringify(data);
fs.writeFileSync(tmpPath, jsonStr);
return tmpPath;
}
function unwrapJsonField(list = []) {
return list.reduce((acc, item) => {
if ('json' in item) {
acc.push(item.json);
}
else {
acc.push(item);
}
return acc;
}, []);
}
function wrapJsonField(list = []) {
return list.reduce((acc, item) => {
const newItem = Object.assign({}, item);
newItem.json = item;
acc.push(newItem);
return acc;
}, []);
}
//# sourceMappingURL=PythonFunction.node.js.map