mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 20:54:07 -08:00
69745c93d8
Fixed some improper grammar in the 'Keep Source' toggle of the Move Binary Data node in order to improve readability
499 lines
12 KiB
TypeScript
499 lines
12 KiB
TypeScript
import {
|
|
get,
|
|
set,
|
|
unset,
|
|
} from 'lodash';
|
|
|
|
import {
|
|
BINARY_ENCODING,
|
|
} from 'n8n-core';
|
|
|
|
import { IExecuteFunctions } from 'n8n-core';
|
|
import {
|
|
IBinaryData,
|
|
IDataObject,
|
|
INodeExecutionData,
|
|
INodePropertyOptions,
|
|
INodeType,
|
|
INodeTypeDescription,
|
|
NodeOperationError,
|
|
} from 'n8n-workflow';
|
|
|
|
import * as iconv from 'iconv-lite';
|
|
iconv.encodingExists('utf8');
|
|
|
|
// Create options for bomAware and encoding
|
|
const bomAware: string[] = [];
|
|
const encodeDecodeOptions: INodePropertyOptions[] = [];
|
|
const encodings = (iconv as any).encodings; // tslint:disable-line:no-any
|
|
Object.keys(encodings).forEach(encoding => {
|
|
if (!(encoding.startsWith('_') || typeof encodings[encoding] === 'string')) { // only encodings without direct alias or internals
|
|
if (encodings[encoding].bomAware) {
|
|
bomAware.push(encoding);
|
|
}
|
|
encodeDecodeOptions.push({ name: encoding, value: encoding });
|
|
}
|
|
});
|
|
|
|
encodeDecodeOptions.sort((a, b) => {
|
|
if (a.name < b.name) { return -1; }
|
|
if (a.name > b.name) { return 1; }
|
|
return 0;
|
|
});
|
|
|
|
export class MoveBinaryData implements INodeType {
|
|
description: INodeTypeDescription = {
|
|
displayName: 'Move Binary Data',
|
|
name: 'moveBinaryData',
|
|
icon: 'fa:exchange-alt',
|
|
group: ['transform'],
|
|
version: 1,
|
|
subtitle: '={{$parameter["mode"]==="binaryToJson" ? "Binary to JSON" : "JSON to Binary"}}',
|
|
description: 'Move data between binary and JSON properties',
|
|
defaults: {
|
|
name: 'Move Binary Data',
|
|
color: '#7722CC',
|
|
},
|
|
inputs: ['main'],
|
|
outputs: ['main'],
|
|
properties: [
|
|
{
|
|
displayName: 'Mode',
|
|
name: 'mode',
|
|
type: 'options',
|
|
options: [
|
|
{
|
|
name: 'Binary to JSON',
|
|
value: 'binaryToJson',
|
|
description: 'Move data from Binary to JSON',
|
|
},
|
|
{
|
|
name: 'JSON to Binary',
|
|
value: 'jsonToBinary',
|
|
description: 'Move data from JSON to Binary.',
|
|
},
|
|
],
|
|
default: 'binaryToJson',
|
|
description: 'From and to where data should be moved.',
|
|
},
|
|
|
|
|
|
// ----------------------------------
|
|
// binaryToJson
|
|
// ----------------------------------
|
|
{
|
|
displayName: 'Set all Data',
|
|
name: 'setAllData',
|
|
type: 'boolean',
|
|
displayOptions: {
|
|
show: {
|
|
mode: [
|
|
'binaryToJson',
|
|
],
|
|
},
|
|
},
|
|
default: true,
|
|
description: 'If all JSON data should be replaced with the data retrieved<br />from binary key. Else the data will be written to a single key.',
|
|
},
|
|
{
|
|
displayName: 'Source Key',
|
|
name: 'sourceKey',
|
|
type: 'string',
|
|
displayOptions: {
|
|
show: {
|
|
mode: [
|
|
'binaryToJson',
|
|
],
|
|
},
|
|
},
|
|
default: 'data',
|
|
required: true,
|
|
placeholder: 'data',
|
|
description: 'The name of the binary key to get data from.<br />It is also possible to define deep keys by using dot-notation like for example:<br />"level1.level2.currentKey"',
|
|
},
|
|
{
|
|
displayName: 'Destination Key',
|
|
name: 'destinationKey',
|
|
type: 'string',
|
|
displayOptions: {
|
|
show: {
|
|
mode: [
|
|
'binaryToJson',
|
|
],
|
|
setAllData: [
|
|
false,
|
|
],
|
|
},
|
|
},
|
|
default: 'data',
|
|
required: true,
|
|
placeholder: '',
|
|
description: 'The name the JSON key to copy data to. It is also possible<br />to define deep keys by using dot-notation like for example:<br />"level1.level2.newKey"',
|
|
},
|
|
|
|
// ----------------------------------
|
|
// jsonToBinary
|
|
// ----------------------------------
|
|
{
|
|
displayName: 'Convert all Data',
|
|
name: 'convertAllData',
|
|
type: 'boolean',
|
|
displayOptions: {
|
|
show: {
|
|
mode: [
|
|
'jsonToBinary',
|
|
],
|
|
},
|
|
},
|
|
default: true,
|
|
description: 'If all JSON data should be converted to binary.<br />Else only the data of one key will be converted.',
|
|
},
|
|
{
|
|
displayName: 'Source Key',
|
|
name: 'sourceKey',
|
|
type: 'string',
|
|
displayOptions: {
|
|
show: {
|
|
convertAllData: [
|
|
false,
|
|
],
|
|
mode: [
|
|
'jsonToBinary',
|
|
],
|
|
},
|
|
},
|
|
default: 'data',
|
|
required: true,
|
|
placeholder: 'data',
|
|
description: 'The name of the JSON key to get data from. It is also possible<br />to define deep keys by using dot-notation like for example:<br />"level1.level2.currentKey"',
|
|
},
|
|
{
|
|
displayName: 'Destination Key',
|
|
name: 'destinationKey',
|
|
type: 'string',
|
|
displayOptions: {
|
|
show: {
|
|
mode: [
|
|
'jsonToBinary',
|
|
],
|
|
},
|
|
},
|
|
default: 'data',
|
|
required: true,
|
|
placeholder: 'data',
|
|
description: 'The name the binary key to copy data to. It is also possible<br />to define deep keys by using dot-notation like for example:<br />"level1.level2.newKey"',
|
|
},
|
|
|
|
{
|
|
displayName: 'Options',
|
|
name: 'options',
|
|
type: 'collection',
|
|
placeholder: 'Add Option',
|
|
default: {},
|
|
options: [
|
|
{
|
|
displayName: 'Data Is Base64',
|
|
name: 'dataIsBase64',
|
|
type: 'boolean',
|
|
displayOptions: {
|
|
hide: {
|
|
'useRawData': [
|
|
true,
|
|
],
|
|
},
|
|
show: {
|
|
'/mode': [
|
|
'jsonToBinary',
|
|
],
|
|
'/convertAllData': [
|
|
false,
|
|
],
|
|
},
|
|
},
|
|
default: false,
|
|
description: 'Keeps the binary data as base64 string.',
|
|
},
|
|
{
|
|
displayName: 'Encoding',
|
|
name: 'encoding',
|
|
type: 'options',
|
|
options: encodeDecodeOptions,
|
|
displayOptions: {
|
|
show: {
|
|
'/mode': [
|
|
'binaryToJson',
|
|
'jsonToBinary',
|
|
],
|
|
},
|
|
},
|
|
default: 'utf8',
|
|
description: 'Set the encoding of the data stream',
|
|
},
|
|
{
|
|
displayName: 'Strip BOM',
|
|
name: 'stripBOM',
|
|
displayOptions: {
|
|
show: {
|
|
'/mode': [
|
|
'binaryToJson',
|
|
],
|
|
encoding: bomAware,
|
|
},
|
|
},
|
|
type: 'boolean',
|
|
default: true,
|
|
},
|
|
{
|
|
displayName: 'Add BOM',
|
|
name: 'addBOM',
|
|
displayOptions: {
|
|
show: {
|
|
'/mode': [
|
|
'jsonToBinary',
|
|
],
|
|
encoding: bomAware,
|
|
},
|
|
},
|
|
type: 'boolean',
|
|
default: false,
|
|
},
|
|
{
|
|
displayName: 'File Name',
|
|
name: 'fileName',
|
|
type: 'string',
|
|
displayOptions: {
|
|
show: {
|
|
'/mode': [
|
|
'jsonToBinary',
|
|
],
|
|
},
|
|
},
|
|
default: '',
|
|
placeholder: 'example.json',
|
|
description: 'The file name to set.',
|
|
},
|
|
{
|
|
displayName: 'JSON Parse',
|
|
name: 'jsonParse',
|
|
type: 'boolean',
|
|
displayOptions: {
|
|
hide: {
|
|
'keepAsBase64': [
|
|
true,
|
|
],
|
|
},
|
|
show: {
|
|
'/mode': [
|
|
'binaryToJson',
|
|
],
|
|
'/setAllData': [
|
|
false,
|
|
],
|
|
},
|
|
},
|
|
default: false,
|
|
description: 'Run JSON parse on the data to get proper object data.',
|
|
},
|
|
{
|
|
displayName: 'Keep Source',
|
|
name: 'keepSource',
|
|
type: 'boolean',
|
|
default: false,
|
|
description: 'If the source key should be kept. By default it will be deleted.',
|
|
},
|
|
{
|
|
displayName: 'Keep As Base64',
|
|
name: 'keepAsBase64',
|
|
type: 'boolean',
|
|
displayOptions: {
|
|
hide: {
|
|
'jsonParse': [
|
|
true,
|
|
],
|
|
},
|
|
show: {
|
|
'/mode': [
|
|
'binaryToJson',
|
|
],
|
|
'/setAllData': [
|
|
false,
|
|
],
|
|
},
|
|
},
|
|
default: false,
|
|
description: 'Keeps the binary data as base64 string.',
|
|
},
|
|
{
|
|
displayName: 'Mime Type',
|
|
name: 'mimeType',
|
|
type: 'string',
|
|
displayOptions: {
|
|
show: {
|
|
'/mode': [
|
|
'jsonToBinary',
|
|
],
|
|
},
|
|
},
|
|
default: 'application/json',
|
|
placeholder: 'application/json',
|
|
description: 'The mime-type to set. By default will the mime-type for JSON be set.',
|
|
},
|
|
{
|
|
displayName: 'Use Raw Data',
|
|
name: 'useRawData',
|
|
type: 'boolean',
|
|
displayOptions: {
|
|
hide: {
|
|
'dataIsBase64': [
|
|
true,
|
|
],
|
|
},
|
|
show: {
|
|
'/mode': [
|
|
'jsonToBinary',
|
|
],
|
|
},
|
|
},
|
|
default: false,
|
|
description: 'Use data as is and do not JSON.stringify it.',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
|
|
const items = this.getInputData();
|
|
|
|
const mode = this.getNodeParameter('mode', 0) as string;
|
|
|
|
const returnData: INodeExecutionData[] = [];
|
|
|
|
let item: INodeExecutionData;
|
|
let newItem: INodeExecutionData;
|
|
let options: IDataObject;
|
|
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
item = items[itemIndex];
|
|
options = this.getNodeParameter('options', itemIndex, {}) as IDataObject;
|
|
|
|
// Copy the whole JSON data as data on any level can be renamed
|
|
newItem = {
|
|
json: {},
|
|
};
|
|
|
|
if (mode === 'binaryToJson') {
|
|
const setAllData = this.getNodeParameter('setAllData', itemIndex) as boolean;
|
|
const sourceKey = this.getNodeParameter('sourceKey', itemIndex) as string;
|
|
|
|
const value = get(item.binary, sourceKey);
|
|
|
|
if (value === undefined) {
|
|
// No data found so skip
|
|
continue;
|
|
}
|
|
|
|
const encoding = (options.encoding as string) || 'utf8';
|
|
let convertedValue = value.data;
|
|
|
|
if (setAllData === true) {
|
|
// Set the full data
|
|
convertedValue = iconv.decode(Buffer.from(convertedValue, BINARY_ENCODING), encoding, { stripBOM: options.stripBOM as boolean });
|
|
newItem.json = JSON.parse(convertedValue);
|
|
} else {
|
|
// Does get added to existing data so copy it first
|
|
newItem.json = JSON.parse(JSON.stringify(item.json));
|
|
|
|
if (options.keepAsBase64 !== true) {
|
|
convertedValue = iconv.decode(Buffer.from(convertedValue, BINARY_ENCODING), encoding, { stripBOM: options.stripBOM as boolean });
|
|
}
|
|
|
|
if (options.jsonParse) {
|
|
convertedValue = JSON.parse(convertedValue);
|
|
}
|
|
|
|
const destinationKey = this.getNodeParameter('destinationKey', itemIndex, '') as string;
|
|
set(newItem.json, destinationKey, convertedValue);
|
|
}
|
|
|
|
if (options.keepSource === true) {
|
|
// Binary data does not get touched so simply reference it
|
|
newItem.binary = item.binary;
|
|
} else {
|
|
// Binary data will change so copy it
|
|
newItem.binary = JSON.parse(JSON.stringify(item.binary));
|
|
unset(newItem.binary, sourceKey);
|
|
}
|
|
|
|
} else if (mode === 'jsonToBinary') {
|
|
const convertAllData = this.getNodeParameter('convertAllData', itemIndex) as boolean;
|
|
const destinationKey = this.getNodeParameter('destinationKey', itemIndex) as string;
|
|
|
|
const encoding = (options.encoding as string) || 'utf8';
|
|
let value: IDataObject | string = item.json;
|
|
if (convertAllData === false) {
|
|
const sourceKey = this.getNodeParameter('sourceKey', itemIndex) as string;
|
|
value = get(item.json, sourceKey) as IDataObject;
|
|
}
|
|
|
|
if (value === undefined) {
|
|
// No data found so skip
|
|
continue;
|
|
}
|
|
|
|
if (item.binary !== undefined) {
|
|
// Item already has binary data so copy it
|
|
newItem.binary = JSON.parse(JSON.stringify(item.binary));
|
|
} else {
|
|
// Item does not have binary data yet so initialize empty
|
|
newItem.binary = {};
|
|
}
|
|
|
|
if (options.dataIsBase64 !== true) {
|
|
if (options.useRawData !== true) {
|
|
value = JSON.stringify(value);
|
|
}
|
|
|
|
value = iconv.encode(value as string, encoding, { addBOM: options.addBOM as boolean }).toString(BINARY_ENCODING);
|
|
}
|
|
|
|
const convertedValue: IBinaryData = {
|
|
data: value as string,
|
|
mimeType: (options.mimeType as string) || 'application/json',
|
|
};
|
|
|
|
if (options.fileName) {
|
|
convertedValue.fileName = options.fileName as string;
|
|
}
|
|
|
|
set(newItem.binary!, destinationKey, convertedValue);
|
|
|
|
if (options.keepSource === true) {
|
|
// JSON data does not get touched so simply reference it
|
|
newItem.json = item.json;
|
|
} else {
|
|
// JSON data will change so copy it
|
|
|
|
if (convertAllData === true) {
|
|
// Data should not be kept and all data got converted. So simply set new as empty
|
|
newItem.json = {};
|
|
} else {
|
|
// Data should not be kept and only one key has to get removed. So copy all
|
|
// data and then remove the not needed one
|
|
newItem.json = JSON.parse(JSON.stringify(item.json));
|
|
const sourceKey = this.getNodeParameter('sourceKey', itemIndex) as string;
|
|
|
|
unset(newItem.json, sourceKey);
|
|
}
|
|
}
|
|
} else {
|
|
throw new NodeOperationError(this.getNode(), `The operation "${mode}" is not known!`);
|
|
}
|
|
|
|
returnData.push(newItem);
|
|
}
|
|
|
|
return [returnData];
|
|
}
|
|
}
|