diff --git a/cypress/e2e/16-webhook-node.cy.ts b/cypress/e2e/16-webhook-node.cy.ts
index c32a1407dc565..da43a7cf4b14f 100644
--- a/cypress/e2e/16-webhook-node.cy.ts
+++ b/cypress/e2e/16-webhook-node.cy.ts
@@ -187,12 +187,14 @@ describe('Webhook Trigger node', async () => {
ndv.getters.backToCanvas().click();
- workflowPage.actions.addNodeToCanvas('Convert to/from binary data');
+ workflowPage.actions.addNodeToCanvas('Convert to File');
workflowPage.actions.zoomToFit();
- workflowPage.actions.openNode('Convert to/from binary data');
+ workflowPage.actions.openNode('Convert to File');
+ cy.getByTestId('parameter-input-operation').click();
+ getVisibleSelect().find('.option-headline').contains('Convert to JSON').click();
cy.getByTestId('parameter-input-mode').click();
- getVisibleSelect().find('.option-headline').contains('JSON to Binary').click();
+ getVisibleSelect().find('.option-headline').contains('Each Item to Separate File').click();
ndv.getters.backToCanvas().click();
workflowPage.actions.executeWorkflow();
diff --git a/cypress/e2e/4-node-creator.cy.ts b/cypress/e2e/4-node-creator.cy.ts
index 49ff848cfee36..42f08ba51b543 100644
--- a/cypress/e2e/4-node-creator.cy.ts
+++ b/cypress/e2e/4-node-creator.cy.ts
@@ -110,7 +110,7 @@ describe('Node Creator', () => {
it('should not show actions for single action nodes', () => {
const singleActionNodes = [
'DHL',
- 'iCalendar',
+ 'Edit Fields',
'LingvaNex',
'Mailcheck',
'MSG91',
@@ -484,8 +484,9 @@ describe('Node Creator', () => {
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Wait');
nodeCreatorFeature.getters.searchBar().find('input').clear().type('spreadsheet');
- nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Spreadsheet File');
- nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Google Sheets');
+ nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Convert to File');
+ nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Extract From File');
+ nodeCreatorFeature.getters.nodeItemName().eq(2).should('have.text', 'Google Sheets');
nodeCreatorFeature.getters.searchBar().find('input').clear().type('sheets');
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Google Sheets');
diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts
index df6f7865821d7..2c014c6487f6c 100644
--- a/packages/core/src/NodeExecuteFunctions.ts
+++ b/packages/core/src/NodeExecuteFunctions.ts
@@ -987,16 +987,27 @@ export function assertBinaryData(
): IBinaryData {
const binaryKeyData = inputData.main[inputIndex]![itemIndex]!.binary;
if (binaryKeyData === undefined) {
- throw new NodeOperationError(node, 'No binary data exists on item!', {
- itemIndex,
- });
+ throw new NodeOperationError(
+ node,
+ `This operation expects the node's input data to contain a binary file '${propertyName}', but none was found [item ${itemIndex}]`,
+ {
+ itemIndex,
+ description: 'Make sure that the previous node outputs a binary file',
+ },
+ );
}
const binaryPropertyData = binaryKeyData[propertyName];
if (binaryPropertyData === undefined) {
- throw new NodeOperationError(node, `Item has no binary property called "${propertyName}"`, {
- itemIndex,
- });
+ throw new NodeOperationError(
+ node,
+ `The item has no binary field '${propertyName}' [item ${itemIndex}]`,
+ {
+ itemIndex,
+ description:
+ 'Check that the parameter where you specified the input binary field name is correct, and that it matches a field in the binary input',
+ },
+ );
}
return binaryPropertyData;
diff --git a/packages/editor-ui/src/components/Node/NodeCreator/viewsData.ts b/packages/editor-ui/src/components/Node/NodeCreator/viewsData.ts
index 91a8088807685..109f21ad62e08 100644
--- a/packages/editor-ui/src/components/Node/NodeCreator/viewsData.ts
+++ b/packages/editor-ui/src/components/Node/NodeCreator/viewsData.ts
@@ -28,6 +28,8 @@ import {
AI_CATEGORY_EMBEDDING,
AI_OTHERS_NODE_CREATOR_VIEW,
AI_UNCATEGORIZED_CATEGORY,
+ CONVERT_TO_FILE_NODE_TYPE,
+ EXTRACT_FROM_FILE_NODE_TYPE,
SET_NODE_TYPE,
CODE_NODE_TYPE,
DATETIME_NODE_TYPE,
@@ -48,6 +50,8 @@ import {
HELPERS_SUBCATEGORY,
RSS_READ_NODE_TYPE,
EMAIL_SEND_NODE_TYPE,
+ EDIT_IMAGE_NODE_TYPE,
+ COMPRESSION_NODE_TYPE,
} from '@/constants';
import { useI18n } from '@/composables/useI18n';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
@@ -394,7 +398,16 @@ export function RegularView(nodes: SimplifiedNodeType[]) {
{
key: 'convert',
title: i18n.baseText('nodeCreator.sectionNames.transform.convert'),
- items: [HTML_NODE_TYPE, MARKDOWN_NODE_TYPE, XML_NODE_TYPE, CRYPTO_NODE_TYPE],
+ items: [
+ HTML_NODE_TYPE,
+ MARKDOWN_NODE_TYPE,
+ XML_NODE_TYPE,
+ CRYPTO_NODE_TYPE,
+ EXTRACT_FROM_FILE_NODE_TYPE,
+ CONVERT_TO_FILE_NODE_TYPE,
+ COMPRESSION_NODE_TYPE,
+ EDIT_IMAGE_NODE_TYPE,
+ ],
},
],
},
@@ -422,6 +435,13 @@ export function RegularView(nodes: SimplifiedNodeType[]) {
properties: {
title: FILES_SUBCATEGORY,
icon: 'file-alt',
+ sections: [
+ {
+ key: 'popular',
+ title: i18n.baseText('nodeCreator.sectionNames.popular'),
+ items: [CONVERT_TO_FILE_NODE_TYPE, EXTRACT_FROM_FILE_NODE_TYPE],
+ },
+ ],
},
},
{
diff --git a/packages/editor-ui/src/constants.ts b/packages/editor-ui/src/constants.ts
index d16c8ac34b04a..8e11557032bbc 100644
--- a/packages/editor-ui/src/constants.ts
+++ b/packages/editor-ui/src/constants.ts
@@ -161,6 +161,8 @@ export const XERO_NODE_TYPE = 'n8n-nodes-base.xero';
export const ZENDESK_NODE_TYPE = 'n8n-nodes-base.zendesk';
export const ZENDESK_TRIGGER_NODE_TYPE = 'n8n-nodes-base.zendeskTrigger';
export const DISCORD_NODE_TYPE = 'n8n-nodes-base.discord';
+export const EXTRACT_FROM_FILE_NODE_TYPE = 'n8n-nodes-base.extractFromFile';
+export const CONVERT_TO_FILE_NODE_TYPE = 'n8n-nodes-base.convertToFile';
export const DATETIME_NODE_TYPE = 'n8n-nodes-base.dateTime';
export const REMOVE_DUPLICATES_NODE_TYPE = 'n8n-nodes-base.removeDuplicates';
export const SPLIT_OUT_NODE_TYPE = 'n8n-nodes-base.splitOut';
@@ -172,6 +174,8 @@ export const MARKDOWN_NODE_TYPE = 'n8n-nodes-base.markdown';
export const XML_NODE_TYPE = 'n8n-nodes-base.xml';
export const CRYPTO_NODE_TYPE = 'n8n-nodes-base.crypto';
export const RSS_READ_NODE_TYPE = 'n8n-nodes-base.rssFeedRead';
+export const COMPRESSION_NODE_TYPE = 'n8n-nodes-base.compression';
+export const EDIT_IMAGE_NODE_TYPE = 'n8n-nodes-base.editImage';
export const CREDENTIAL_ONLY_NODE_PREFIX = 'n8n-creds-base';
export const CREDENTIAL_ONLY_HTTP_NODE_VERSION = 4.1;
diff --git a/packages/nodes-base/nodes/ApiTemplateIo/ApiTemplateIo.node.ts b/packages/nodes-base/nodes/ApiTemplateIo/ApiTemplateIo.node.ts
index 0d3560fe1d175..765a3fc76c988 100644
--- a/packages/nodes-base/nodes/ApiTemplateIo/ApiTemplateIo.node.ts
+++ b/packages/nodes-base/nodes/ApiTemplateIo/ApiTemplateIo.node.ts
@@ -181,12 +181,12 @@ export class ApiTemplateIo implements INodeType {
description: 'Name of the binary property to which to write the data of the read file',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryProperty',
type: 'string',
required: true,
default: 'data',
- description: 'Name of the binary property to which to write to',
+ hint: 'The name of the output binary field to put the file in',
displayOptions: {
show: {
resource: ['pdf', 'image'],
diff --git a/packages/nodes-base/nodes/Aws/Rekognition/AwsRekognition.node.ts b/packages/nodes-base/nodes/Aws/Rekognition/AwsRekognition.node.ts
index 99345befbfc33..405b26637d200 100644
--- a/packages/nodes-base/nodes/Aws/Rekognition/AwsRekognition.node.ts
+++ b/packages/nodes-base/nodes/Aws/Rekognition/AwsRekognition.node.ts
@@ -90,7 +90,7 @@ export class AwsRekognition implements INodeType {
default: 'detectFaces',
},
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
@@ -104,7 +104,7 @@ export class AwsRekognition implements INodeType {
description: 'Whether the image to analyze should be taken from binary field',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
displayOptions: {
show: {
operation: ['analyze'],
@@ -115,7 +115,7 @@ export class AwsRekognition implements INodeType {
name: 'binaryPropertyName',
type: 'string',
default: 'data',
- description: 'Object property name which holds binary data',
+ hint: 'The name of the input binary field containing the file to be written',
required: true,
},
{
diff --git a/packages/nodes-base/nodes/Aws/S3/V1/FileDescription.ts b/packages/nodes-base/nodes/Aws/S3/V1/FileDescription.ts
index be5146777efed..6328aea0a0065 100644
--- a/packages/nodes-base/nodes/Aws/S3/V1/FileDescription.ts
+++ b/packages/nodes-base/nodes/Aws/S3/V1/FileDescription.ts
@@ -373,7 +373,7 @@ export const fileFields: INodeProperties[] = [
description: 'If not set the binary data filename will be used',
},
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: true,
@@ -401,7 +401,7 @@ export const fileFields: INodeProperties[] = [
description: 'The text content of the file to upload',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -414,7 +414,7 @@ export const fileFields: INodeProperties[] = [
},
},
placeholder: '',
- description: 'Name of the binary property which contains the data for the file to be uploaded',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'Additional Fields',
@@ -698,7 +698,7 @@ export const fileFields: INodeProperties[] = [
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -709,7 +709,7 @@ export const fileFields: INodeProperties[] = [
resource: ['file'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
/* -------------------------------------------------------------------------- */
/* file:delete */
diff --git a/packages/nodes-base/nodes/Aws/S3/V2/FileDescription.ts b/packages/nodes-base/nodes/Aws/S3/V2/FileDescription.ts
index be5146777efed..6328aea0a0065 100644
--- a/packages/nodes-base/nodes/Aws/S3/V2/FileDescription.ts
+++ b/packages/nodes-base/nodes/Aws/S3/V2/FileDescription.ts
@@ -373,7 +373,7 @@ export const fileFields: INodeProperties[] = [
description: 'If not set the binary data filename will be used',
},
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: true,
@@ -401,7 +401,7 @@ export const fileFields: INodeProperties[] = [
description: 'The text content of the file to upload',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -414,7 +414,7 @@ export const fileFields: INodeProperties[] = [
},
},
placeholder: '',
- description: 'Name of the binary property which contains the data for the file to be uploaded',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'Additional Fields',
@@ -698,7 +698,7 @@ export const fileFields: INodeProperties[] = [
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -709,7 +709,7 @@ export const fileFields: INodeProperties[] = [
resource: ['file'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
/* -------------------------------------------------------------------------- */
/* file:delete */
diff --git a/packages/nodes-base/nodes/Box/FileDescription.ts b/packages/nodes-base/nodes/Box/FileDescription.ts
index 896100ffe9160..48c49157409f4 100644
--- a/packages/nodes-base/nodes/Box/FileDescription.ts
+++ b/packages/nodes-base/nodes/Box/FileDescription.ts
@@ -161,7 +161,7 @@ export const fileFields: INodeProperties[] = [
default: '',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -172,7 +172,7 @@ export const fileFields: INodeProperties[] = [
resource: ['file'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
/* -------------------------------------------------------------------------- */
@@ -671,7 +671,7 @@ export const fileFields: INodeProperties[] = [
description: 'The name the file should be saved as',
},
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
@@ -700,7 +700,7 @@ export const fileFields: INodeProperties[] = [
description: 'The text content of the file',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -712,7 +712,7 @@ export const fileFields: INodeProperties[] = [
resource: ['file'],
},
},
- description: 'Name of the binary property which contains the data for the file',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'Parent ID',
diff --git a/packages/nodes-base/nodes/Cisco/Webex/descriptions/MessageDescription.ts b/packages/nodes-base/nodes/Cisco/Webex/descriptions/MessageDescription.ts
index c9f7fc3e92843..6cf90d50a58f6 100644
--- a/packages/nodes-base/nodes/Cisco/Webex/descriptions/MessageDescription.ts
+++ b/packages/nodes-base/nodes/Cisco/Webex/descriptions/MessageDescription.ts
@@ -327,7 +327,7 @@ export const messageFields: INodeProperties[] = [
value: 'url',
},
{
- name: 'Binary Data',
+ name: 'Binary File',
value: 'binaryData',
},
],
diff --git a/packages/nodes-base/nodes/Compression/Compression.node.json b/packages/nodes-base/nodes/Compression/Compression.node.json
index 5661d4476013d..c85e4aaf6b75f 100644
--- a/packages/nodes-base/nodes/Compression/Compression.node.json
+++ b/packages/nodes-base/nodes/Compression/Compression.node.json
@@ -11,8 +11,19 @@
}
]
},
- "alias": ["Zip", "Gzip", "uncompress"],
+ "alias": [
+ "Zip",
+ "Gzip",
+ "uncompress",
+ "compress",
+ "decompress",
+ "archive",
+ "unarchive",
+ "Binary",
+ "Files",
+ "File"
+ ],
"subcategories": {
- "Core Nodes": ["Files"]
+ "Core Nodes": ["Files", "Data Transformation"]
}
}
diff --git a/packages/nodes-base/nodes/Compression/Compression.node.ts b/packages/nodes-base/nodes/Compression/Compression.node.ts
index 022113a469682..a26a99657bc9b 100644
--- a/packages/nodes-base/nodes/Compression/Compression.node.ts
+++ b/packages/nodes-base/nodes/Compression/Compression.node.ts
@@ -50,7 +50,7 @@ export class Compression implements INodeType {
icon: 'fa:file-archive',
group: ['transform'],
subtitle: '={{$parameter["operation"]}}',
- version: 1,
+ version: [1, 1.1],
description: 'Compress and decompress files',
defaults: {
name: 'Compression',
@@ -68,28 +68,49 @@ export class Compression implements INodeType {
{
name: 'Compress',
value: 'compress',
+ action: 'Compress file(s)',
+ description: 'Compress files into a zip or gzip archive',
},
{
name: 'Decompress',
value: 'decompress',
+ action: 'Decompress file(s)',
+ description: 'Decompress zip or gzip archives',
},
],
default: 'decompress',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field(s)',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
- operation: ['compress', 'decompress'],
+ operation: ['compress'],
+ },
+ },
+ placeholder: 'e.g. data,data2,data3',
+ hint: 'The name of the input binary field(s) containing the file(s) to be compressed',
+ description:
+ 'To process more than one file, use a comma-separated list of the binary fields names',
+ },
+ {
+ displayName: 'Input Binary Field(s)',
+ name: 'binaryPropertyName',
+ type: 'string',
+ default: 'data',
+ required: true,
+ displayOptions: {
+ show: {
+ operation: ['decompress'],
},
},
- placeholder: '',
+ placeholder: 'e.g. data',
+ hint: 'The name of the input binary field(s) containing the file(s) to decompress',
description:
- 'Name of the binary property which contains the data for the file(s) to be compress/decompress. Multiple can be used separated by a comma (,).',
+ 'To process more than one file, use a comma-separated list of the binary fields names',
},
{
displayName: 'Output Format',
@@ -106,19 +127,45 @@ export class Compression implements INodeType {
value: 'zip',
},
],
+ displayOptions: {
+ show: {
+ operation: ['compress'],
+ '@version': [1],
+ },
+ },
+ description: 'Format of the output',
+ },
+ {
+ displayName: 'Output Format',
+ name: 'outputFormat',
+ type: 'options',
+ default: 'zip',
+ options: [
+ {
+ name: 'Gzip',
+ value: 'gzip',
+ },
+ {
+ name: 'Zip',
+ value: 'zip',
+ },
+ ],
displayOptions: {
show: {
operation: ['compress'],
},
+ hide: {
+ '@version': [1],
+ },
},
- description: 'Format of the output file',
+ description: 'Format of the output',
},
{
displayName: 'File Name',
name: 'fileName',
type: 'string',
default: '',
- placeholder: 'data.zip',
+ placeholder: 'e.g. data.zip',
required: true,
displayOptions: {
show: {
@@ -126,10 +173,10 @@ export class Compression implements INodeType {
outputFormat: ['zip'],
},
},
- description: 'Name of the file to be compressed',
+ description: 'Name of the output file',
},
{
- displayName: 'Binary Property Output',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyOutput',
type: 'string',
default: 'data',
@@ -139,12 +186,43 @@ export class Compression implements INodeType {
operation: ['compress'],
},
},
- placeholder: '',
- description:
- 'Name of the binary property to which to write the data of the compressed files',
+ hint: 'The name of the output binary field to put the file in',
},
{
- displayName: 'Output Prefix',
+ displayName: 'File Name',
+ name: 'fileName',
+ type: 'string',
+ default: '',
+ placeholder: 'e.g. data.txt',
+ displayOptions: {
+ show: {
+ operation: ['compress'],
+ outputFormat: ['gzip'],
+ },
+ hide: {
+ '@version': [1],
+ },
+ },
+ description: 'Name of the output file',
+ },
+ {
+ displayName: 'Put Output File in Field',
+ name: 'binaryPropertyOutput',
+ type: 'string',
+ default: 'data',
+ displayOptions: {
+ show: {
+ outputFormat: ['gzip'],
+ operation: ['compress'],
+ },
+ hide: {
+ '@version': [1],
+ },
+ },
+ hint: 'The name of the output binary field to put the file in',
+ },
+ {
+ displayName: 'Output File Prefix',
name: 'outputPrefix',
type: 'string',
default: 'data',
@@ -153,9 +231,10 @@ export class Compression implements INodeType {
show: {
operation: ['compress'],
outputFormat: ['gzip'],
+ '@version': [1],
},
},
- description: 'Prefix use for all gzip compressed files',
+ description: 'Prefix to add to the gzip file',
},
{
displayName: 'Output Prefix',
@@ -168,7 +247,7 @@ export class Compression implements INodeType {
operation: ['decompress'],
},
},
- description: 'Prefix use for all decompressed files',
+ description: 'Prefix to add to the decompressed files',
},
],
};
@@ -178,6 +257,7 @@ export class Compression implements INodeType {
const length = items.length;
const returnData: INodeExecutionData[] = [];
const operation = this.getNodeParameter('operation', 0);
+ const nodeVersion = this.getNode().typeVersion;
for (let i = 0; i < length; i++) {
try {
@@ -212,20 +292,37 @@ export class Compression implements INodeType {
binaryObject[`${outputPrefix}${zipIndex++}`] = data;
}
- } else if (binaryData.fileExtension?.toLowerCase() === 'gz') {
+ } else if (['gz', 'gzip'].includes(binaryData.fileExtension?.toLowerCase() as string)) {
const file = await gunzip(binaryDataBuffer);
const fileName = binaryData.fileName?.split('.')[0];
+ let fileExtension;
+ let mimeType;
+
+ if (binaryData.fileName?.endsWith('.gz')) {
+ const extractedFileExtension = binaryData.fileName.replace('.gz', '').split('.');
+ if (extractedFileExtension.length > 1) {
+ fileExtension = extractedFileExtension[extractedFileExtension.length - 1];
+ mimeType = mime.lookup(fileExtension) as string;
+ }
+ }
const propertyName = `${outputPrefix}${index}`;
binaryObject[propertyName] = await this.helpers.prepareBinaryData(
Buffer.from(file.buffer),
fileName,
+ mimeType,
);
- const fileExtension = mime.extension(binaryObject[propertyName].mimeType) as string;
+
+ if (!fileExtension) {
+ mimeType = binaryObject[propertyName].mimeType;
+ fileExtension = mime.extension(mimeType) as string;
+ }
+
binaryObject[propertyName].fileName = `${fileName}.${fileExtension}`;
binaryObject[propertyName].fileExtension = fileExtension;
+ binaryObject[propertyName].mimeType = mimeType as string;
}
}
@@ -239,14 +336,21 @@ export class Compression implements INodeType {
}
if (operation === 'compress') {
- const binaryPropertyNames = this.getNodeParameter('binaryPropertyName', 0)
+ let binaryPropertyNameIndex = 0;
+ if (nodeVersion > 1) {
+ binaryPropertyNameIndex = i;
+ }
+
+ const binaryPropertyNames = this.getNodeParameter(
+ 'binaryPropertyName',
+ binaryPropertyNameIndex,
+ )
.split(',')
.map((key) => key.trim());
const outputFormat = this.getNodeParameter('outputFormat', 0) as string;
const zipData: fflate.Zippable = {};
-
const binaryObject: IBinaryKeyData = {};
for (const [index, binaryPropertyName] of binaryPropertyNames.entries()) {
@@ -261,26 +365,53 @@ export class Compression implements INodeType {
},
];
} else if (outputFormat === 'gzip') {
- const outputPrefix = this.getNodeParameter('outputPrefix', 0) as string;
+ let outputPrefix;
+ let fileName;
+ let binaryProperty;
+ let filePath;
+
+ if (nodeVersion > 1) {
+ outputPrefix = this.getNodeParameter('binaryPropertyOutput', i, 'data');
+ binaryProperty = `${outputPrefix}${index ? index : ''}`;
+
+ fileName = this.getNodeParameter('fileName', i, '') as string;
+ if (!fileName) {
+ fileName = binaryData.fileName?.split('.')[0];
+ } else {
+ fileName = fileName.replace('.gz', '').replace('.gzip', '');
+ }
- const data = await gzip(binaryDataBuffer);
+ const fileExtension = binaryData.fileExtension
+ ? `.${binaryData.fileExtension.toLowerCase()}`
+ : '';
+ filePath = `${fileName}${fileExtension}.gz`;
+ } else {
+ outputPrefix = this.getNodeParameter('outputPrefix', 0) as string;
+ binaryProperty = `${outputPrefix}${index}`;
+ fileName = binaryData.fileName?.split('.')[0];
+ filePath = `${fileName}.gzip`;
+ }
- const fileName = binaryData.fileName?.split('.')[0];
+ const data = await gzip(binaryDataBuffer);
- binaryObject[`${outputPrefix}${index}`] = await this.helpers.prepareBinaryData(
+ binaryObject[binaryProperty] = await this.helpers.prepareBinaryData(
Buffer.from(data),
- `${fileName}.gzip`,
+ filePath,
);
}
}
if (outputFormat === 'zip') {
- const fileName = this.getNodeParameter('fileName', 0) as string;
-
- const binaryPropertyOutput = this.getNodeParameter('binaryPropertyOutput', 0);
-
+ let zipOptionsIndex = 0;
+ if (nodeVersion > 1) {
+ zipOptionsIndex = i;
+ }
+ const fileName = this.getNodeParameter('fileName', zipOptionsIndex) as string;
+ const binaryPropertyOutput = this.getNodeParameter(
+ 'binaryPropertyOutput',
+ zipOptionsIndex,
+ );
const buffer = await zip(zipData);
-
const data = await this.helpers.prepareBinaryData(Buffer.from(buffer), fileName);
returnData.push({
diff --git a/packages/nodes-base/nodes/Cortex/AnalyzerDescriptions.ts b/packages/nodes-base/nodes/Cortex/AnalyzerDescriptions.ts
index f5c78b1579c3a..449f34a593ec4 100644
--- a/packages/nodes-base/nodes/Cortex/AnalyzerDescriptions.ts
+++ b/packages/nodes-base/nodes/Cortex/AnalyzerDescriptions.ts
@@ -89,7 +89,7 @@ export const analyzerFields: INodeProperties[] = [
description: 'Enter the observable value',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -101,7 +101,7 @@ export const analyzerFields: INodeProperties[] = [
operation: ['execute'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
{
displayName: 'TLP',
diff --git a/packages/nodes-base/nodes/Cortex/ResponderDescription.ts b/packages/nodes-base/nodes/Cortex/ResponderDescription.ts
index 31fb47e35673f..64966847f3b08 100644
--- a/packages/nodes-base/nodes/Cortex/ResponderDescription.ts
+++ b/packages/nodes-base/nodes/Cortex/ResponderDescription.ts
@@ -233,7 +233,7 @@ export const responderFields: INodeProperties[] = [
name: 'artifactValues',
values: [
{
- displayName: 'Binary Property',
+ displayName: 'Binary Field',
name: 'binaryProperty',
type: 'string',
displayOptions: {
@@ -490,7 +490,7 @@ export const responderFields: INodeProperties[] = [
name: 'values',
values: [
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -499,7 +499,7 @@ export const responderFields: INodeProperties[] = [
dataType: ['file'],
},
},
- description: 'Name of the binary property which contains the attachement data',
+ hint: 'The name of the input binary field containing the attachement data',
},
{
displayName: 'Data',
diff --git a/packages/nodes-base/nodes/Crypto/Crypto.node.ts b/packages/nodes-base/nodes/Crypto/Crypto.node.ts
index 91b74169e6f6a..e95ff7ef65763 100644
--- a/packages/nodes-base/nodes/Crypto/Crypto.node.ts
+++ b/packages/nodes-base/nodes/Crypto/Crypto.node.ts
@@ -121,7 +121,7 @@ export class Crypto implements INodeType {
required: true,
},
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
diff --git a/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts b/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts
index af836169f3ce8..2b2e006a60abc 100644
--- a/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts
+++ b/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts
@@ -312,7 +312,7 @@ export class Dropbox implements INodeType {
description: 'The file path of the file to download. Has to contain the full path.',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -323,7 +323,7 @@ export class Dropbox implements INodeType {
resource: ['file'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
// ----------------------------------
@@ -346,7 +346,7 @@ export class Dropbox implements INodeType {
'The file path of the file to upload. Has to contain the full path. The parent folder has to exist. Existing files get overwritten.',
},
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
@@ -374,7 +374,7 @@ export class Dropbox implements INodeType {
description: 'The text content of the file to upload',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -387,8 +387,7 @@ export class Dropbox implements INodeType {
},
},
placeholder: '',
- description:
- 'Name of the binary property which contains the data for the file to be uploaded',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
// ----------------------------------
diff --git a/packages/nodes-base/nodes/EditImage/EditImage.node.json b/packages/nodes-base/nodes/EditImage/EditImage.node.json
index f7987f9765b64..56219a87a2991 100644
--- a/packages/nodes-base/nodes/EditImage/EditImage.node.json
+++ b/packages/nodes-base/nodes/EditImage/EditImage.node.json
@@ -11,7 +11,8 @@
}
]
},
+ "aliases": ["File", "Binary"],
"subcategories": {
- "Core Nodes": ["Files"]
+ "Core Nodes": ["Files", "Data Transformation"]
}
}
diff --git a/packages/nodes-base/nodes/Facebook/FacebookGraphApi.node.ts b/packages/nodes-base/nodes/Facebook/FacebookGraphApi.node.ts
index 09e232c365ec4..b457a2c6218e0 100644
--- a/packages/nodes-base/nodes/Facebook/FacebookGraphApi.node.ts
+++ b/packages/nodes-base/nodes/Facebook/FacebookGraphApi.node.ts
@@ -184,7 +184,7 @@ export class FacebookGraphApi implements INodeType {
description: 'Whether to connect even if SSL certificate validation is not possible',
},
{
- displayName: 'Send Binary Data',
+ displayName: 'Send Binary File',
name: 'sendBinaryData',
type: 'boolean',
displayOptions: {
@@ -197,7 +197,7 @@ export class FacebookGraphApi implements INodeType {
description: 'Whether binary data should be sent as body',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: '',
@@ -210,8 +210,9 @@ export class FacebookGraphApi implements INodeType {
httpRequestMethod: ['POST', 'PUT'],
},
},
+ hint: 'The name of the input binary field containing the file to be uploaded',
description:
- 'Name of the binary property which contains the data for the file to be uploaded. For Form-Data Multipart, they can be provided in the format: "sendKey1:binaryProperty1,sendKey2:binaryProperty2
',
+ 'For Form-Data Multipart, they can be provided in the format: "sendKey1:binaryProperty1,sendKey2:binaryProperty2
',
},
{
displayName: 'Options',
diff --git a/packages/nodes-base/nodes/Files/ConvertToFile/ConvertToFile.node.json b/packages/nodes-base/nodes/Files/ConvertToFile/ConvertToFile.node.json
new file mode 100644
index 0000000000000..bb99ab9459234
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ConvertToFile/ConvertToFile.node.json
@@ -0,0 +1,35 @@
+{
+ "node": "n8n-nodes-base.convertToFile",
+ "nodeVersion": "1.0",
+ "codexVersion": "1.0",
+ "categories": ["Core Nodes"],
+ "resources": {
+ "primaryDocumentation": [
+ {
+ "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.converttofile/"
+ }
+ ]
+ },
+ "alias": [
+ "CSV",
+ "Spreadsheet",
+ "Excel",
+ "xls",
+ "xlsx",
+ "ods",
+ "tabular",
+ "encode",
+ "encoding",
+ "Move Binary Data",
+ "Binary",
+ "File",
+ "JSON",
+ "HTML",
+ "ICS",
+ "RTF",
+ "64"
+ ],
+ "subcategories": {
+ "Core Nodes": ["Files", "Data Transformation"]
+ }
+}
diff --git a/packages/nodes-base/nodes/Files/ConvertToFile/ConvertToFile.node.ts b/packages/nodes-base/nodes/Files/ConvertToFile/ConvertToFile.node.ts
new file mode 100644
index 0000000000000..d17b431e15e34
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ConvertToFile/ConvertToFile.node.ts
@@ -0,0 +1,121 @@
+import type {
+ IExecuteFunctions,
+ INodeExecutionData,
+ INodeType,
+ INodeTypeDescription,
+} from 'n8n-workflow';
+
+import * as spreadsheet from './actions/spreadsheet.operation';
+import * as toBinary from './actions/toBinary.operation';
+import * as toJson from './actions/toJson.operation';
+import * as iCall from './actions/iCall.operation';
+
+export class ConvertToFile implements INodeType {
+ // eslint-disable-next-line n8n-nodes-base/node-class-description-missing-subtitle
+ description: INodeTypeDescription = {
+ displayName: 'Convert to File',
+ name: 'convertToFile',
+ icon: 'file:convertToFile.svg',
+ group: ['input'],
+ version: 1,
+ description: 'Convert JSON data to binary data',
+ defaults: {
+ name: 'Convert to File',
+ },
+ inputs: ['main'],
+ outputs: ['main'],
+ properties: [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ noDataExpression: true,
+ options: [
+ {
+ name: 'Convert to CSV',
+ value: 'csv',
+ action: 'Convert to CSV',
+ description: 'Transform input data into a CSV file',
+ },
+ {
+ name: 'Convert to HTML',
+ value: 'html',
+ action: 'Convert to HTML',
+ description: 'Transform input data into a table in an HTML file',
+ },
+ {
+ name: 'Convert to iCal',
+ value: 'iCal',
+ action: 'Convert to iCal',
+ description: 'Converts each input item to an ICS event file',
+ },
+ {
+ name: 'Convert to JSON',
+ value: 'toJson',
+ action: 'Convert to JSON',
+ description: 'Transform input data into a single or multiple JSON files',
+ },
+ {
+ name: 'Convert to ODS',
+ value: 'ods',
+ action: 'Convert to ODS',
+ description: 'Transform input data into an ODS file',
+ },
+ {
+ name: 'Convert to RTF',
+ value: 'rtf',
+ action: 'Convert to RTF',
+ description: 'Transform input data into a table in an RTF file',
+ },
+ {
+ name: 'Convert to XLS',
+ value: 'xls',
+ action: 'Convert to XLS',
+ description: 'Transform input data into an Excel file',
+ },
+ {
+ name: 'Convert to XLSX',
+ value: 'xlsx',
+ action: 'Convert to XLSX',
+ description: 'Transform input data into an Excel file',
+ },
+ {
+ name: 'Move Base64 String to File',
+ value: 'toBinary',
+ action: 'Move base64 string to file',
+ description: 'Convert a base64-encoded string into its original file format',
+ },
+ ],
+ default: 'csv',
+ },
+ ...spreadsheet.description,
+ ...toBinary.description,
+ ...toJson.description,
+ ...iCall.description,
+ ],
+ };
+
+ async execute(this: IExecuteFunctions) {
+ const items = this.getInputData();
+ const operation = this.getNodeParameter('operation', 0);
+ let returnData: INodeExecutionData[] = [];
+
+ if (spreadsheet.operations.includes(operation)) {
+ returnData = await spreadsheet.execute.call(this, items, operation);
+ }
+
+ if (operation === 'toJson') {
+ returnData = await toJson.execute.call(this, items);
+ }
+
+ if (operation === 'toBinary') {
+ returnData = await toBinary.execute.call(this, items);
+ }
+
+ if (operation === 'iCal') {
+ returnData = await iCall.execute.call(this, items);
+ }
+
+ return [returnData];
+ }
+}
diff --git a/packages/nodes-base/nodes/Files/ConvertToFile/actions/iCall.operation.ts b/packages/nodes-base/nodes/Files/ConvertToFile/actions/iCall.operation.ts
new file mode 100644
index 0000000000000..7fcca69b4cd0e
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ConvertToFile/actions/iCall.operation.ts
@@ -0,0 +1,20 @@
+import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
+
+import * as createEvent from '../../../ICalendar/createEvent.operation';
+
+import { updateDisplayOptions } from '@utils/utilities';
+
+export const description: INodeProperties[] = updateDisplayOptions(
+ {
+ show: {
+ operation: ['iCal'],
+ },
+ },
+ createEvent.description,
+);
+
+export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]) {
+ const returnData = await createEvent.execute.call(this, items);
+
+ return returnData;
+}
diff --git a/packages/nodes-base/nodes/Files/ConvertToFile/actions/spreadsheet.operation.ts b/packages/nodes-base/nodes/Files/ConvertToFile/actions/spreadsheet.operation.ts
new file mode 100644
index 0000000000000..eb0beca2ce95f
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ConvertToFile/actions/spreadsheet.operation.ts
@@ -0,0 +1,126 @@
+import {
+ NodeOperationError,
+ type IExecuteFunctions,
+ type INodeExecutionData,
+ type INodeProperties,
+} from 'n8n-workflow';
+
+import { generatePairedItemData, updateDisplayOptions } from '@utils/utilities';
+import type { JsonToSpreadsheetBinaryOptions, JsonToSpreadsheetBinaryFormat } from '@utils/binary';
+
+import { convertJsonToSpreadsheetBinary } from '@utils/binary';
+
+export const operations = ['csv', 'html', 'rtf', 'ods', 'xls', 'xlsx'];
+
+export const properties: INodeProperties[] = [
+ {
+ displayName: 'Put Output File in Field',
+ name: 'binaryPropertyName',
+ type: 'string',
+ default: 'data',
+ required: true,
+ placeholder: 'e.g data',
+ hint: 'The name of the output binary field to put the file in',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ options: [
+ {
+ displayName: 'Compression',
+ name: 'compression',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ '/operation': ['xlsx', 'ods'],
+ },
+ },
+ default: false,
+ description: 'Whether to reduce the output file size',
+ },
+ {
+ displayName: 'File Name',
+ name: 'fileName',
+ type: 'string',
+ default: '',
+ description: 'Name of the output file',
+ },
+ {
+ displayName: 'Header Row',
+ name: 'headerRow',
+ type: 'boolean',
+ default: true,
+ description: 'Whether the first row of the file contains the header names',
+ },
+ {
+ displayName: 'Sheet Name',
+ name: 'sheetName',
+ type: 'string',
+ displayOptions: {
+ show: {
+ '/operation': ['ods', 'xls', 'xlsx'],
+ },
+ },
+ default: 'Sheet',
+ description: 'Name of the sheet to create in the spreadsheet',
+ placeholder: 'e.g. mySheet',
+ },
+ ],
+ },
+];
+
+const displayOptions = {
+ show: {
+ operation: operations,
+ },
+};
+
+export const description = updateDisplayOptions(displayOptions, properties);
+
+export async function execute(
+ this: IExecuteFunctions,
+ items: INodeExecutionData[],
+ operation: string,
+) {
+ let returnData: INodeExecutionData[] = [];
+
+ const pairedItem = generatePairedItemData(items.length);
+ try {
+ const options = this.getNodeParameter('options', 0, {}) as JsonToSpreadsheetBinaryOptions;
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', 0, 'data');
+
+ const binaryData = await convertJsonToSpreadsheetBinary.call(
+ this,
+ items,
+ operation as JsonToSpreadsheetBinaryFormat,
+ options,
+ 'File',
+ );
+
+ const newItem: INodeExecutionData = {
+ json: {},
+ binary: {
+ [binaryPropertyName]: binaryData,
+ },
+ pairedItem,
+ };
+
+ returnData = [newItem];
+ } catch (error) {
+ if (this.continueOnFail()) {
+ returnData.push({
+ json: {
+ error: error.message,
+ },
+ pairedItem,
+ });
+ } else {
+ throw new NodeOperationError(this.getNode(), error);
+ }
+ }
+
+ return returnData;
+}
diff --git a/packages/nodes-base/nodes/Files/ConvertToFile/actions/toBinary.operation.ts b/packages/nodes-base/nodes/Files/ConvertToFile/actions/toBinary.operation.ts
new file mode 100644
index 0000000000000..770b55b72ba9f
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ConvertToFile/actions/toBinary.operation.ts
@@ -0,0 +1,147 @@
+import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
+
+import { NodeOperationError } from 'n8n-workflow';
+
+import type { JsonToBinaryOptions } from '@utils/binary';
+import { createBinaryFromJson } from '@utils/binary';
+import { encodeDecodeOptions } from '@utils/descriptions';
+import { updateDisplayOptions } from '@utils/utilities';
+
+export const properties: INodeProperties[] = [
+ {
+ displayName: 'Base64 Input Field',
+ name: 'sourceProperty',
+ type: 'string',
+ default: '',
+ required: true,
+ placeholder: 'e.g data',
+ requiresDataPath: 'single',
+ description:
+ "The name of the input field that contains the base64 string to convert to a file. Use dot-notation for deep fields (e.g. 'level1.level2.currentKey').",
+ },
+ {
+ displayName: 'Put Output File in Field',
+ name: 'binaryPropertyName',
+ type: 'string',
+ default: 'data',
+ required: true,
+ placeholder: 'e.g data',
+ hint: 'The name of the output binary field to put the file in',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ options: [
+ {
+ displayName: 'Add Byte Order Mark (BOM)',
+ description:
+ 'Whether to add special marker at the start of your text file. This marker helps some programs understand how to read the file correctly.',
+ name: 'addBOM',
+ displayOptions: {
+ show: {
+ encoding: ['utf8', 'cesu8', 'ucs2'],
+ },
+ },
+ type: 'boolean',
+ default: false,
+ },
+ {
+ displayName: 'Data Is Base64',
+ name: 'dataIsBase64',
+ type: 'boolean',
+ default: true,
+ description: 'Whether the data is already base64 encoded',
+ },
+ {
+ displayName: 'Encoding',
+ name: 'encoding',
+ type: 'options',
+ options: encodeDecodeOptions,
+ default: 'utf8',
+ description: 'Choose the character set to use to encode the data',
+ displayOptions: {
+ hide: {
+ dataIsBase64: [true],
+ },
+ },
+ },
+ {
+ displayName: 'File Name',
+ name: 'fileName',
+ type: 'string',
+ default: '',
+ placeholder: 'e.g. myFile',
+ description: 'Name of the output file',
+ },
+ {
+ displayName: 'MIME Type',
+ name: 'mimeType',
+ type: 'string',
+ default: '',
+ placeholder: 'e.g text/plain',
+ description:
+ 'The MIME type of the output file. Common MIME types.',
+ },
+ ],
+ },
+];
+
+const displayOptions = {
+ show: {
+ operation: ['toBinary'],
+ },
+};
+
+export const description = updateDisplayOptions(displayOptions, properties);
+
+export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]) {
+ const returnData: INodeExecutionData[] = [];
+
+ for (let i = 0; i < items.length; i++) {
+ try {
+ const options = this.getNodeParameter('options', i, {});
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i, 'data');
+ const sourceProperty = this.getNodeParameter('sourceProperty', i) as string;
+
+ const jsonToBinaryOptions: JsonToBinaryOptions = {
+ sourceKey: sourceProperty,
+ fileName: options.fileName as string,
+ mimeType: options.mimeType as string,
+ dataIsBase64: options.dataIsBase64 !== false,
+ encoding: options.encoding as string,
+ addBOM: options.addBOM as boolean,
+ itemIndex: i,
+ };
+
+ const binaryData = await createBinaryFromJson.call(this, items[i].json, jsonToBinaryOptions);
+
+ const newItem: INodeExecutionData = {
+ json: {},
+ binary: {
+ [binaryPropertyName]: binaryData,
+ },
+ pairedItem: { item: i },
+ };
+
+ returnData.push(newItem);
+ } catch (error) {
+ if (this.continueOnFail()) {
+ returnData.push({
+ json: {
+ error: error.message,
+ },
+ pairedItem: {
+ item: i,
+ },
+ });
+ continue;
+ }
+ throw new NodeOperationError(this.getNode(), error, { itemIndex: i });
+ }
+ }
+
+ return returnData;
+}
diff --git a/packages/nodes-base/nodes/Files/ConvertToFile/actions/toJson.operation.ts b/packages/nodes-base/nodes/Files/ConvertToFile/actions/toJson.operation.ts
new file mode 100644
index 0000000000000..eaccdfb5ff6f3
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ConvertToFile/actions/toJson.operation.ts
@@ -0,0 +1,165 @@
+import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
+import { NodeOperationError } from 'n8n-workflow';
+
+import { generatePairedItemData, updateDisplayOptions } from '@utils/utilities';
+import { createBinaryFromJson } from '@utils/binary';
+import { encodeDecodeOptions } from '@utils/descriptions';
+
+export const properties: INodeProperties[] = [
+ {
+ displayName: 'Mode',
+ name: 'mode',
+ type: 'options',
+ noDataExpression: true,
+ options: [
+ {
+ name: 'All Items to One File',
+ value: 'once',
+ },
+ {
+ name: 'Each Item to Separate File',
+ value: 'each',
+ },
+ ],
+ default: 'once',
+ },
+ {
+ displayName: 'Put Output File in Field',
+ name: 'binaryPropertyName',
+ type: 'string',
+ default: 'data',
+ required: true,
+ placeholder: 'e.g data',
+ hint: 'The name of the output binary field to put the file in',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ options: [
+ {
+ displayName: 'Add Byte Order Mark (BOM)',
+ name: 'addBOM',
+ type: 'boolean',
+ default: false,
+ description:
+ 'Whether to add special marker at the start of your text file. This marker helps some programs understand how to read the file correctly.',
+ displayOptions: {
+ show: {
+ encoding: ['utf8', 'cesu8', 'ucs2'],
+ },
+ },
+ },
+ {
+ displayName: 'Encoding',
+ name: 'encoding',
+ type: 'options',
+ options: encodeDecodeOptions,
+ default: 'utf8',
+ description: 'Choose the character set to use to encode the data',
+ },
+ {
+ displayName: 'File Name',
+ name: 'fileName',
+ type: 'string',
+ default: '',
+ placeholder: 'e.g. myFile.json',
+ description: 'Name of the output file',
+ },
+ ],
+ },
+];
+
+const displayOptions = {
+ show: {
+ operation: ['toJson'],
+ },
+};
+
+export const description = updateDisplayOptions(displayOptions, properties);
+
+export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]) {
+ let returnData: INodeExecutionData[] = [];
+
+ const mode = this.getNodeParameter('mode', 0, 'once') as string;
+ if (mode === 'once') {
+ const pairedItem = generatePairedItemData(items.length);
+ try {
+ const options = this.getNodeParameter('options', 0, {});
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', 0, 'data');
+
+ const binaryData = await createBinaryFromJson.call(
+ this,
+ items.map((item) => item.json),
+ {
+ fileName: options.fileName as string,
+ mimeType: 'application/json',
+ encoding: options.encoding as string,
+ addBOM: options.addBOM as boolean,
+ },
+ );
+
+ const newItem: INodeExecutionData = {
+ json: {},
+ binary: {
+ [binaryPropertyName]: binaryData,
+ },
+ pairedItem,
+ };
+
+ returnData = [newItem];
+ } catch (error) {
+ if (this.continueOnFail()) {
+ returnData.push({
+ json: {
+ error: error.message,
+ },
+ pairedItem,
+ });
+ }
+ throw new NodeOperationError(this.getNode(), error);
+ }
+ } else {
+ for (let i = 0; i < items.length; i++) {
+ try {
+ const options = this.getNodeParameter('options', i, {});
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i, 'data');
+
+ const binaryData = await createBinaryFromJson.call(this, items[i].json, {
+ fileName: options.fileName as string,
+ encoding: options.encoding as string,
+ addBOM: options.addBOM as boolean,
+ mimeType: 'application/json',
+ itemIndex: i,
+ });
+
+ const newItem: INodeExecutionData = {
+ json: {},
+ binary: {
+ [binaryPropertyName]: binaryData,
+ },
+ pairedItem: { item: i },
+ };
+
+ returnData.push(newItem);
+ } catch (error) {
+ if (this.continueOnFail()) {
+ returnData.push({
+ json: {
+ error: error.message,
+ },
+ pairedItem: {
+ item: i,
+ },
+ });
+ continue;
+ }
+ throw new NodeOperationError(this.getNode(), error, { itemIndex: i });
+ }
+ }
+ }
+
+ return returnData;
+}
diff --git a/packages/nodes-base/nodes/Files/ConvertToFile/convertToFile.svg b/packages/nodes-base/nodes/Files/ConvertToFile/convertToFile.svg
new file mode 100644
index 0000000000000..60ca4a415a137
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ConvertToFile/convertToFile.svg
@@ -0,0 +1,12 @@
+
diff --git a/packages/nodes-base/nodes/Files/ExtractFromFile/ExtractFromFile.node.json b/packages/nodes-base/nodes/Files/ExtractFromFile/ExtractFromFile.node.json
new file mode 100644
index 0000000000000..989173a6a5e36
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ExtractFromFile/ExtractFromFile.node.json
@@ -0,0 +1,39 @@
+{
+ "node": "n8n-nodes-base.extractFromFile",
+ "nodeVersion": "1.0",
+ "codexVersion": "1.0",
+ "categories": ["Core Nodes"],
+ "resources": {
+ "primaryDocumentation": [
+ {
+ "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.extractfromfile/"
+ }
+ ]
+ },
+ "alias": [
+ "CSV",
+ "Spreadsheet",
+ "Excel",
+ "xls",
+ "xlsx",
+ "ods",
+ "tabular",
+ "decode",
+ "decoding",
+ "Move Binary Data",
+ "Binary",
+ "File",
+ "PDF",
+ "JSON",
+ "HTML",
+ "ICS",
+ "txt",
+ "Text",
+ "RTF",
+ "XML",
+ "64"
+ ],
+ "subcategories": {
+ "Core Nodes": ["Files", "Data Transformation"]
+ }
+}
diff --git a/packages/nodes-base/nodes/Files/ExtractFromFile/ExtractFromFile.node.ts b/packages/nodes-base/nodes/Files/ExtractFromFile/ExtractFromFile.node.ts
new file mode 100644
index 0000000000000..3908fc842e1a1
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ExtractFromFile/ExtractFromFile.node.ts
@@ -0,0 +1,134 @@
+import type {
+ IExecuteFunctions,
+ INodeExecutionData,
+ INodeType,
+ INodeTypeDescription,
+} from 'n8n-workflow';
+
+import * as spreadsheet from './actions/spreadsheet.operation';
+import * as moveTo from './actions/moveTo.operation';
+import * as pdf from './actions/pdf.operation';
+
+export class ExtractFromFile implements INodeType {
+ // eslint-disable-next-line n8n-nodes-base/node-class-description-missing-subtitle
+ description: INodeTypeDescription = {
+ displayName: 'Extract From File',
+ name: 'extractFromFile',
+ icon: 'file:extractFromFile.svg',
+ group: ['input'],
+ version: 1,
+ description: 'Convert binary data to JSON',
+ defaults: {
+ name: 'Extract From File',
+ },
+ inputs: ['main'],
+ outputs: ['main'],
+ properties: [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ noDataExpression: true,
+ // eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
+ options: [
+ {
+ name: 'Extract From CSV',
+ value: 'csv',
+ action: 'Extract from CSV',
+ description: 'Transform a CSV file into output items',
+ },
+ {
+ name: 'Extract From HTML',
+ value: 'html',
+ action: 'Extract from HTML',
+ description: 'Transform a table in an HTML file into output items',
+ },
+ {
+ name: 'Extract From JSON',
+ value: 'fromJson',
+ action: 'Extract from JSON',
+ description: 'Transform a JSON file into output items',
+ },
+ {
+ name: 'Extract From ICS',
+ value: 'fromIcs',
+ action: 'Extract from ICS',
+ description: 'Transform a ICS file into output items',
+ },
+ {
+ name: 'Extract From ODS',
+ value: 'ods',
+ action: 'Extract from ODS',
+ description: 'Transform an ODS file into output items',
+ },
+ {
+ name: 'Extract From PDF',
+ value: 'pdf',
+ action: 'Extract from PDF',
+ description: 'Extracts the content and metadata from a PDF file',
+ },
+ {
+ name: 'Extract From RTF',
+ value: 'rtf',
+ action: 'Extract from RTF',
+ description: 'Transform a table in an RTF file into output items',
+ },
+ {
+ name: 'Extract From Text File',
+ value: 'text',
+ action: 'Extract from text file',
+ description: 'Extracts the content of a text file',
+ },
+ {
+ name: 'Extract From XML',
+ value: 'xml',
+ action: 'Extract from XLS',
+ description: 'Extracts the content of an XML file',
+ },
+ {
+ name: 'Extract From XLS',
+ value: 'xls',
+ action: 'Extract from XLS',
+ description: 'Transform an Excel file into output items',
+ },
+ {
+ name: 'Extract From XLSX',
+ value: 'xlsx',
+ action: 'Extract from XLSX',
+ description: 'Transform an Excel file into output items',
+ },
+ {
+ name: 'Move File to Base64 String',
+ value: 'binaryToPropery',
+ action: 'Move file to base64 string',
+ description: 'Convert a file into a base64-encoded string',
+ },
+ ],
+ default: 'csv',
+ },
+ ...spreadsheet.description,
+ ...moveTo.description,
+ ...pdf.description,
+ ],
+ };
+
+ async execute(this: IExecuteFunctions) {
+ const items = this.getInputData();
+ const operation = this.getNodeParameter('operation', 0);
+ let returnData: INodeExecutionData[] = [];
+
+ if (spreadsheet.operations.includes(operation)) {
+ returnData = await spreadsheet.execute.call(this, items, 'operation');
+ }
+
+ if (['binaryToPropery', 'fromJson', 'text', 'fromIcs', 'xml'].includes(operation)) {
+ returnData = await moveTo.execute.call(this, items, operation);
+ }
+
+ if (operation === 'pdf') {
+ returnData = await pdf.execute.call(this, items);
+ }
+
+ return [returnData];
+ }
+}
diff --git a/packages/nodes-base/nodes/Files/ExtractFromFile/actions/moveTo.operation.ts b/packages/nodes-base/nodes/Files/ExtractFromFile/actions/moveTo.operation.ts
new file mode 100644
index 0000000000000..523c2090cc903
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ExtractFromFile/actions/moveTo.operation.ts
@@ -0,0 +1,192 @@
+import type {
+ IDataObject,
+ IExecuteFunctions,
+ INodeExecutionData,
+ INodeProperties,
+} from 'n8n-workflow';
+
+import { BINARY_ENCODING, NodeOperationError, deepCopy, jsonParse } from 'n8n-workflow';
+
+import { encodeDecodeOptions } from '@utils/descriptions';
+import { updateDisplayOptions } from '@utils/utilities';
+
+import get from 'lodash/get';
+import set from 'lodash/set';
+import unset from 'lodash/unset';
+
+import iconv from 'iconv-lite';
+
+import { icsCalendarToObject } from 'ts-ics';
+
+export const properties: INodeProperties[] = [
+ {
+ displayName: 'Input Binary Field',
+ name: 'binaryPropertyName',
+ type: 'string',
+ default: 'data',
+ required: true,
+ placeholder: 'e.g data',
+ hint: 'The name of the input field containing the file data to be processed',
+ },
+ {
+ displayName: 'Destination Output Field',
+ name: 'destinationKey',
+ type: 'string',
+ default: 'data',
+ required: true,
+ placeholder: 'e.g data',
+ description: 'The name of the output field that will contain the extracted data',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ options: [
+ {
+ displayName: 'File Encoding',
+ name: 'encoding',
+ type: 'options',
+ options: encodeDecodeOptions,
+ default: 'utf8',
+ description: 'Specify the encoding of the file, defaults to UTF-8',
+ },
+ {
+ displayName: 'Strip BOM',
+ name: 'stripBOM',
+ displayOptions: {
+ show: {
+ encoding: ['utf8', 'cesu8', 'ucs2'],
+ },
+ },
+ type: 'boolean',
+ default: true,
+ description:
+ 'Whether to strip the BOM (Byte Order Mark) from the file, this could help in an environment where the presence of the BOM is causing issues or inconsistencies',
+ },
+ {
+ displayName: 'Keep Source',
+ name: 'keepSource',
+ type: 'options',
+ default: 'json',
+ options: [
+ {
+ name: 'JSON',
+ value: 'json',
+ description: 'Include JSON data of the input item',
+ },
+ {
+ name: 'Binary',
+ value: 'binary',
+ description: 'Include binary data of the input item',
+ },
+ {
+ name: 'Both',
+ value: 'both',
+ description: 'Include both JSON and binary data of the input item',
+ },
+ ],
+ },
+ ],
+ },
+];
+
+const displayOptions = {
+ show: {
+ operation: ['binaryToPropery', 'fromJson', 'text', 'fromIcs', 'xml'],
+ },
+};
+
+export const description = updateDisplayOptions(displayOptions, properties);
+
+export async function execute(
+ this: IExecuteFunctions,
+ items: INodeExecutionData[],
+ operation: string,
+) {
+ const returnData: INodeExecutionData[] = [];
+
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
+ try {
+ const item = items[itemIndex];
+ const options = this.getNodeParameter('options', itemIndex);
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', itemIndex);
+
+ const newItem: INodeExecutionData = {
+ json: {},
+ pairedItem: { item: itemIndex },
+ };
+
+ const value = get(item.binary, binaryPropertyName);
+
+ if (!value) continue;
+
+ const encoding = (options.encoding as string) || 'utf8';
+ const buffer = await this.helpers.getBinaryDataBuffer(itemIndex, binaryPropertyName);
+
+ if (options.keepSource && options.keepSource !== 'binary') {
+ newItem.json = deepCopy(item.json);
+ }
+
+ let convertedValue: string | IDataObject;
+ if (operation !== 'binaryToPropery') {
+ convertedValue = iconv.decode(buffer, encoding, {
+ stripBOM: options.stripBOM as boolean,
+ });
+ } else {
+ convertedValue = Buffer.from(buffer).toString(BINARY_ENCODING);
+ }
+
+ if (operation === 'fromJson') {
+ if (convertedValue === '') {
+ convertedValue = {};
+ } else {
+ convertedValue = jsonParse(convertedValue);
+ }
+ }
+
+ if (operation === 'fromIcs') {
+ convertedValue = icsCalendarToObject(convertedValue as string);
+ }
+
+ const destinationKey = this.getNodeParameter('destinationKey', itemIndex, '') as string;
+ set(newItem.json, destinationKey, convertedValue);
+
+ if (options.keepSource === 'binary' || options.keepSource === 'both') {
+ newItem.binary = item.binary;
+ } else {
+ // this binary data would not be included, but there also might be other binary data
+ // which should be included, copy it over and unset current binary data
+ newItem.binary = deepCopy(item.binary);
+ unset(newItem.binary, binaryPropertyName);
+ }
+
+ returnData.push(newItem);
+ } catch (error) {
+ let errorDescription;
+ if (error.message.includes('Unexpected token')) {
+ error.message = "The file selected in 'Input Binary Field' is not in JSON format";
+ errorDescription =
+ "Try to change the operation or select a JSON file in 'Input Binary Field'";
+ }
+ if (this.continueOnFail()) {
+ returnData.push({
+ json: {
+ error: error.message,
+ },
+ pairedItem: {
+ item: itemIndex,
+ },
+ });
+ continue;
+ }
+ throw new NodeOperationError(this.getNode(), error, {
+ itemIndex,
+ description: errorDescription,
+ });
+ }
+ }
+
+ return returnData;
+}
diff --git a/packages/nodes-base/nodes/Files/ExtractFromFile/actions/pdf.operation.ts b/packages/nodes-base/nodes/Files/ExtractFromFile/actions/pdf.operation.ts
new file mode 100644
index 0000000000000..59473851459f2
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ExtractFromFile/actions/pdf.operation.ts
@@ -0,0 +1,141 @@
+import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
+
+import { NodeOperationError, deepCopy } from 'n8n-workflow';
+
+import unset from 'lodash/unset';
+
+import { extractDataFromPDF } from '@utils/binary';
+import { updateDisplayOptions } from '@utils/utilities';
+
+export const properties: INodeProperties[] = [
+ {
+ displayName: 'Input Binary Field',
+ name: 'binaryPropertyName',
+ type: 'string',
+ default: 'data',
+ required: true,
+ placeholder: 'e.g data',
+ hint: 'The name of the input binary field containing the file to be extracted',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ options: [
+ {
+ displayName: 'Join Pages',
+ name: 'joinPages',
+ type: 'boolean',
+ default: true,
+ description:
+ 'Whether to join the text from all pages or return an array of text from each page',
+ },
+ {
+ displayName: 'Keep Source',
+ name: 'keepSource',
+ type: 'options',
+ default: 'json',
+ options: [
+ {
+ name: 'JSON',
+ value: 'json',
+ description: 'Include JSON data of the input item',
+ },
+ {
+ name: 'Binary',
+ value: 'binary',
+ description: 'Include binary data of the input item',
+ },
+ {
+ name: 'Both',
+ value: 'both',
+ description: 'Include both JSON and binary data of the input item',
+ },
+ ],
+ },
+ {
+ displayName: 'Max Pages',
+ name: 'maxPages',
+ type: 'number',
+ default: 0,
+ description: 'Maximum number of pages to include',
+ },
+ {
+ displayName: 'Password',
+ name: 'password',
+ type: 'string',
+ typeOptions: { password: true },
+ default: '',
+ description: 'Prowide password, if the PDF is encrypted',
+ },
+ ],
+ },
+];
+
+const displayOptions = {
+ show: {
+ operation: ['pdf'],
+ },
+};
+
+export const description = updateDisplayOptions(displayOptions, properties);
+
+export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]) {
+ const returnData: INodeExecutionData[] = [];
+
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
+ try {
+ const item = items[itemIndex];
+ const options = this.getNodeParameter('options', itemIndex);
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', itemIndex);
+
+ const json = await extractDataFromPDF.call(
+ this,
+ binaryPropertyName,
+ options.password as string,
+ options.maxPages as number,
+ options.joinPages as boolean,
+ itemIndex,
+ );
+
+ const newItem: INodeExecutionData = {
+ json: {},
+ pairedItem: { item: itemIndex },
+ };
+
+ if (options.keepSource && options.keepSource !== 'binary') {
+ newItem.json = { ...deepCopy(item.json), ...json };
+ } else {
+ newItem.json = json;
+ }
+
+ if (options.keepSource === 'binary' || options.keepSource === 'both') {
+ newItem.binary = item.binary;
+ } else {
+ // this binary data would not be included, but there also might be other binary data
+ // which should be included, copy it over and unset current binary data
+ newItem.binary = deepCopy(item.binary);
+ unset(newItem.binary, binaryPropertyName);
+ }
+
+ returnData.push(newItem);
+ } catch (error) {
+ if (this.continueOnFail()) {
+ returnData.push({
+ json: {
+ error: error.message,
+ },
+ pairedItem: {
+ item: itemIndex,
+ },
+ });
+ continue;
+ }
+ throw new NodeOperationError(this.getNode(), error, { itemIndex });
+ }
+ }
+
+ return returnData;
+}
diff --git a/packages/nodes-base/nodes/Files/ExtractFromFile/actions/spreadsheet.operation.ts b/packages/nodes-base/nodes/Files/ExtractFromFile/actions/spreadsheet.operation.ts
new file mode 100644
index 0000000000000..61530374386af
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ExtractFromFile/actions/spreadsheet.operation.ts
@@ -0,0 +1,59 @@
+import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
+
+import * as fromFile from '../../../SpreadsheetFile/v2/fromFile.operation';
+
+export const operations = ['csv', 'html', 'rtf', 'ods', 'xls', 'xlsx'];
+
+export const description: INodeProperties[] = fromFile.description
+ .filter((property) => property.name !== 'fileFormat')
+ .map((property) => {
+ const newProperty = { ...property };
+ newProperty.displayOptions = {
+ show: {
+ operation: operations,
+ },
+ };
+
+ if (newProperty.name === 'options') {
+ newProperty.options = (newProperty.options as INodeProperties[]).map((option) => {
+ let newOption = option;
+ if (['delimiter', 'fromLine', 'maxRowCount', 'enableBOM'].includes(option.name)) {
+ newOption = { ...option, displayOptions: { show: { '/operation': ['csv'] } } };
+ }
+ if (option.name === 'sheetName') {
+ newOption = {
+ ...option,
+ displayOptions: { show: { '/operation': ['ods', 'xls', 'xlsx'] } },
+ description: 'Name of the sheet to read from in the spreadsheet',
+ };
+ }
+ if (option.name === 'range') {
+ newOption = {
+ ...option,
+ displayOptions: { show: { '/operation': ['ods', 'xls', 'xlsx'] } },
+ };
+ }
+ if (['includeEmptyCells', 'headerRow'].includes(option.name)) {
+ newOption = {
+ ...option,
+ displayOptions: { show: { '/operation': ['ods', 'xls', 'xlsx', 'csv', 'html'] } },
+ };
+ }
+ return newOption;
+ });
+ }
+ return newProperty;
+ });
+
+export async function execute(
+ this: IExecuteFunctions,
+ items: INodeExecutionData[],
+ fileFormatProperty: string,
+) {
+ const returnData: INodeExecutionData[] = await fromFile.execute.call(
+ this,
+ items,
+ fileFormatProperty,
+ );
+ return returnData;
+}
diff --git a/packages/nodes-base/nodes/Files/ExtractFromFile/extractFromFile.svg b/packages/nodes-base/nodes/Files/ExtractFromFile/extractFromFile.svg
new file mode 100644
index 0000000000000..2c235b7a2d9d8
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ExtractFromFile/extractFromFile.svg
@@ -0,0 +1,5 @@
+
diff --git a/packages/nodes-base/nodes/Files/ReadWriteFile/ReadWriteFile.node.json b/packages/nodes-base/nodes/Files/ReadWriteFile/ReadWriteFile.node.json
new file mode 100644
index 0000000000000..57bdf62dd7d35
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ReadWriteFile/ReadWriteFile.node.json
@@ -0,0 +1,17 @@
+{
+ "node": "n8n-nodes-base.readWriteFile",
+ "nodeVersion": "1.0",
+ "codexVersion": "1.0",
+ "categories": ["Core Nodes"],
+ "resources": {
+ "primaryDocumentation": [
+ {
+ "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.filesreadwrite/"
+ }
+ ]
+ },
+ "alias": ["Binary", "File", "Text", "Open", "Import", "Save", "Export", "Disk", "Transfer"],
+ "subcategories": {
+ "Core Nodes": ["Files"]
+ }
+}
diff --git a/packages/nodes-base/nodes/Files/ReadWriteFile/ReadWriteFile.node.ts b/packages/nodes-base/nodes/Files/ReadWriteFile/ReadWriteFile.node.ts
new file mode 100644
index 0000000000000..9c4b95d17031e
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ReadWriteFile/ReadWriteFile.node.ts
@@ -0,0 +1,73 @@
+import type {
+ IExecuteFunctions,
+ INodeExecutionData,
+ INodeType,
+ INodeTypeDescription,
+} from 'n8n-workflow';
+
+import * as read from './actions/read.operation';
+import * as write from './actions/write.operation';
+
+export class ReadWriteFile implements INodeType {
+ description: INodeTypeDescription = {
+ displayName: 'Read/Write Files from Disk',
+ name: 'readWriteFile',
+ icon: 'file:readWriteFile.svg',
+ group: ['input'],
+ version: 1,
+ description: 'Read or write files from the computer that runs n8n',
+ defaults: {
+ name: 'Read/Write Files from Disk',
+ },
+ inputs: ['main'],
+ outputs: ['main'],
+ properties: [
+ {
+ displayName:
+ 'Use this node to read and write files on the same computer running n8n. To handle files between different computers please use other nodes (e.g. FTP, HTTP Request, AWS).',
+ name: 'info',
+ type: 'notice',
+ default: '',
+ },
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ noDataExpression: true,
+ options: [
+ {
+ name: 'Read File(s) From Disk',
+ value: 'read',
+ description: 'Retrieve one or more files from the computer that runs n8n',
+ action: 'Read File(s) From Disk',
+ },
+ {
+ name: 'Write File to Disk',
+ value: 'write',
+ description: 'Create a binary file on the computer that runs n8n',
+ action: 'Write File to Disk',
+ },
+ ],
+ default: 'read',
+ },
+ ...read.description,
+ ...write.description,
+ ],
+ };
+
+ async execute(this: IExecuteFunctions) {
+ const operation = this.getNodeParameter('operation', 0, 'read');
+ const items = this.getInputData();
+ let returnData: INodeExecutionData[] = [];
+
+ if (operation === 'read') {
+ returnData = await read.execute.call(this, items);
+ }
+
+ if (operation === 'write') {
+ returnData = await write.execute.call(this, items);
+ }
+
+ return [returnData];
+ }
+}
diff --git a/packages/nodes-base/nodes/Files/ReadWriteFile/actions/read.operation.ts b/packages/nodes-base/nodes/Files/ReadWriteFile/actions/read.operation.ts
new file mode 100644
index 0000000000000..48e532c8a73c6
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ReadWriteFile/actions/read.operation.ts
@@ -0,0 +1,144 @@
+import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
+
+import glob from 'fast-glob';
+import { updateDisplayOptions } from '@utils/utilities';
+import { errorMapper } from '../helpers/utils';
+
+export const properties: INodeProperties[] = [
+ {
+ displayName: 'File(s) Selector',
+ name: 'fileSelector',
+ type: 'string',
+ default: '',
+ required: true,
+ placeholder: 'e.g. /home/user/Pictures/**/*.png',
+ hint: 'Supports patterns, learn more here',
+ description: "Specify a file's path or path pattern to read multiple files",
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ options: [
+ {
+ displayName: 'File Extension',
+ name: 'fileExtension',
+ type: 'string',
+ default: '',
+ placeholder: 'e.g. zip',
+ description: 'Extension of the file in the output binary',
+ },
+ {
+ displayName: 'File Name',
+ name: 'fileName',
+ type: 'string',
+ default: '',
+ placeholder: 'e.g. data.zip',
+ description: 'Name of the file in the output binary',
+ },
+ {
+ displayName: 'Mime Type',
+ name: 'mimeType',
+ type: 'string',
+ default: '',
+ placeholder: 'e.g. application/zip',
+ description: 'Mime type of the file in the output binary',
+ },
+ {
+ displayName: 'Put Output File in Field',
+ name: 'dataPropertyName',
+ type: 'string',
+ default: 'data',
+ placeholder: 'e.g. data',
+ description: "By default 'data' is used",
+ hint: 'The name of the output binary field to put the file in',
+ },
+ ],
+ },
+];
+
+const displayOptions = {
+ show: {
+ operation: ['read'],
+ },
+};
+
+export const description = updateDisplayOptions(displayOptions, properties);
+
+export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]) {
+ const returnData: INodeExecutionData[] = [];
+ let fileSelector;
+
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
+ try {
+ fileSelector = this.getNodeParameter('fileSelector', itemIndex) as string;
+ const options = this.getNodeParameter('options', itemIndex, {});
+
+ let dataPropertyName = 'data';
+
+ if (options.dataPropertyName) {
+ dataPropertyName = options.dataPropertyName as string;
+ }
+
+ const files = await glob(fileSelector);
+
+ const newItems: INodeExecutionData[] = [];
+ for (const filePath of files) {
+ const stream = await this.helpers.createReadStream(filePath);
+ const binaryData = await this.helpers.prepareBinaryData(stream, filePath);
+
+ if (options.fileName !== undefined) {
+ binaryData.fileName = options.fileName as string;
+ }
+
+ if (options.fileExtension !== undefined) {
+ binaryData.fileExtension = options.fileExtension as string;
+ }
+
+ if (options.mimeType !== undefined) {
+ binaryData.mimeType = options.mimeType as string;
+ }
+
+ newItems.push({
+ binary: {
+ [dataPropertyName]: binaryData,
+ },
+ json: {
+ mimeType: binaryData.mimeType,
+ fileType: binaryData.fileType,
+ fileName: binaryData.fileName,
+ directory: binaryData.directory,
+ fileExtension: binaryData.fileExtension,
+ fileSize: binaryData.fileSize,
+ },
+ pairedItem: {
+ item: itemIndex,
+ },
+ });
+ }
+
+ returnData.push(...newItems);
+ } catch (error) {
+ const nodeOperatioinError = errorMapper.call(this, error, itemIndex, {
+ filePath: fileSelector,
+ operation: 'read',
+ });
+ if (this.continueOnFail()) {
+ returnData.push({
+ json: {
+ error: nodeOperatioinError.message,
+ },
+ pairedItem: {
+ item: itemIndex,
+ },
+ });
+ continue;
+ }
+ throw nodeOperatioinError;
+ }
+ }
+
+ return returnData;
+}
diff --git a/packages/nodes-base/nodes/Files/ReadWriteFile/actions/write.operation.ts b/packages/nodes-base/nodes/Files/ReadWriteFile/actions/write.operation.ts
new file mode 100644
index 0000000000000..310f753320f40
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ReadWriteFile/actions/write.operation.ts
@@ -0,0 +1,123 @@
+import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
+import { BINARY_ENCODING } from 'n8n-workflow';
+
+import type { Readable } from 'stream';
+
+import { updateDisplayOptions } from '@utils/utilities';
+import { errorMapper } from '../helpers/utils';
+
+export const properties: INodeProperties[] = [
+ {
+ displayName: 'File Path and Name',
+ name: 'fileName',
+ type: 'string',
+ default: '',
+ required: true,
+ placeholder: 'e.g. /data/example.jpg',
+ description:
+ 'Path and name of the file that should be written. Also include the file extension.',
+ },
+ {
+ displayName: 'Input Binary Field',
+ name: 'dataPropertyName',
+ type: 'string',
+ default: 'data',
+ placeholder: 'e.g. data',
+ required: true,
+ hint: 'The name of the input binary field containing the file to be written',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ options: [
+ {
+ displayName: 'Append',
+ name: 'append',
+ type: 'boolean',
+ default: false,
+ description:
+ "Whether to append to an existing file. While it's commonly used with text files, it's not limited to them, however, it wouldn't be applicable for file types that have a specific structure like most binary formats.",
+ },
+ ],
+ },
+];
+
+const displayOptions = {
+ show: {
+ operation: ['write'],
+ },
+};
+
+export const description = updateDisplayOptions(displayOptions, properties);
+
+export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]) {
+ const returnData: INodeExecutionData[] = [];
+ let fileName;
+
+ let item: INodeExecutionData;
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
+ try {
+ const dataPropertyName = this.getNodeParameter('dataPropertyName', itemIndex);
+ fileName = this.getNodeParameter('fileName', itemIndex) as string;
+ const options = this.getNodeParameter('options', itemIndex, {});
+ const flag: string = options.append ? 'a' : 'w';
+
+ item = items[itemIndex];
+
+ const newItem: INodeExecutionData = {
+ json: {},
+ pairedItem: {
+ item: itemIndex,
+ },
+ };
+ Object.assign(newItem.json, item.json);
+
+ const binaryData = this.helpers.assertBinaryData(itemIndex, dataPropertyName);
+
+ let fileContent: Buffer | Readable;
+ if (binaryData.id) {
+ fileContent = await this.helpers.getBinaryStream(binaryData.id);
+ } else {
+ fileContent = Buffer.from(binaryData.data, BINARY_ENCODING);
+ }
+
+ // Write the file to disk
+ await this.helpers.writeContentToFile(fileName, fileContent, flag);
+
+ if (item.binary !== undefined) {
+ // Create a shallow copy of the binary data so that the old
+ // data references which do not get changed still stay behind
+ // but the incoming data does not get changed.
+ newItem.binary = {};
+ Object.assign(newItem.binary, item.binary);
+ }
+
+ // Add the file name to data
+ newItem.json.fileName = fileName;
+
+ returnData.push(newItem);
+ } catch (error) {
+ const nodeOperatioinError = errorMapper.call(this, error, itemIndex, {
+ filePath: fileName,
+ operation: 'write',
+ });
+ if (this.continueOnFail()) {
+ returnData.push({
+ json: {
+ error: nodeOperatioinError.message,
+ },
+ pairedItem: {
+ item: itemIndex,
+ },
+ });
+ continue;
+ }
+ throw nodeOperatioinError;
+ }
+ }
+
+ return returnData;
+}
diff --git a/packages/nodes-base/nodes/Files/ReadWriteFile/helpers/utils.ts b/packages/nodes-base/nodes/Files/ReadWriteFile/helpers/utils.ts
new file mode 100644
index 0000000000000..8ecbb66706f29
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ReadWriteFile/helpers/utils.ts
@@ -0,0 +1,32 @@
+import type { IDataObject, IExecuteFunctions } from 'n8n-workflow';
+import { NodeOperationError } from 'n8n-workflow';
+
+export function errorMapper(
+ this: IExecuteFunctions,
+ error: Error,
+ itemIndex: number,
+ context?: IDataObject,
+) {
+ let message;
+ let description;
+
+ if (error.message.includes('Cannot create a string longer than')) {
+ message = 'The file is too large';
+ description =
+ 'The binary file you are attempting to read exceeds 512MB, which is limit when using default binary data mode, try using the filesystem binary mode. More information here.';
+ } else if (error.message.includes('EACCES') && context?.operation === 'read') {
+ const path =
+ ((error as unknown as IDataObject).path as string) || (context?.filePath as string);
+ message = `You don't have the permissions to access ${path}`;
+ description =
+ "Verify that the path specified in 'File(s) Selector' is correct, or change the file(s) permissions if needed";
+ } else if (error.message.includes('EACCES') && context?.operation === 'write') {
+ const path =
+ ((error as unknown as IDataObject).path as string) || (context?.filePath as string);
+ message = `You don't have the permissions to write the file ${path}`;
+ description =
+ "Specify another destination folder in 'File Path and Name', or change the permissions of the parent folder";
+ }
+
+ return new NodeOperationError(this.getNode(), error, { itemIndex, message, description });
+}
diff --git a/packages/nodes-base/nodes/Files/ReadWriteFile/readWriteFile.svg b/packages/nodes-base/nodes/Files/ReadWriteFile/readWriteFile.svg
new file mode 100644
index 0000000000000..288224b9563e9
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ReadWriteFile/readWriteFile.svg
@@ -0,0 +1,13 @@
+
diff --git a/packages/nodes-base/nodes/Files/ReadWriteFile/test/ReadWriteFile.test.ts b/packages/nodes-base/nodes/Files/ReadWriteFile/test/ReadWriteFile.test.ts
new file mode 100644
index 0000000000000..38c8d0057e9c2
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ReadWriteFile/test/ReadWriteFile.test.ts
@@ -0,0 +1,101 @@
+/* eslint-disable @typescript-eslint/no-loop-func */
+import * as Helpers from '@test/nodes/Helpers';
+import type { WorkflowTestData } from '@test/nodes/types';
+import { executeWorkflow } from '@test/nodes/ExecuteWorkflow';
+
+describe('Test ReadWriteFile Node', () => {
+ beforeEach(async () => {
+ await Helpers.initBinaryDataService();
+ });
+
+ const temporaryDir = Helpers.createTemporaryDir();
+ const directory = __dirname.replace(/\\/gi, '/');
+
+ const workflow = Helpers.readJsonFileSync(
+ 'nodes/Files/ReadWriteFile/test/ReadWriteFile.workflow.json',
+ );
+
+ const readFileNode = workflow.nodes.find((n: any) => n.name === 'Read from Disk');
+ readFileNode.parameters.fileSelector = `${directory}/image.jpg`;
+
+ const writeFileNode = workflow.nodes.find((n: any) => n.name === 'Write to Disk');
+ writeFileNode.parameters.fileName = `${temporaryDir}/image-written.jpg`;
+
+ const tests: WorkflowTestData[] = [
+ {
+ description: 'nodes/Files/ReadWriteFile/test/ReadWriteFile.workflow.json',
+ input: {
+ workflowData: workflow,
+ },
+ output: {
+ nodeData: {
+ 'Read from Disk': [
+ [
+ {
+ json: {
+ directory,
+ fileExtension: 'jpg',
+ fileName: 'image.jpg',
+ fileSize: '1.04 kB',
+ fileType: 'image',
+ mimeType: 'image/jpeg',
+ },
+ binary: {
+ data: {
+ mimeType: 'image/jpeg',
+ fileType: 'image',
+ fileExtension: 'jpg',
+ data: '/9j/4AAQSkZJRgABAQEASABIAAD/4QBmRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAAExAAIAAAAQAAAATgAAAAAAARlJAAAD6AABGUkAAAPocGFpbnQubmV0IDUuMC4xAP/bAEMAIBYYHBgUIBwaHCQiICYwUDQwLCwwYkZKOlB0Znp4cmZwboCQuJyAiK6KbnCg2qKuvsTO0M58muLy4MjwuMrOxv/bAEMBIiQkMCowXjQ0XsaEcITGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxv/AABEIAB8AOwMBEgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AOgqgrXF2zNHJ5aKcD3oNPZ23di/VKG82bkuTh1OMgdaAdOSLtZ6G5ut0iSeWoOAKAdO27NCqUN8oQrcHDqccDrQDpyRNPdRwEKcsx7CobIebPLORwThc0inGMF724jagNpxG4OOM1dIDAgjIPBpkqUOxnR2pmh85pW3nJB9KkNi4yqTssZ6rSNXNX0ehHFfusYDLuI7+tXY4I40ChQcdzQRKcL7Fb7PcQO32cqUY5we1XqZPtH11KsFoFDGYK7sckkZxVqgTnJlEQXMBZYGUoTkZ7VeoH7RvcqwWaIh80K7k5JIq1QJzkyhbMtvdSxMdqnlc1amgjmx5i5I70inNSVpFdrmaWRltkBVerHvUW57B2AUNGxyOaC+VW9xXLVrcGbcjrtkXqKZZxvveeTAL9APSgiooq1ty3RTMj//2Q==',
+ directory,
+ fileName: 'image.jpg',
+ fileSize: '1.04 kB',
+ },
+ },
+ },
+ ],
+ ],
+ 'Write to Disk': [
+ [
+ {
+ json: {
+ directory,
+ fileExtension: 'jpg',
+ fileName: writeFileNode.parameters.fileName,
+ fileSize: '1.04 kB',
+ fileType: 'image',
+ mimeType: 'image/jpeg',
+ },
+ binary: {
+ data: {
+ mimeType: 'image/jpeg',
+ fileType: 'image',
+ fileExtension: 'jpg',
+ data: '/9j/4AAQSkZJRgABAQEASABIAAD/4QBmRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAAExAAIAAAAQAAAATgAAAAAAARlJAAAD6AABGUkAAAPocGFpbnQubmV0IDUuMC4xAP/bAEMAIBYYHBgUIBwaHCQiICYwUDQwLCwwYkZKOlB0Znp4cmZwboCQuJyAiK6KbnCg2qKuvsTO0M58muLy4MjwuMrOxv/bAEMBIiQkMCowXjQ0XsaEcITGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxv/AABEIAB8AOwMBEgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AOgqgrXF2zNHJ5aKcD3oNPZ23di/VKG82bkuTh1OMgdaAdOSLtZ6G5ut0iSeWoOAKAdO27NCqUN8oQrcHDqccDrQDpyRNPdRwEKcsx7CobIebPLORwThc0inGMF724jagNpxG4OOM1dIDAgjIPBpkqUOxnR2pmh85pW3nJB9KkNi4yqTssZ6rSNXNX0ehHFfusYDLuI7+tXY4I40ChQcdzQRKcL7Fb7PcQO32cqUY5we1XqZPtH11KsFoFDGYK7sckkZxVqgTnJlEQXMBZYGUoTkZ7VeoH7RvcqwWaIh80K7k5JIq1QJzkyhbMtvdSxMdqnlc1amgjmx5i5I70inNSVpFdrmaWRltkBVerHvUW57B2AUNGxyOaC+VW9xXLVrcGbcjrtkXqKZZxvveeTAL9APSgiooq1ty3RTMj//2Q==',
+ directory,
+ fileName: 'image.jpg',
+ fileSize: '1.04 kB',
+ },
+ },
+ },
+ ],
+ ],
+ },
+ },
+ },
+ ];
+
+ const nodeTypes = Helpers.setup(tests);
+
+ for (const testData of tests) {
+ test(testData.description, async () => {
+ const { result } = await executeWorkflow(testData, nodeTypes);
+
+ const resultNodeData = Helpers.getResultNodeData(result, testData);
+ resultNodeData.forEach(({ nodeName, resultData }) => {
+ expect(resultData).toEqual(testData.output.nodeData[nodeName]);
+ });
+
+ expect(result.finished).toEqual(true);
+ });
+ }
+});
diff --git a/packages/nodes-base/nodes/Files/ReadWriteFile/test/ReadWriteFile.workflow.json b/packages/nodes-base/nodes/Files/ReadWriteFile/test/ReadWriteFile.workflow.json
new file mode 100644
index 0000000000000..46fb3bc2a01bd
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/ReadWriteFile/test/ReadWriteFile.workflow.json
@@ -0,0 +1,72 @@
+{
+ "meta": {
+ "instanceId": "104a4d08d8897b8bdeb38aaca515021075e0bd8544c983c2bb8c86e6a8e6081c"
+ },
+ "nodes": [
+ {
+ "parameters": {},
+ "id": "01b8609f-a345-41de-80bf-6d84276b5e7a",
+ "name": "When clicking \"Execute Workflow\"",
+ "type": "n8n-nodes-base.manualTrigger",
+ "typeVersion": 1,
+ "position": [
+ 700,
+ 320
+ ]
+ },
+ {
+ "parameters": {
+ "fileSelector": "C:/Test/image.jpg",
+ "options": {}
+ },
+ "id": "a1ea0fd0-cc95-4de2-bc58-bc980cb1d97e",
+ "name": "Read from Disk",
+ "type": "n8n-nodes-base.readWriteFile",
+ "typeVersion": 1,
+ "position": [
+ 920,
+ 320
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "write",
+ "fileName": "C:/Test/image-written.jpg",
+ "options": {}
+ },
+ "id": "94abac52-bd10-4b57-85b0-691c70989137",
+ "name": "Write to Disk",
+ "type": "n8n-nodes-base.readWriteFile",
+ "typeVersion": 1,
+ "position": [
+ 1140,
+ 320
+ ]
+ }
+ ],
+ "connections": {
+ "When clicking \"Execute Workflow\"": {
+ "main": [
+ [
+ {
+ "node": "Read from Disk",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Read from Disk": {
+ "main": [
+ [
+ {
+ "node": "Write to Disk",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ },
+ "pinData": {}
+}
\ No newline at end of file
diff --git a/packages/nodes-base/nodes/Files/ReadWriteFile/test/image.jpg b/packages/nodes-base/nodes/Files/ReadWriteFile/test/image.jpg
new file mode 100644
index 0000000000000..24b60c421ecc1
Binary files /dev/null and b/packages/nodes-base/nodes/Files/ReadWriteFile/test/image.jpg differ
diff --git a/packages/nodes-base/nodes/Files/test/ConvertExtract.test.ts b/packages/nodes-base/nodes/Files/test/ConvertExtract.test.ts
new file mode 100644
index 0000000000000..9ff4604263c7b
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/test/ConvertExtract.test.ts
@@ -0,0 +1,5 @@
+import { testWorkflows, getWorkflowFilenames } from '@test/nodes/Helpers';
+
+const workflows = getWorkflowFilenames(__dirname);
+
+describe('Test Convert to File Node', () => testWorkflows(workflows));
diff --git a/packages/nodes-base/nodes/Files/test/convert_extract.workflow.json b/packages/nodes-base/nodes/Files/test/convert_extract.workflow.json
new file mode 100644
index 0000000000000..ea5c3a274cfde
--- /dev/null
+++ b/packages/nodes-base/nodes/Files/test/convert_extract.workflow.json
@@ -0,0 +1,707 @@
+{
+ "name": "convert to tests",
+ "nodes": [
+ {
+ "parameters": {},
+ "id": "35cce987-aa4f-4738-bfcd-b85098948341",
+ "name": "When clicking \"Execute Workflow\"",
+ "type": "n8n-nodes-base.manualTrigger",
+ "typeVersion": 1,
+ "position": [
+ 680,
+ 1100
+ ]
+ },
+ {
+ "parameters": {
+ "fields": {
+ "values": [
+ {
+ "name": "row_number",
+ "type": "numberValue",
+ "numberValue": "2"
+ },
+ {
+ "name": "country",
+ "stringValue": "uk"
+ },
+ {
+ "name": "browser",
+ "stringValue": "firefox"
+ },
+ {
+ "name": "session_duration",
+ "type": "numberValue",
+ "numberValue": "1"
+ },
+ {
+ "name": "visits",
+ "type": "numberValue",
+ "numberValue": "1"
+ }
+ ]
+ },
+ "include": "none",
+ "options": {}
+ },
+ "id": "13305747-c966-4f46-90b3-ffff6835b714",
+ "name": "Edit Fields",
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.2,
+ "position": [
+ 980,
+ 880
+ ]
+ },
+ {
+ "parameters": {
+ "options": {}
+ },
+ "id": "b08b269d-0735-4dc4-b5a7-7870f5e115f9",
+ "name": "Convert to File",
+ "type": "n8n-nodes-base.convertToFile",
+ "typeVersion": 1,
+ "position": [
+ 1380,
+ 420
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "html",
+ "options": {}
+ },
+ "id": "c68c209f-771f-4d25-b832-3d55ee97e6ff",
+ "name": "Convert to File1",
+ "type": "n8n-nodes-base.convertToFile",
+ "typeVersion": 1,
+ "position": [
+ 1380,
+ 600
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "toJson",
+ "options": {}
+ },
+ "id": "2b6f27ed-a4dc-4a29-905f-f0a921ea6ee7",
+ "name": "Convert to File2",
+ "type": "n8n-nodes-base.convertToFile",
+ "typeVersion": 1,
+ "position": [
+ 1380,
+ 780
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "toJson",
+ "mode": "each",
+ "options": {}
+ },
+ "id": "cd482d12-7547-4eb6-880b-bd2c31ef06d5",
+ "name": "Convert to File3",
+ "type": "n8n-nodes-base.convertToFile",
+ "typeVersion": 1,
+ "position": [
+ 1380,
+ 960
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "xlsx",
+ "options": {}
+ },
+ "id": "1fd693e3-4286-49f4-ba45-70584c1d67f7",
+ "name": "Convert to File5",
+ "type": "n8n-nodes-base.convertToFile",
+ "typeVersion": 1,
+ "position": [
+ 1380,
+ 1140
+ ]
+ },
+ {
+ "parameters": {
+ "options": {}
+ },
+ "id": "636866e9-ca0d-44a3-b27a-90cf33e26485",
+ "name": "Extract From File",
+ "type": "n8n-nodes-base.extractFromFile",
+ "typeVersion": 1,
+ "position": [
+ 1600,
+ 420
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "html",
+ "options": {}
+ },
+ "id": "5c66b2ea-94b2-4a1b-a34d-acb07fe33e13",
+ "name": "Extract From File1",
+ "type": "n8n-nodes-base.extractFromFile",
+ "typeVersion": 1,
+ "position": [
+ 1600,
+ 600
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "fromJson",
+ "options": {}
+ },
+ "id": "a03752f0-e3bb-4dd3-9aae-dc4d1471c281",
+ "name": "Extract From File2",
+ "type": "n8n-nodes-base.extractFromFile",
+ "typeVersion": 1,
+ "position": [
+ 1600,
+ 780
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "fromJson",
+ "options": {}
+ },
+ "id": "eb10c006-60d7-4842-b9e5-a364f42dd1ab",
+ "name": "Extract From File3",
+ "type": "n8n-nodes-base.extractFromFile",
+ "typeVersion": 1,
+ "position": [
+ 1600,
+ 960
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "xlsx",
+ "options": {}
+ },
+ "id": "64c98172-2a77-4e83-b19b-232899df8113",
+ "name": "Extract From File4",
+ "type": "n8n-nodes-base.extractFromFile",
+ "typeVersion": 1,
+ "position": [
+ 1600,
+ 1140
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "xls",
+ "options": {}
+ },
+ "id": "edbfd57e-d36b-470d-9e8d-9c7f67c131b4",
+ "name": "Convert to File6",
+ "type": "n8n-nodes-base.convertToFile",
+ "typeVersion": 1,
+ "position": [
+ 1380,
+ 1320
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "xls",
+ "options": {}
+ },
+ "id": "86713806-8dc6-45ec-a710-e80b839ec193",
+ "name": "Extract From File5",
+ "type": "n8n-nodes-base.extractFromFile",
+ "typeVersion": 1,
+ "position": [
+ 1600,
+ 1320
+ ]
+ },
+ {
+ "parameters": {
+ "fields": {
+ "values": [
+ {
+ "name": "base64",
+ "stringValue": "VGhpcyBpcyB0ZXh0IGNvbnZlcnRlZCB0byBiYXNlIDY0"
+ }
+ ]
+ },
+ "include": "none",
+ "options": {}
+ },
+ "id": "c205d380-2459-4d16-bb56-f862c53c25de",
+ "name": "Edit Fields1",
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.2,
+ "position": [
+ 980,
+ 1060
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "toBinary",
+ "sourceProperty": "base64",
+ "options": {}
+ },
+ "id": "3f5fe04c-63cf-47d0-a7ca-d427b95a0c52",
+ "name": "Convert to File7",
+ "type": "n8n-nodes-base.convertToFile",
+ "typeVersion": 1,
+ "position": [
+ 1380,
+ 1880
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "text",
+ "options": {}
+ },
+ "id": "dbb4eae0-f0ee-4453-aed7-7d8322974c94",
+ "name": "Extract From File6",
+ "type": "n8n-nodes-base.extractFromFile",
+ "typeVersion": 1,
+ "position": [
+ 1600,
+ 1880
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "ods",
+ "options": {}
+ },
+ "id": "e3bf810d-7795-4663-82d1-768f76adc0d9",
+ "name": "Convert to File8",
+ "type": "n8n-nodes-base.convertToFile",
+ "typeVersion": 1,
+ "position": [
+ 1380,
+ 1520
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "ods",
+ "options": {}
+ },
+ "id": "7713d273-2d51-4e3e-8ac9-9a4e6013c690",
+ "name": "Extract From File7",
+ "type": "n8n-nodes-base.extractFromFile",
+ "typeVersion": 1,
+ "position": [
+ 1600,
+ 1520
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "rtf",
+ "options": {}
+ },
+ "id": "631b29cb-dcde-42cc-a514-45622923dab6",
+ "name": "Convert to File9",
+ "type": "n8n-nodes-base.convertToFile",
+ "typeVersion": 1,
+ "position": [
+ 1380,
+ 1700
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "rtf",
+ "options": {}
+ },
+ "id": "168b6586-c89d-4b0e-880e-4ddb8ea7cb2f",
+ "name": "Extract From File8",
+ "type": "n8n-nodes-base.extractFromFile",
+ "typeVersion": 1,
+ "position": [
+ 1600,
+ 1700
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "iCal",
+ "start": "2024-01-03T00:00:00",
+ "end": "2024-01-04T00:00:00",
+ "allDay": true,
+ "additionalFields": {
+ "description": "event description"
+ }
+ },
+ "id": "2ba4acd9-2677-4b25-9379-48433ac5e9cc",
+ "name": "Convert to File10",
+ "type": "n8n-nodes-base.convertToFile",
+ "typeVersion": 1,
+ "position": [
+ 1380,
+ 2100
+ ]
+ },
+ {
+ "parameters": {
+ "operation": "fromIcs",
+ "options": {}
+ },
+ "id": "c0179d40-7de0-4e42-a6bf-97c5bb764665",
+ "name": "Extract From File9",
+ "type": "n8n-nodes-base.extractFromFile",
+ "typeVersion": 1,
+ "position": [
+ 1600,
+ 2100
+ ]
+ },
+ {
+ "parameters": {
+ "fields": {
+ "values": [
+ {
+ "name": "description",
+ "stringValue": "={{ $json.data.events[0].description }}"
+ }
+ ]
+ },
+ "include": "none",
+ "options": {}
+ },
+ "id": "8ee98090-f3b0-41d5-8122-d27e5559738f",
+ "name": "Edit Fields2",
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.2,
+ "position": [
+ 1820,
+ 2100
+ ]
+ }
+ ],
+ "pinData": {
+ "Extract From File": [
+ {
+ "json": {
+ "row_number": "2",
+ "country": "uk",
+ "browser": "firefox",
+ "session_duration": "1",
+ "visits": "1"
+ }
+ }
+ ],
+ "Extract From File1": [
+ {
+ "json": {
+ "row_number": 2,
+ "country": "uk",
+ "browser": "firefox",
+ "session_duration": 1,
+ "visits": 1
+ }
+ }
+ ],
+ "Extract From File2": [
+ {
+ "json": {
+ "data": [
+ {
+ "row_number": 2,
+ "country": "uk",
+ "browser": "firefox",
+ "session_duration": 1,
+ "visits": 1
+ }
+ ]
+ }
+ }
+ ],
+ "Extract From File3": [
+ {
+ "json": {
+ "data": {
+ "row_number": 2,
+ "country": "uk",
+ "browser": "firefox",
+ "session_duration": 1,
+ "visits": 1
+ }
+ }
+ }
+ ],
+ "Extract From File4": [
+ {
+ "json": {
+ "row_number": 2,
+ "country": "uk",
+ "browser": "firefox",
+ "session_duration": 1,
+ "visits": 1
+ }
+ }
+ ],
+ "Extract From File5": [
+ {
+ "json": {
+ "row_number": 2,
+ "country": "uk",
+ "browser": "firefox",
+ "session_duration": 1,
+ "visits": 1
+ }
+ }
+ ],
+ "Extract From File7": [
+ {
+ "json": {
+ "row_number": 2,
+ "country": "uk",
+ "browser": "firefox",
+ "session_duration": 1,
+ "visits": 1
+ }
+ }
+ ],
+ "Extract From File6": [
+ {
+ "json": {
+ "data": "This is text converted to base 64"
+ }
+ }
+ ],
+ "Extract From File8": [
+ {
+ "json": {
+ "row_number": 2,
+ "country": "uk",
+ "browser": "firefox",
+ "session_duration": 1,
+ "visits": 1
+ }
+ }
+ ],
+ "Edit Fields2": [
+ {
+ "json": {
+ "description": "event description"
+ }
+ }
+ ]
+ },
+ "connections": {
+ "When clicking \"Execute Workflow\"": {
+ "main": [
+ [
+ {
+ "node": "Edit Fields",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Edit Fields1",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Convert to File10",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Edit Fields": {
+ "main": [
+ [
+ {
+ "node": "Convert to File",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Convert to File1",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Convert to File2",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Convert to File3",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Convert to File5",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Convert to File6",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Convert to File8",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Convert to File9",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Convert to File": {
+ "main": [
+ [
+ {
+ "node": "Extract From File",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Convert to File1": {
+ "main": [
+ [
+ {
+ "node": "Extract From File1",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Convert to File2": {
+ "main": [
+ [
+ {
+ "node": "Extract From File2",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Convert to File3": {
+ "main": [
+ [
+ {
+ "node": "Extract From File3",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Convert to File5": {
+ "main": [
+ [
+ {
+ "node": "Extract From File4",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Convert to File6": {
+ "main": [
+ [
+ {
+ "node": "Extract From File5",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Convert to File7": {
+ "main": [
+ [
+ {
+ "node": "Extract From File6",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Edit Fields1": {
+ "main": [
+ [
+ {
+ "node": "Convert to File7",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Convert to File8": {
+ "main": [
+ [
+ {
+ "node": "Extract From File7",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Convert to File9": {
+ "main": [
+ [
+ {
+ "node": "Extract From File8",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Convert to File10": {
+ "main": [
+ [
+ {
+ "node": "Extract From File9",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Extract From File9": {
+ "main": [
+ [
+ {
+ "node": "Edit Fields2",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ },
+ "active": false,
+ "settings": {
+ "executionOrder": "v1"
+ },
+ "versionId": "3287cce3-f02f-45df-9d3b-2f116852c1fb",
+ "meta": {
+ "instanceId": "b888bd11cd1ddbb95450babf3e199556799d999b896f650de768b8370ee50363"
+ },
+ "id": "ZzoOtOee7hxaNcmp",
+ "tags": []
+}
diff --git a/packages/nodes-base/nodes/Ftp/Ftp.node.json b/packages/nodes-base/nodes/Ftp/Ftp.node.json
index 33da5703c2d5c..076aa4f81e7c1 100644
--- a/packages/nodes-base/nodes/Ftp/Ftp.node.json
+++ b/packages/nodes-base/nodes/Ftp/Ftp.node.json
@@ -15,7 +15,7 @@
}
]
},
- "alias": ["SFTP"],
+ "alias": ["SFTP", "FTP", "Binary", "File", "Transfer"],
"subcategories": {
"Core Nodes": ["Files"]
}
diff --git a/packages/nodes-base/nodes/Ftp/Ftp.node.ts b/packages/nodes-base/nodes/Ftp/Ftp.node.ts
index 1ba99cf990f75..e7d03b1d9f763 100644
--- a/packages/nodes-base/nodes/Ftp/Ftp.node.ts
+++ b/packages/nodes-base/nodes/Ftp/Ftp.node.ts
@@ -120,7 +120,7 @@ export class Ftp implements INodeType {
group: ['input'],
version: 1,
subtitle: '={{$parameter["protocol"] + ": " + $parameter["operation"]}}',
- description: 'Transfers files via FTP or SFTP',
+ description: 'Transfer files via FTP or SFTP',
defaults: {
name: 'FTP',
color: '#303050',
@@ -223,6 +223,7 @@ export class Ftp implements INodeType {
type: 'string',
default: '',
description: 'The file path of the file to delete. Has to contain the full path.',
+ placeholder: 'e.g. /public/documents/file-to-delete.txt',
required: true,
},
@@ -273,12 +274,12 @@ export class Ftp implements INodeType {
name: 'path',
type: 'string',
default: '',
- placeholder: '/documents/invoice.txt',
description: 'The file path of the file to download. Has to contain the full path.',
+ placeholder: 'e.g. /public/documents/file-to-download.txt',
required: true,
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
displayOptions: {
show: {
operation: ['download'],
@@ -287,7 +288,7 @@ export class Ftp implements INodeType {
name: 'binaryPropertyName',
type: 'string',
default: 'data',
- description: 'Object property name which holds binary data',
+ hint: 'The name of the output binary field to put the file in',
required: true,
},
@@ -304,6 +305,7 @@ export class Ftp implements INodeType {
name: 'oldPath',
type: 'string',
default: '',
+ placeholder: 'e.g. /public/documents/old-file.txt',
required: true,
},
{
@@ -316,6 +318,7 @@ export class Ftp implements INodeType {
name: 'newPath',
type: 'string',
default: '',
+ placeholder: 'e.g. /public/documents/new-file.txt',
required: true,
},
{
@@ -355,10 +358,11 @@ export class Ftp implements INodeType {
type: 'string',
default: '',
description: 'The file path of the file to upload. Has to contain the full path.',
+ placeholder: 'e.g. /public/documents/file-to-upload.txt',
required: true,
},
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
displayOptions: {
show: {
operation: ['upload'],
@@ -371,7 +375,7 @@ export class Ftp implements INodeType {
description: 'The text content of the file to upload',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
displayOptions: {
show: {
operation: ['upload'],
@@ -381,7 +385,7 @@ export class Ftp implements INodeType {
name: 'binaryPropertyName',
type: 'string',
default: 'data',
- description: 'Object property name which holds binary data',
+ hint: 'The name of the input binary field containing the file to be written',
required: true,
},
{
@@ -411,6 +415,7 @@ export class Ftp implements INodeType {
name: 'path',
type: 'string',
default: '/',
+ placeholder: 'e.g. /public/folder',
description: 'Path of directory to list contents of',
required: true,
},
diff --git a/packages/nodes-base/nodes/Github/Github.node.ts b/packages/nodes-base/nodes/Github/Github.node.ts
index 90a0ad8fa0125..6b3f2e4a7f3d0 100644
--- a/packages/nodes-base/nodes/Github/Github.node.ts
+++ b/packages/nodes-base/nodes/Github/Github.node.ts
@@ -550,7 +550,7 @@ export class Github implements INodeType {
// file:create/edit
// ----------------------------------
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
@@ -580,7 +580,7 @@ export class Github implements INodeType {
description: 'The text content of the file',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -593,7 +593,7 @@ export class Github implements INodeType {
},
},
placeholder: '',
- description: 'Name of the binary property which contains the data for the file',
+ hint: 'The name of the input binary field containing the file to be written',
},
{
displayName: 'Commit Message',
@@ -699,7 +699,7 @@ export class Github implements INodeType {
'Whether to set the data of the file as binary property instead of returning the raw API response',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -712,8 +712,7 @@ export class Github implements INodeType {
},
},
placeholder: '',
- description:
- 'Name of the binary property in which to save the binary data of the received file',
+ hint: 'The name of the output binary field to put the file in',
},
{
diff --git a/packages/nodes-base/nodes/Gitlab/Gitlab.node.ts b/packages/nodes-base/nodes/Gitlab/Gitlab.node.ts
index f0fb8bfcd6547..ecfece1211cf6 100644
--- a/packages/nodes-base/nodes/Gitlab/Gitlab.node.ts
+++ b/packages/nodes-base/nodes/Gitlab/Gitlab.node.ts
@@ -1138,7 +1138,7 @@ export class Gitlab implements INodeType {
'Whether to set the data of the file as binary property instead of returning the raw API response',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -1151,8 +1151,7 @@ export class Gitlab implements INodeType {
},
},
placeholder: '',
- description:
- 'Name of the binary property in which to save the binary data of the received file',
+ hint: 'The name of the output binary field to put the file in',
},
{
displayName: 'Additional Parameters',
@@ -1184,7 +1183,7 @@ export class Gitlab implements INodeType {
// file:create/edit
// ----------------------------------
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
@@ -1214,7 +1213,7 @@ export class Gitlab implements INodeType {
description: 'The text content of the file',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -1227,7 +1226,7 @@ export class Gitlab implements INodeType {
},
},
placeholder: '',
- description: 'Name of the binary property which contains the data for the file',
+ hint: 'The name of the input binary field containing the file to be written',
},
{
displayName: 'Commit Message',
diff --git a/packages/nodes-base/nodes/Google/Chat/descriptions/MediaDescription.ts b/packages/nodes-base/nodes/Google/Chat/descriptions/MediaDescription.ts
index 37131819aeb93..18fc055056ee8 100644
--- a/packages/nodes-base/nodes/Google/Chat/descriptions/MediaDescription.ts
+++ b/packages/nodes-base/nodes/Google/Chat/descriptions/MediaDescription.ts
@@ -42,7 +42,7 @@ export const mediaFields: INodeProperties[] = [
description: 'Name of the media that is being downloaded',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -53,6 +53,6 @@ export const mediaFields: INodeProperties[] = [
operation: ['download'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
];
diff --git a/packages/nodes-base/nodes/Google/CloudStorage/ObjectDescription.ts b/packages/nodes-base/nodes/Google/CloudStorage/ObjectDescription.ts
index 755c2c236ceb9..a4378515b96c0 100644
--- a/packages/nodes-base/nodes/Google/CloudStorage/ObjectDescription.ts
+++ b/packages/nodes-base/nodes/Google/CloudStorage/ObjectDescription.ts
@@ -496,7 +496,7 @@ export const objectFields: INodeProperties[] = [
},
},
{
- displayName: 'Use Binary Property',
+ displayName: 'Use Input Binary Field',
name: 'createFromBinary',
type: 'boolean',
displayOptions: {
@@ -510,9 +510,10 @@ export const objectFields: INodeProperties[] = [
description: 'Whether the data for creating a file should come from a binary field',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'createBinaryPropertyName',
type: 'string',
+ hint: 'The name of the input binary field containing the file to be written',
displayOptions: {
show: {
resource: ['object'],
@@ -537,9 +538,10 @@ export const objectFields: INodeProperties[] = [
description: 'Content of the file to be uploaded',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
+ hint: 'The name of the output binary field to put the file in',
displayOptions: {
show: {
resource: ['object'],
diff --git a/packages/nodes-base/nodes/Google/Drive/v1/GoogleDriveV1.node.ts b/packages/nodes-base/nodes/Google/Drive/v1/GoogleDriveV1.node.ts
index c06dc93fa6010..9099b12c582a1 100644
--- a/packages/nodes-base/nodes/Google/Drive/v1/GoogleDriveV1.node.ts
+++ b/packages/nodes-base/nodes/Google/Drive/v1/GoogleDriveV1.node.ts
@@ -355,7 +355,7 @@ const versionDescription: INodeTypeDescription = {
// file:download
// ----------------------------------
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -366,7 +366,7 @@ const versionDescription: INodeTypeDescription = {
resource: ['file'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
{
displayName: 'Options',
@@ -833,7 +833,7 @@ const versionDescription: INodeTypeDescription = {
},
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
@@ -861,7 +861,7 @@ const versionDescription: INodeTypeDescription = {
description: 'The text content of the file to upload',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -874,8 +874,7 @@ const versionDescription: INodeTypeDescription = {
},
},
placeholder: '',
- description:
- 'Name of the binary property which contains the data for the file to be uploaded',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
// ----------------------------------
diff --git a/packages/nodes-base/nodes/Google/Drive/v2/actions/file/download.operation.ts b/packages/nodes-base/nodes/Google/Drive/v2/actions/file/download.operation.ts
index 2bad381bbfc20..0ce6805cb81bd 100644
--- a/packages/nodes-base/nodes/Google/Drive/v2/actions/file/download.operation.ts
+++ b/packages/nodes-base/nodes/Google/Drive/v2/actions/file/download.operation.ts
@@ -23,13 +23,13 @@ const properties: INodeProperties[] = [
default: {},
options: [
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
placeholder: 'e.g. data',
default: 'data',
description: 'Use this field name in the following nodes, to use the binary file data',
- hint: 'The name of the output field to put the binary file data in',
+ hint: 'The name of the output binary field to put the file in',
},
{
displayName: 'Google File Conversion',
diff --git a/packages/nodes-base/nodes/Google/Slides/GoogleSlides.node.ts b/packages/nodes-base/nodes/Google/Slides/GoogleSlides.node.ts
index 183f0734e87aa..38ee4bb8ab847 100644
--- a/packages/nodes-base/nodes/Google/Slides/GoogleSlides.node.ts
+++ b/packages/nodes-base/nodes/Google/Slides/GoogleSlides.node.ts
@@ -341,12 +341,12 @@ export class GoogleSlides implements INodeType {
description: 'Name of the binary property to which to write the data of the read page',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryProperty',
type: 'string',
required: true,
default: 'data',
- description: 'Name of the binary property to which to write to',
+ hint: 'The name of the output binary field to put the file in',
displayOptions: {
show: {
resource: ['page'],
diff --git a/packages/nodes-base/nodes/Google/YouTube/ChannelDescription.ts b/packages/nodes-base/nodes/Google/YouTube/ChannelDescription.ts
index 7475c23920d6f..7503b0af01f4b 100644
--- a/packages/nodes-base/nodes/Google/YouTube/ChannelDescription.ts
+++ b/packages/nodes-base/nodes/Google/YouTube/ChannelDescription.ts
@@ -545,10 +545,11 @@ export const channelFields: INodeProperties[] = [
default: '',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryProperty',
type: 'string',
required: true,
+ hint: 'The name of the input binary field containing the file to be uploaded',
displayOptions: {
show: {
operation: ['uploadBanner'],
diff --git a/packages/nodes-base/nodes/Google/YouTube/VideoDescription.ts b/packages/nodes-base/nodes/Google/YouTube/VideoDescription.ts
index bdb6e8160f016..339d20e3f0d17 100644
--- a/packages/nodes-base/nodes/Google/YouTube/VideoDescription.ts
+++ b/packages/nodes-base/nodes/Google/YouTube/VideoDescription.ts
@@ -107,10 +107,11 @@ export const videoFields: INodeProperties[] = [
default: '',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryProperty',
type: 'string',
required: true,
+ hint: 'The name of the input binary field containing the file to be uploaded',
displayOptions: {
show: {
operation: ['upload'],
diff --git a/packages/nodes-base/nodes/HomeAssistant/CameraProxyDescription.ts b/packages/nodes-base/nodes/HomeAssistant/CameraProxyDescription.ts
index 31d76dda84ed6..540a4af70d53b 100644
--- a/packages/nodes-base/nodes/HomeAssistant/CameraProxyDescription.ts
+++ b/packages/nodes-base/nodes/HomeAssistant/CameraProxyDescription.ts
@@ -46,7 +46,7 @@ export const cameraProxyFields: INodeProperties[] = [
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -57,6 +57,6 @@ export const cameraProxyFields: INodeProperties[] = [
resource: ['cameraProxy'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
];
diff --git a/packages/nodes-base/nodes/Html/Html.node.ts b/packages/nodes-base/nodes/Html/Html.node.ts
index d6b8aee568948..10d3e75e9ce80 100644
--- a/packages/nodes-base/nodes/Html/Html.node.ts
+++ b/packages/nodes-base/nodes/Html/Html.node.ts
@@ -112,7 +112,7 @@ export class Html implements INodeType {
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'dataPropertyName',
type: 'string',
requiresDataPath: 'single',
@@ -124,8 +124,7 @@ export class Html implements INodeType {
},
default: 'data',
required: true,
- description:
- 'Name of the binary property in which the HTML to extract the data from can be found',
+ hint: 'The name of the input binary field containing the file to be extracted',
},
{
displayName: 'JSON Property',
diff --git a/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts b/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts
index 9161a0d9e9b71..d62cd05f06816 100644
--- a/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts
+++ b/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts
@@ -78,7 +78,7 @@ export class HtmlExtract implements INodeType {
description: 'If HTML should be read from binary or JSON data',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'dataPropertyName',
type: 'string',
displayOptions: {
@@ -88,8 +88,7 @@ export class HtmlExtract implements INodeType {
},
default: 'data',
required: true,
- description:
- 'Name of the binary property in which the HTML to extract the data from can be found',
+ hint: 'The name of the input binary field containing the file to be extracted',
},
{
displayName: 'JSON Property',
diff --git a/packages/nodes-base/nodes/HttpRequest/V1/HttpRequestV1.node.ts b/packages/nodes-base/nodes/HttpRequest/V1/HttpRequestV1.node.ts
index 57b7d5278a433..46b3fe61c9e9c 100644
--- a/packages/nodes-base/nodes/HttpRequest/V1/HttpRequestV1.node.ts
+++ b/packages/nodes-base/nodes/HttpRequest/V1/HttpRequestV1.node.ts
@@ -232,7 +232,7 @@ export class HttpRequestV1 implements INodeType {
description: 'Name of the property to which to write the response data',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'dataPropertyName',
type: 'string',
default: 'data',
@@ -242,7 +242,7 @@ export class HttpRequestV1 implements INodeType {
responseFormat: ['file'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
{
@@ -397,7 +397,7 @@ export class HttpRequestV1 implements INodeType {
// Body Parameter
{
- displayName: 'Send Binary Data',
+ displayName: 'Send Binary File',
name: 'sendBinaryData',
type: 'boolean',
displayOptions: {
@@ -414,7 +414,7 @@ export class HttpRequestV1 implements INodeType {
description: 'Whether binary data should be send as body',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -428,8 +428,9 @@ export class HttpRequestV1 implements INodeType {
requestMethod: ['PATCH', 'POST', 'PUT'],
},
},
+ hint: 'The name of the input binary field containing the file to be uploaded',
description:
- 'Name of the binary property which contains the data for the file to be uploaded. For Form-Data Multipart, they can be provided in the format: "sendKey1:binaryProperty1,sendKey2:binaryProperty2
',
+ 'For Form-Data Multipart, they can be provided in the format: "sendKey1:binaryProperty1,sendKey2:binaryProperty2
',
},
{
displayName: 'Body Parameters',
diff --git a/packages/nodes-base/nodes/HttpRequest/V2/HttpRequestV2.node.ts b/packages/nodes-base/nodes/HttpRequest/V2/HttpRequestV2.node.ts
index 4ccdae8df4b60..82941b01db53b 100644
--- a/packages/nodes-base/nodes/HttpRequest/V2/HttpRequestV2.node.ts
+++ b/packages/nodes-base/nodes/HttpRequest/V2/HttpRequestV2.node.ts
@@ -247,7 +247,7 @@ export class HttpRequestV2 implements INodeType {
description: 'Name of the property to which to write the response data',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'dataPropertyName',
type: 'string',
default: 'data',
@@ -257,7 +257,7 @@ export class HttpRequestV2 implements INodeType {
responseFormat: ['file'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
{
@@ -412,7 +412,7 @@ export class HttpRequestV2 implements INodeType {
// Body Parameter
{
- displayName: 'Send Binary Data',
+ displayName: 'Send Binary File',
name: 'sendBinaryData',
type: 'boolean',
displayOptions: {
@@ -429,7 +429,7 @@ export class HttpRequestV2 implements INodeType {
description: 'Whether binary data should be send as body',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -443,8 +443,9 @@ export class HttpRequestV2 implements INodeType {
requestMethod: ['PATCH', 'POST', 'PUT'],
},
},
+ hint: 'The name of the input binary field containing the file to be uploaded',
description:
- 'Name of the binary property which contains the data for the file to be uploaded. For Form-Data Multipart, they can be provided in the format: "sendKey1:binaryProperty1,sendKey2:binaryProperty2
',
+ 'For Form-Data Multipart, they can be provided in the format: "sendKey1:binaryProperty1,sendKey2:binaryProperty2
',
},
{
displayName: 'Body Parameters',
diff --git a/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts b/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts
index d6247f9c81406..fdb304083b8d0 100644
--- a/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts
+++ b/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts
@@ -372,7 +372,7 @@ export class HttpRequestV3 implements INodeType {
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
- name: 'n8n Binary Data',
+ name: 'n8n Binary File',
value: 'binaryData',
},
{
@@ -502,7 +502,7 @@ export class HttpRequestV3 implements INodeType {
options: [
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
- name: 'n8n Binary Data',
+ name: 'n8n Binary File',
value: 'formBinaryData',
},
{
diff --git a/packages/nodes-base/nodes/HumanticAI/ProfileDescription.ts b/packages/nodes-base/nodes/HumanticAI/ProfileDescription.ts
index f12dc4b4d4d90..e27350232ed6c 100644
--- a/packages/nodes-base/nodes/HumanticAI/ProfileDescription.ts
+++ b/packages/nodes-base/nodes/HumanticAI/ProfileDescription.ts
@@ -68,7 +68,7 @@ export const profileFields: INodeProperties[] = [
description: 'Whether to send a resume for a resume based analysis',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -79,7 +79,7 @@ export const profileFields: INodeProperties[] = [
sendResume: [true],
},
},
- description: 'The resume in PDF or DOCX format',
+ hint: 'The name of the input binary field containing the resume in PDF or DOCX format',
},
/* -------------------------------------------------------------------------- */
@@ -180,7 +180,7 @@ export const profileFields: INodeProperties[] = [
description: 'Additional text written by the user',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -191,6 +191,6 @@ export const profileFields: INodeProperties[] = [
sendResume: [true],
},
},
- description: 'The resume in PDF or DOCX format',
+ hint: 'The name of the input binary field containing the resume in PDF or DOCX format',
},
];
diff --git a/packages/nodes-base/nodes/ICalendar/ICalendar.node.ts b/packages/nodes-base/nodes/ICalendar/ICalendar.node.ts
index 7e7550fd7ba39..f24a3644d7075 100644
--- a/packages/nodes-base/nodes/ICalendar/ICalendar.node.ts
+++ b/packages/nodes-base/nodes/ICalendar/ICalendar.node.ts
@@ -1,21 +1,16 @@
/* eslint-disable n8n-nodes-base/node-filename-against-convention */
-import { promisify } from 'util';
import type {
IExecuteFunctions,
- IDataObject,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
-import moment from 'moment-timezone';
-
-import * as ics from 'ics';
-
-const createEvent = promisify(ics.createEvent);
+import * as createEvent from './createEvent.operation';
export class ICalendar implements INodeType {
description: INodeTypeDescription = {
+ hidden: true,
displayName: 'iCalendar',
name: 'iCal',
icon: 'fa:calendar',
@@ -44,330 +39,20 @@ export class ICalendar implements INodeType {
],
default: 'createEventFile',
},
- {
- displayName: 'Event Title',
- name: 'title',
- type: 'string',
- default: '',
- },
- {
- displayName: 'Start',
- name: 'start',
- type: 'dateTime',
- default: '',
- required: true,
- description:
- 'Date and time at which the event begins. (For all-day events, the time will be ignored.).',
- },
- {
- displayName: 'End',
- name: 'end',
- type: 'dateTime',
- default: '',
- required: true,
- description:
- 'Date and time at which the event ends. (For all-day events, the time will be ignored.).',
- },
- {
- displayName: 'All Day',
- name: 'allDay',
- type: 'boolean',
- default: false,
- description: 'Whether the event lasts all day or not',
- },
- {
- displayName: 'Binary Property',
- name: 'binaryPropertyName',
- type: 'string',
- default: 'data',
- required: true,
- description: 'The field that your iCalendar file will be available under in the output',
- },
- {
- displayName: 'Additional Fields',
- name: 'additionalFields',
- type: 'collection',
- placeholder: 'Add Field',
- default: {},
- displayOptions: {
- show: {
- operation: ['createEventFile'],
- },
- },
- options: [
- {
- displayName: 'Attendees',
- name: 'attendeesUi',
- type: 'fixedCollection',
- typeOptions: {
- multipleValues: true,
- },
- placeholder: 'Add Attendee',
- default: {},
- options: [
- {
- displayName: 'Attendees',
- name: 'attendeeValues',
- values: [
- {
- displayName: 'Name',
- name: 'name',
- type: 'string',
- required: true,
- default: '',
- },
- {
- displayName: 'Email',
- name: 'email',
- type: 'string',
- placeholder: 'name@email.com',
- required: true,
- default: '',
- },
- {
- displayName: 'RSVP',
- name: 'rsvp',
- type: 'boolean',
- default: false,
- description: 'Whether the attendee has to confirm attendance or not',
- },
- ],
- },
- ],
- },
- {
- displayName: 'Busy Status',
- name: 'busyStatus',
- type: 'options',
- options: [
- {
- name: 'Busy',
- value: 'BUSY',
- },
- {
- name: 'Tentative',
- value: 'TENTATIVE',
- },
- ],
- default: '',
- description: 'Used to specify busy status for Microsoft applications, like Outlook',
- },
- {
- displayName: 'Calendar Name',
- name: 'calName',
- type: 'string',
- default: '',
- description:
- 'Specifies the calendar (not event) name. Used by Apple iCal and Microsoft Outlook (spec).',
- },
- {
- displayName: 'Description',
- name: 'description',
- type: 'string',
- default: '',
- },
- {
- displayName: 'File Name',
- name: 'fileName',
- type: 'string',
- default: '',
- description: 'The name of the file to be generated. Default value is event.ics.',
- },
- {
- displayName: 'Geolocation',
- name: 'geolocationUi',
- type: 'fixedCollection',
- typeOptions: {
- multipleValues: false,
- },
- placeholder: 'Add Geolocation',
- default: {},
- options: [
- {
- displayName: 'Geolocation',
- name: 'geolocationValues',
- values: [
- {
- displayName: 'Latitude',
- name: 'lat',
- type: 'string',
- default: '',
- },
- {
- displayName: 'Longitude',
- name: 'lon',
- type: 'string',
- default: '',
- },
- ],
- },
- ],
- },
- {
- displayName: 'Location',
- name: 'location',
- type: 'string',
- default: '',
- description: 'The intended venue',
- },
- {
- displayName: 'Recurrence Rule',
- name: 'recurrenceRule',
- type: 'string',
- default: '',
- description:
- 'A rule to define the repeat pattern of the event (RRULE). (Rule generator).',
- },
- {
- displayName: 'Organizer',
- name: 'organizerUi',
- type: 'fixedCollection',
- typeOptions: {
- multipleValues: false,
- },
- placeholder: 'Add Organizer',
- default: {},
- options: [
- {
- displayName: 'Organizer',
- name: 'organizerValues',
- values: [
- {
- displayName: 'Name',
- name: 'name',
- type: 'string',
- default: '',
- required: true,
- },
- {
- displayName: 'Email',
- name: 'email',
- type: 'string',
- placeholder: 'name@email.com',
- default: '',
- required: true,
- },
- ],
- },
- ],
- },
- {
- displayName: 'Sequence',
- name: 'sequence',
- type: 'number',
- default: 0,
- description:
- 'When sending an update for an event (with the same uid), defines the revision sequence number',
- },
- {
- displayName: 'Status',
- name: 'status',
- type: 'options',
- options: [
- {
- name: 'Confirmed',
- value: 'CONFIRMED',
- },
- {
- name: 'Cancelled',
- value: 'CANCELLED',
- },
- {
- name: 'Tentative',
- value: 'TENTATIVE',
- },
- ],
- default: 'CONFIRMED',
- },
- {
- displayName: 'UID',
- name: 'uid',
- type: 'string',
- default: '',
- description:
- 'Universally unique ID for the event (will be auto-generated if not specified here). Should be globally unique.',
- },
- {
- displayName: 'URL',
- name: 'url',
- type: 'string',
- default: '',
- description: 'URL associated with event',
- },
- ],
- },
+ ...createEvent.description,
],
};
- async execute(this: IExecuteFunctions): Promise {
+ async execute(this: IExecuteFunctions) {
const items = this.getInputData();
- const length = items.length;
- const returnData: INodeExecutionData[] = [];
const operation = this.getNodeParameter('operation', 0);
- if (operation === 'createEventFile') {
- for (let i = 0; i < length; i++) {
- const title = this.getNodeParameter('title', i) as string;
- const allDay = this.getNodeParameter('allDay', i) as boolean;
- const start = this.getNodeParameter('start', i) as string;
- let end = this.getNodeParameter('end', i) as string;
- end = allDay ? moment(end).utc().add(1, 'day').format() : end;
- const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
- const additionalFields = this.getNodeParameter('additionalFields', i);
- let fileName = 'event.ics';
-
- const eventStart = moment(start)
- .toArray()
- .splice(0, allDay ? 3 : 6) as ics.DateArray;
- eventStart[1]++;
- const eventEnd = moment(end)
- .toArray()
- .splice(0, allDay ? 3 : 6) as ics.DateArray;
- eventEnd[1]++;
-
- if (additionalFields.fileName) {
- fileName = additionalFields.fileName as string;
- }
-
- const data: ics.EventAttributes = {
- title,
- start: eventStart,
- end: eventEnd,
- startInputType: 'utc',
- endInputType: 'utc',
- };
- if (additionalFields.geolocationUi) {
- data.geo = (additionalFields.geolocationUi as IDataObject)
- .geolocationValues as ics.GeoCoordinates;
- delete additionalFields.geolocationUi;
- }
+ let returnData: INodeExecutionData[] = [];
- if (additionalFields.organizerUi) {
- data.organizer = (additionalFields.organizerUi as IDataObject)
- .organizerValues as ics.Person;
- delete additionalFields.organizerUi;
- }
-
- if (additionalFields.attendeesUi) {
- data.attendees = (additionalFields.attendeesUi as IDataObject)
- .attendeeValues as ics.Attendee[];
- delete additionalFields.attendeesUi;
- }
-
- Object.assign(data, additionalFields);
- const buffer = Buffer.from((await createEvent(data)) as string);
- const binaryData = await this.helpers.prepareBinaryData(buffer, fileName, 'text/calendar');
- returnData.push({
- json: {},
- binary: {
- [binaryPropertyName]: binaryData,
- },
- pairedItem: {
- item: i,
- },
- });
- }
+ if (operation === 'createEventFile') {
+ returnData = await createEvent.execute.call(this, items);
}
+
return [returnData];
}
}
diff --git a/packages/nodes-base/nodes/ICalendar/createEvent.operation.ts b/packages/nodes-base/nodes/ICalendar/createEvent.operation.ts
new file mode 100644
index 0000000000000..22f263a8c1947
--- /dev/null
+++ b/packages/nodes-base/nodes/ICalendar/createEvent.operation.ts
@@ -0,0 +1,376 @@
+import {
+ type IExecuteFunctions,
+ type IDataObject,
+ type INodeExecutionData,
+ type INodeProperties,
+ NodeOperationError,
+} from 'n8n-workflow';
+
+import moment from 'moment-timezone';
+import * as ics from 'ics';
+import { promisify } from 'util';
+
+const createEvent = promisify(ics.createEvent);
+
+export const description: INodeProperties[] = [
+ {
+ displayName: 'Event Title',
+ name: 'title',
+ type: 'string',
+ default: '',
+ placeholder: 'e.g. New Event',
+ },
+ {
+ displayName: 'Start',
+ name: 'start',
+ type: 'dateTime',
+ default: '',
+ required: true,
+ description:
+ 'Date and time at which the event begins. (For all-day events, the time will be ignored.).',
+ validateType: 'dateTime',
+ },
+ {
+ displayName: 'End',
+ name: 'end',
+ type: 'dateTime',
+ default: '',
+ required: true,
+ description:
+ 'Date and time at which the event ends. (For all-day events, the time will be ignored.).',
+ hint: 'If not set, will be equal to the start date',
+ },
+ {
+ displayName: 'All Day',
+ name: 'allDay',
+ type: 'boolean',
+ default: false,
+ description: 'Whether the event lasts all day or not',
+ },
+ {
+ displayName: 'Put Output File in Field',
+ name: 'binaryPropertyName',
+ type: 'string',
+ default: 'data',
+ required: true,
+ hint: 'The name of the output binary field to put the file in',
+ description: 'The field that your iCalendar file will be available under in the output',
+ },
+ {
+ displayName: 'Options',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ options: [
+ {
+ displayName: 'Attendees',
+ name: 'attendeesUi',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ },
+ placeholder: 'Add Attendee',
+ default: {},
+ options: [
+ {
+ displayName: 'Attendees',
+ name: 'attendeeValues',
+ values: [
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ required: true,
+ default: '',
+ },
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ placeholder: 'e.g. name@email.com',
+ required: true,
+ default: '',
+ },
+ {
+ displayName: 'RSVP',
+ name: 'rsvp',
+ type: 'boolean',
+ default: false,
+ description: 'Whether the attendee has to confirm attendance or not',
+ },
+ ],
+ },
+ ],
+ },
+ {
+ displayName: 'Busy Status',
+ name: 'busyStatus',
+ type: 'options',
+ options: [
+ {
+ name: 'Busy',
+ value: 'BUSY',
+ },
+ {
+ name: 'Tentative',
+ value: 'TENTATIVE',
+ },
+ ],
+ default: '',
+ description: 'Used to specify busy status for Microsoft applications, like Outlook',
+ },
+ {
+ displayName: 'Calendar Name',
+ name: 'calName',
+ type: 'string',
+ default: '',
+ description:
+ 'Specifies the calendar (not event) name. Used by Apple iCal and Microsoft Outlook. More info.',
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'File Name',
+ name: 'fileName',
+ type: 'string',
+ default: '',
+ placeholder: 'e.g. event.ics',
+ description: 'The name of the file to be generated. Default name is event.ics.',
+ },
+ {
+ displayName: 'Geolocation',
+ name: 'geolocationUi',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: false,
+ },
+ placeholder: 'Add Geolocation',
+ default: {},
+ options: [
+ {
+ displayName: 'Geolocation',
+ name: 'geolocationValues',
+ values: [
+ {
+ displayName: 'Latitude',
+ name: 'lat',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Longitude',
+ name: 'lon',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+ ],
+ },
+ {
+ displayName: 'Location',
+ name: 'location',
+ type: 'string',
+ default: '',
+ description: 'The intended venue',
+ },
+ {
+ displayName: 'Recurrence Rule',
+ name: 'recurrenceRule',
+ type: 'string',
+ default: '',
+ description:
+ 'A rule to define the repeat pattern of the event (RRULE). (Rule generator).',
+ },
+ {
+ displayName: 'Organizer',
+ name: 'organizerUi',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: false,
+ },
+ placeholder: 'Add Organizer',
+ default: {},
+ options: [
+ {
+ displayName: 'Organizer',
+ name: 'organizerValues',
+ values: [
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ required: true,
+ },
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ placeholder: 'e.g. name@email.com',
+ default: '',
+ required: true,
+ },
+ ],
+ },
+ ],
+ },
+ {
+ displayName: 'Sequence',
+ name: 'sequence',
+ type: 'number',
+ default: 0,
+ description:
+ 'When sending an update for an event (with the same uid), defines the revision sequence number',
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ options: [
+ {
+ name: 'Confirmed',
+ value: 'CONFIRMED',
+ },
+ {
+ name: 'Cancelled',
+ value: 'CANCELLED',
+ },
+ {
+ name: 'Tentative',
+ value: 'TENTATIVE',
+ },
+ ],
+ default: 'CONFIRMED',
+ },
+ {
+ displayName: 'UID',
+ name: 'uid',
+ type: 'string',
+ default: '',
+ description:
+ 'Universally unique ID for the event (will be auto-generated if not specified here). Should be globally unique.',
+ },
+ {
+ displayName: 'URL',
+ name: 'url',
+ type: 'string',
+ default: '',
+ description: 'URL associated with event',
+ },
+ {
+ displayName: 'Use Workflow Timezone',
+ name: 'useWorkflowTimezone',
+ type: 'boolean',
+ default: false,
+ description: "Whether to use the workflow timezone set in node's settings rather than UTC",
+ },
+ ],
+ },
+];
+
+export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]) {
+ const returnData: INodeExecutionData[] = [];
+ const workflowTimezone = this.getTimezone();
+
+ for (let i = 0; i < items.length; i++) {
+ try {
+ const title = this.getNodeParameter('title', i) as string;
+ const allDay = this.getNodeParameter('allDay', i) as boolean;
+
+ let start = this.getNodeParameter('start', i) as string;
+ let end = this.getNodeParameter('end', i) as string;
+
+ if (!end) {
+ end = start;
+ }
+
+ end = allDay ? moment(end).utc().add(1, 'day').format() : end;
+
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
+ const options = this.getNodeParameter('additionalFields', i);
+
+ if (options.useWorkflowTimezone) {
+ start = moment(start).tz(workflowTimezone).format();
+ end = moment(end).tz(workflowTimezone).format();
+ delete options.useWorkflowTimezone;
+ }
+
+ let fileName = 'event.ics';
+
+ const eventStart = moment(start)
+ .toArray()
+ .splice(0, allDay ? 3 : 6) as ics.DateArray;
+ eventStart[1]++;
+
+ const eventEnd = moment(end)
+ .toArray()
+ .splice(0, allDay ? 3 : 6) as ics.DateArray;
+ eventEnd[1]++;
+
+ if (options.fileName) {
+ fileName = options.fileName as string;
+ }
+
+ const data: ics.EventAttributes = {
+ title,
+ start: eventStart,
+ end: eventEnd,
+ startInputType: 'utc',
+ endInputType: 'utc',
+ };
+
+ if (options.geolocationUi) {
+ data.geo = (options.geolocationUi as IDataObject).geolocationValues as ics.GeoCoordinates;
+ delete options.geolocationUi;
+ }
+
+ if (options.organizerUi) {
+ data.organizer = (options.organizerUi as IDataObject).organizerValues as ics.Person;
+ delete options.organizerUi;
+ }
+
+ if (options.attendeesUi) {
+ data.attendees = (options.attendeesUi as IDataObject).attendeeValues as ics.Attendee[];
+ delete options.attendeesUi;
+ }
+
+ Object.assign(data, options);
+ const buffer = Buffer.from((await createEvent(data)) as string);
+ const binaryData = await this.helpers.prepareBinaryData(buffer, fileName, 'text/calendar');
+ returnData.push({
+ json: {},
+ binary: {
+ [binaryPropertyName]: binaryData,
+ },
+ pairedItem: {
+ item: i,
+ },
+ });
+ } catch (error) {
+ const errorDescription = error.description;
+ if (this.continueOnFail()) {
+ returnData.push({
+ json: {
+ error: error.message,
+ },
+ pairedItem: {
+ item: i,
+ },
+ });
+ continue;
+ }
+ throw new NodeOperationError(this.getNode(), error, {
+ itemIndex: i,
+ description: errorDescription,
+ });
+ }
+ }
+
+ return returnData;
+}
diff --git a/packages/nodes-base/nodes/Jira/IssueAttachmentDescription.ts b/packages/nodes-base/nodes/Jira/IssueAttachmentDescription.ts
index c0b17190336f5..d46a86235b1ed 100644
--- a/packages/nodes-base/nodes/Jira/IssueAttachmentDescription.ts
+++ b/packages/nodes-base/nodes/Jira/IssueAttachmentDescription.ts
@@ -59,7 +59,7 @@ export const issueAttachmentFields: INodeProperties[] = [
default: '',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
displayOptions: {
show: {
resource: ['issueAttachment'],
@@ -69,7 +69,7 @@ export const issueAttachmentFields: INodeProperties[] = [
name: 'binaryPropertyName',
type: 'string',
default: 'data',
- description: 'Object property name which holds binary data',
+ hint: 'The name of the input binary field containing the file to be written',
required: true,
},
@@ -104,7 +104,7 @@ export const issueAttachmentFields: INodeProperties[] = [
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryProperty',
type: 'string',
default: 'data',
@@ -115,7 +115,7 @@ export const issueAttachmentFields: INodeProperties[] = [
download: [true],
},
},
- description: 'Object property name which holds binary data',
+ hint: 'The name of the output binary field to put the file in',
required: true,
},
/* -------------------------------------------------------------------------- */
@@ -179,7 +179,7 @@ export const issueAttachmentFields: INodeProperties[] = [
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryProperty',
type: 'string',
default: 'data',
@@ -190,7 +190,7 @@ export const issueAttachmentFields: INodeProperties[] = [
download: [true],
},
},
- description: 'Object property name which holds binary data',
+ hint: 'The name of the output binary field to put the file in',
required: true,
},
/* -------------------------------------------------------------------------- */
diff --git a/packages/nodes-base/nodes/Keap/FileDescription.ts b/packages/nodes-base/nodes/Keap/FileDescription.ts
index 7a9cdb1239b55..6471c1e5dc093 100644
--- a/packages/nodes-base/nodes/Keap/FileDescription.ts
+++ b/packages/nodes-base/nodes/Keap/FileDescription.ts
@@ -40,7 +40,7 @@ export const fileFields: INodeProperties[] = [
/* file:upload */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
@@ -53,7 +53,7 @@ export const fileFields: INodeProperties[] = [
description: 'Whether the data to upload should be taken from binary field',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -65,7 +65,7 @@ export const fileFields: INodeProperties[] = [
binaryData: [true],
},
},
- description: 'Name of the binary property which contains the data for the file to be uploaded',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'File Association',
diff --git a/packages/nodes-base/nodes/KoBoToolbox/FileDescription.ts b/packages/nodes-base/nodes/KoBoToolbox/FileDescription.ts
index 8e15042e722a7..868d1538d7fdd 100644
--- a/packages/nodes-base/nodes/KoBoToolbox/FileDescription.ts
+++ b/packages/nodes-base/nodes/KoBoToolbox/FileDescription.ts
@@ -121,7 +121,7 @@ export const fileFields: INodeProperties[] = [
},
options: [
{
- name: 'Binary Data',
+ name: 'Binary File',
value: 'binary',
},
{
diff --git a/packages/nodes-base/nodes/Line/NotificationDescription.ts b/packages/nodes-base/nodes/Line/NotificationDescription.ts
index 289a01ceb2643..b4ce909de0614 100644
--- a/packages/nodes-base/nodes/Line/NotificationDescription.ts
+++ b/packages/nodes-base/nodes/Line/NotificationDescription.ts
@@ -68,7 +68,7 @@ export const notificationFields: INodeProperties[] = [
displayName: 'Image',
values: [
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
@@ -98,7 +98,7 @@ export const notificationFields: INodeProperties[] = [
description: 'HTTP/HTTPS URL. Maximum size of 240×240px JPEG.',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryProperty',
type: 'string',
displayOptions: {
@@ -107,7 +107,7 @@ export const notificationFields: INodeProperties[] = [
},
},
default: 'data',
- description: 'Name of the property that holds the binary data',
+ hint: 'The name of the input binary field containing the file to be written',
},
],
},
diff --git a/packages/nodes-base/nodes/LinkedIn/PostDescription.ts b/packages/nodes-base/nodes/LinkedIn/PostDescription.ts
index 9a4ebbaf04b75..8f2512f09fcc1 100644
--- a/packages/nodes-base/nodes/LinkedIn/PostDescription.ts
+++ b/packages/nodes-base/nodes/LinkedIn/PostDescription.ts
@@ -121,7 +121,7 @@ export const postFields: INodeProperties[] = [
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
displayOptions: {
show: {
operation: ['create'],
@@ -132,7 +132,7 @@ export const postFields: INodeProperties[] = [
name: 'binaryPropertyName',
type: 'string',
default: 'data',
- description: 'Object property name which holds binary data',
+ hint: 'The name of the input binary field containing the file to be written',
required: true,
},
{
@@ -173,11 +173,11 @@ export const postFields: INodeProperties[] = [
},
},
{
- displayName: 'Thumbnail Binary Property',
+ displayName: 'Input Binary Field',
name: 'thumbnailBinaryPropertyName',
type: 'string',
default: 'data',
- description: 'Object property name which holds binary data for the article thumbnail',
+ hint: 'The name of the input binary field containing the file for the article thumbnail',
displayOptions: {
show: {
'/shareMediaCategory': ['ARTICLE'],
diff --git a/packages/nodes-base/nodes/Matrix/MediaDescription.ts b/packages/nodes-base/nodes/Matrix/MediaDescription.ts
index 38e78557a2b25..23f6ddeb0b9a7 100644
--- a/packages/nodes-base/nodes/Matrix/MediaDescription.ts
+++ b/packages/nodes-base/nodes/Matrix/MediaDescription.ts
@@ -46,11 +46,12 @@ export const mediaFields: INodeProperties[] = [
required: true,
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
+ hint: 'The name of the input binary field containing the file to be uploaded',
displayOptions: {
show: {
resource: ['media'],
diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/versionDescription.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/versionDescription.ts
index 128c25231ef5b..76deccbad3b84 100644
--- a/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/versionDescription.ts
+++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/actions/versionDescription.ts
@@ -27,7 +27,7 @@ export const versionDescription: INodeTypeDescription = {
properties: [
{
displayName:
- 'This node connects to the Microsoft 365 cloud platform. Use the \'Spreadsheet File\' node to directly manipulate spreadsheet files (.xls, .csv, etc). More info.',
+ 'This node connects to the Microsoft 365 cloud platform. Use the \'Extract From File\' and \'Convert to File\' nodes to directly manipulate spreadsheet files (.xls, .csv, etc). More info.',
name: 'notice',
type: 'notice',
default: '',
diff --git a/packages/nodes-base/nodes/Microsoft/OneDrive/FileDescription.ts b/packages/nodes-base/nodes/Microsoft/OneDrive/FileDescription.ts
index 93b1dbcefd6e7..80e6a71bc5a12 100644
--- a/packages/nodes-base/nodes/Microsoft/OneDrive/FileDescription.ts
+++ b/packages/nodes-base/nodes/Microsoft/OneDrive/FileDescription.ts
@@ -209,7 +209,7 @@ export const fileFields: INodeProperties[] = [
default: '',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -220,7 +220,7 @@ export const fileFields: INodeProperties[] = [
resource: ['file'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
/* -------------------------------------------------------------------------- */
/* file:get */
@@ -380,7 +380,7 @@ export const fileFields: INodeProperties[] = [
description: 'ID of the parent folder that will contain the file',
},
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
@@ -410,7 +410,7 @@ export const fileFields: INodeProperties[] = [
description: 'The text content of the file',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -423,6 +423,6 @@ export const fileFields: INodeProperties[] = [
},
},
placeholder: '',
- description: 'Name of the binary property which contains the data for the file',
+ hint: 'The name of the input binary field containing the file to be written',
},
];
diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/v1/MessageAttachmentDescription.ts b/packages/nodes-base/nodes/Microsoft/Outlook/v1/MessageAttachmentDescription.ts
index 7669c2d890300..b374a7c1f7e48 100644
--- a/packages/nodes-base/nodes/Microsoft/Outlook/v1/MessageAttachmentDescription.ts
+++ b/packages/nodes-base/nodes/Microsoft/Outlook/v1/MessageAttachmentDescription.ts
@@ -106,9 +106,9 @@ export const messageAttachmentFields: INodeProperties[] = [
// File operations
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
type: 'string',
required: true,
default: 'data',
diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/v1/MessageDescription.ts b/packages/nodes-base/nodes/Microsoft/Outlook/v1/MessageDescription.ts
index fa5b5d53e5675..a490489c723ba 100644
--- a/packages/nodes-base/nodes/Microsoft/Outlook/v1/MessageDescription.ts
+++ b/packages/nodes-base/nodes/Microsoft/Outlook/v1/MessageDescription.ts
@@ -559,9 +559,9 @@ export const messageFields: INodeProperties[] = [
// File operations
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
type: 'string',
required: true,
default: 'data',
diff --git a/packages/nodes-base/nodes/Mindee/Mindee.node.ts b/packages/nodes-base/nodes/Mindee/Mindee.node.ts
index d42b1f8f8c06a..e6fefcbd1b3f3 100644
--- a/packages/nodes-base/nodes/Mindee/Mindee.node.ts
+++ b/packages/nodes-base/nodes/Mindee/Mindee.node.ts
@@ -156,7 +156,7 @@ export class Mindee implements INodeType {
default: 'predict',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -167,8 +167,7 @@ export class Mindee implements INodeType {
resource: ['receipt', 'invoice'],
},
},
- description:
- 'Name of the binary property which containsthe data for the file to be uploaded',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'RAW Data',
diff --git a/packages/nodes-base/nodes/MoveBinaryData/MoveBinaryData.node.ts b/packages/nodes-base/nodes/MoveBinaryData/MoveBinaryData.node.ts
index 1081848431af1..e4c536250aede 100644
--- a/packages/nodes-base/nodes/MoveBinaryData/MoveBinaryData.node.ts
+++ b/packages/nodes-base/nodes/MoveBinaryData/MoveBinaryData.node.ts
@@ -42,6 +42,7 @@ encodeDecodeOptions.sort((a, b) => {
export class MoveBinaryData implements INodeType {
description: INodeTypeDescription = {
+ hidden: true,
displayName: 'Convert to/from binary data',
name: 'moveBinaryData',
icon: 'fa:exchange-alt',
@@ -179,6 +180,20 @@ export class MoveBinaryData implements INodeType {
placeholder: 'Add Option',
default: {},
options: [
+ {
+ displayName: 'Add Byte Order Mark (BOM)',
+ name: 'addBOM',
+ description:
+ 'Whether to add special marker at the start of your text file. This marker helps some programs understand how to read the file correctly.',
+ displayOptions: {
+ show: {
+ '/mode': ['jsonToBinary'],
+ encoding: bomAware,
+ },
+ },
+ type: 'boolean',
+ default: false,
+ },
{
displayName: 'Data Is Base64',
name: 'dataIsBase64',
@@ -206,7 +221,7 @@ export class MoveBinaryData implements INodeType {
},
},
default: 'utf8',
- description: 'Set the encoding of the data stream',
+ description: 'Choose the character set to use to encode the data',
},
{
displayName: 'Strip BOM',
@@ -220,18 +235,6 @@ export class MoveBinaryData implements INodeType {
type: 'boolean',
default: true,
},
- {
- displayName: 'Add BOM',
- name: 'addBOM',
- displayOptions: {
- show: {
- '/mode': ['jsonToBinary'],
- encoding: bomAware,
- },
- },
- type: 'boolean',
- default: false,
- },
{
displayName: 'File Name',
name: 'fileName',
diff --git a/packages/nodes-base/nodes/Nasa/Nasa.node.ts b/packages/nodes-base/nodes/Nasa/Nasa.node.ts
index e9fa2a8d4cc4a..14e601d99adfa 100644
--- a/packages/nodes-base/nodes/Nasa/Nasa.node.ts
+++ b/packages/nodes-base/nodes/Nasa/Nasa.node.ts
@@ -570,7 +570,7 @@ export class Nasa implements INodeType {
'By default just the URL of the image is returned. When set to true the image will be downloaded.',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -582,7 +582,7 @@ export class Nasa implements INodeType {
download: [true],
},
},
- description: 'Name of the binary property to which to write to',
+ hint: 'The name of the output binary field to put the file in',
},
/* date for astronomyPictureOfTheDay */
@@ -766,7 +766,7 @@ export class Nasa implements INodeType {
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -777,7 +777,7 @@ export class Nasa implements INodeType {
resource: ['earthImagery'],
},
},
- description: 'Name of the binary property to which to write to',
+ hint: 'The name of the output binary field to put the file in',
},
//aqui
diff --git a/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts b/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts
index bd2190e82f7d0..510dffbc0e319 100644
--- a/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts
+++ b/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts
@@ -350,7 +350,7 @@ export class NextCloud implements INodeType {
'The file path of the file to download. Has to contain the full path. The path should start with "/".',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -361,7 +361,7 @@ export class NextCloud implements INodeType {
resource: ['file'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
// ----------------------------------
@@ -384,7 +384,7 @@ export class NextCloud implements INodeType {
'The absolute file path of the file to upload. Has to contain the full path. The parent folder has to exist. Existing files get overwritten.',
},
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryDataUpload',
type: 'boolean',
default: false,
@@ -412,7 +412,7 @@ export class NextCloud implements INodeType {
description: 'The text content of the file to upload',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -425,8 +425,7 @@ export class NextCloud implements INodeType {
},
},
placeholder: '',
- description:
- 'Name of the binary property which contains the data for the file to be uploaded',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
// ----------------------------------
diff --git a/packages/nodes-base/nodes/NocoDB/OperationDescription.ts b/packages/nodes-base/nodes/NocoDB/OperationDescription.ts
index 1c73d3fa6a30c..0d1b7ec7437d7 100644
--- a/packages/nodes-base/nodes/NocoDB/OperationDescription.ts
+++ b/packages/nodes-base/nodes/NocoDB/OperationDescription.ts
@@ -495,7 +495,7 @@ export const operationFields: INodeProperties[] = [
default: '',
},
{
- displayName: 'Is Binary Data',
+ displayName: 'Is Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
diff --git a/packages/nodes-base/nodes/OpenAi/ImageDescription.ts b/packages/nodes-base/nodes/OpenAi/ImageDescription.ts
index 15dc95147ab18..19f000d63eab9 100644
--- a/packages/nodes-base/nodes/OpenAi/ImageDescription.ts
+++ b/packages/nodes-base/nodes/OpenAi/ImageDescription.ts
@@ -137,7 +137,7 @@ const createOperations: INodeProperties[] = [
},
options: [
{
- name: 'Binary Data',
+ name: 'Binary File',
value: 'binaryData',
},
{
diff --git a/packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts b/packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts
index e3fe26861e3e8..a3ed0da58946b 100644
--- a/packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts
+++ b/packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts
@@ -1890,7 +1890,7 @@ export class Pipedrive implements INodeType {
// file:create
// ----------------------------------
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -1902,8 +1902,7 @@ export class Pipedrive implements INodeType {
},
},
placeholder: '',
- description:
- 'Name of the binary property which contains the data for the file to be created',
+ hint: 'The name of the input binary field containing the file to be written',
},
{
displayName: 'Additional Fields',
@@ -1996,7 +1995,7 @@ export class Pipedrive implements INodeType {
description: 'ID of the file to download',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -2007,8 +2006,7 @@ export class Pipedrive implements INodeType {
resource: ['file'],
},
},
- description:
- 'Name of the binary property to which to write the data of the downloaded file',
+ hint: 'The name of the output binary field to put the file in',
},
// ----------------------------------
diff --git a/packages/nodes-base/nodes/Pushbullet/Pushbullet.node.ts b/packages/nodes-base/nodes/Pushbullet/Pushbullet.node.ts
index 99b975dcc5b01..ff4d4770c267d 100644
--- a/packages/nodes-base/nodes/Pushbullet/Pushbullet.node.ts
+++ b/packages/nodes-base/nodes/Pushbullet/Pushbullet.node.ts
@@ -156,7 +156,7 @@ export class Pushbullet implements INodeType {
description: 'URL of the push',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -169,8 +169,7 @@ export class Pushbullet implements INodeType {
},
},
placeholder: '',
- description:
- 'Name of the binary property which contains the data for the file to be created',
+ hint: 'The name of the input binary field containing the file to be written',
},
{
displayName: 'Target',
diff --git a/packages/nodes-base/nodes/Pushover/Pushover.node.ts b/packages/nodes-base/nodes/Pushover/Pushover.node.ts
index ed3774a5dc08c..fab8ecdf80413 100644
--- a/packages/nodes-base/nodes/Pushover/Pushover.node.ts
+++ b/packages/nodes-base/nodes/Pushover/Pushover.node.ts
@@ -196,13 +196,12 @@ export class Pushover implements INodeType {
displayName: 'Attachment Property',
values: [
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: '',
placeholder: 'data',
- description:
- 'Name of the binary properties which contain data which should be added to email as attachment',
+ hint: 'The name of the input binary field containing the file which should be added to email as attachment',
},
],
},
diff --git a/packages/nodes-base/nodes/QuickBase/FileDescription.ts b/packages/nodes-base/nodes/QuickBase/FileDescription.ts
index 343f23cdfe127..89e7716c3a7aa 100644
--- a/packages/nodes-base/nodes/QuickBase/FileDescription.ts
+++ b/packages/nodes-base/nodes/QuickBase/FileDescription.ts
@@ -90,7 +90,7 @@ export const fileFields: INodeProperties[] = [
description: 'The file attachment version number',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
displayOptions: {
show: {
resource: ['file'],
@@ -100,7 +100,7 @@ export const fileFields: INodeProperties[] = [
name: 'binaryPropertyName',
type: 'string',
default: 'data',
- description: 'Object property name which holds binary data',
+ hint: 'The name of the input binary field containing the file to be written',
required: true,
},
];
diff --git a/packages/nodes-base/nodes/QuickBooks/descriptions/Estimate/EstimateDescription.ts b/packages/nodes-base/nodes/QuickBooks/descriptions/Estimate/EstimateDescription.ts
index b286dfeb0452e..6c19bd7743317 100644
--- a/packages/nodes-base/nodes/QuickBooks/descriptions/Estimate/EstimateDescription.ts
+++ b/packages/nodes-base/nodes/QuickBooks/descriptions/Estimate/EstimateDescription.ts
@@ -210,12 +210,12 @@ export const estimateFields: INodeProperties[] = [
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryProperty',
type: 'string',
required: true,
default: 'data',
- description: 'Name of the binary property to which to write to',
+ hint: 'The name of the output binary field to put the file in',
displayOptions: {
show: {
resource: ['estimate'],
diff --git a/packages/nodes-base/nodes/QuickBooks/descriptions/Invoice/InvoiceDescription.ts b/packages/nodes-base/nodes/QuickBooks/descriptions/Invoice/InvoiceDescription.ts
index 0e9c7c0e2734a..d15cd417f9931 100644
--- a/packages/nodes-base/nodes/QuickBooks/descriptions/Invoice/InvoiceDescription.ts
+++ b/packages/nodes-base/nodes/QuickBooks/descriptions/Invoice/InvoiceDescription.ts
@@ -215,12 +215,12 @@ export const invoiceFields: INodeProperties[] = [
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryProperty',
type: 'string',
required: true,
default: 'data',
- description: 'Name of the binary property to which to write to',
+ hint: 'The name of the output binary field to put the file in',
displayOptions: {
show: {
resource: ['invoice'],
diff --git a/packages/nodes-base/nodes/QuickBooks/descriptions/Payment/PaymentDescription.ts b/packages/nodes-base/nodes/QuickBooks/descriptions/Payment/PaymentDescription.ts
index ff338841bc94a..faeec56d4a9b4 100644
--- a/packages/nodes-base/nodes/QuickBooks/descriptions/Payment/PaymentDescription.ts
+++ b/packages/nodes-base/nodes/QuickBooks/descriptions/Payment/PaymentDescription.ts
@@ -154,12 +154,12 @@ export const paymentFields: INodeProperties[] = [
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryProperty',
type: 'string',
required: true,
default: 'data',
- description: 'Name of the binary property to which to write to',
+ hint: 'The name of the output binary field to put the file in',
displayOptions: {
show: {
resource: ['payment'],
diff --git a/packages/nodes-base/nodes/ReadBinaryFiles/ReadBinaryFiles.node.ts b/packages/nodes-base/nodes/ReadBinaryFiles/ReadBinaryFiles.node.ts
index 1368de2066a8a..a1f0939c9e91b 100644
--- a/packages/nodes-base/nodes/ReadBinaryFiles/ReadBinaryFiles.node.ts
+++ b/packages/nodes-base/nodes/ReadBinaryFiles/ReadBinaryFiles.node.ts
@@ -10,6 +10,7 @@ import { generatePairedItemData } from '../../utils/utilities';
export class ReadBinaryFiles implements INodeType {
description: INodeTypeDescription = {
+ hidden: true,
displayName: 'Read Binary Files',
name: 'readBinaryFiles',
icon: 'fa:file-import',
diff --git a/packages/nodes-base/nodes/ReadPdf/ReadPDF.node.ts b/packages/nodes-base/nodes/ReadPdf/ReadPDF.node.ts
index b638862970662..ff882dfca5ffa 100644
--- a/packages/nodes-base/nodes/ReadPdf/ReadPDF.node.ts
+++ b/packages/nodes-base/nodes/ReadPdf/ReadPDF.node.ts
@@ -1,35 +1,15 @@
import {
- BINARY_ENCODING,
+ NodeOperationError,
type IExecuteFunctions,
type INodeExecutionData,
type INodeType,
type INodeTypeDescription,
} from 'n8n-workflow';
-
-import { getDocument as readPDF, version as pdfJsVersion } from 'pdfjs-dist';
-
-type Document = Awaited>['promise']>;
-type Page = Awaited>>;
-type TextContent = Awaited>;
-
-const parseText = (textContent: TextContent) => {
- let lastY = undefined;
- const text = [];
- for (const item of textContent.items) {
- if ('str' in item) {
- if (lastY == item.transform[5] || !lastY) {
- text.push(item.str);
- } else {
- text.push(`\n${item.str}`);
- }
- lastY = item.transform[5];
- }
- }
- return text.join('');
-};
+import { extractDataFromPDF } from '@utils/binary';
export class ReadPDF implements INodeType {
description: INodeTypeDescription = {
+ hidden: true,
displayName: 'Read PDF',
// eslint-disable-next-line n8n-nodes-base/node-class-description-name-miscased
name: 'readPDF',
@@ -45,7 +25,7 @@ export class ReadPDF implements INodeType {
outputs: ['main'],
properties: [
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -84,46 +64,26 @@ export class ReadPDF implements INodeType {
for (let itemIndex = 0; itemIndex < length; itemIndex++) {
try {
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', itemIndex);
- const binaryData = this.helpers.assertBinaryData(itemIndex, binaryPropertyName);
-
- const params: { password?: string; url?: URL; data?: ArrayBuffer } = {};
+ let password;
if (this.getNodeParameter('encrypted', itemIndex) === true) {
- params.password = this.getNodeParameter('password', itemIndex) as string;
- }
-
- if (binaryData.id) {
- const binaryPath = this.helpers.getBinaryPath(binaryData.id);
- params.url = new URL(`file://${binaryPath}`);
- } else {
- params.data = Buffer.from(binaryData.data, BINARY_ENCODING).buffer;
+ password = this.getNodeParameter('password', itemIndex) as string;
}
- const document = await readPDF(params).promise;
- const { info, metadata } = await document
- .getMetadata()
- .catch(() => ({ info: null, metadata: null }));
-
- const pages = [];
- for (let i = 1; i <= document.numPages; i++) {
- const page = await document.getPage(i);
- const text = await page.getTextContent().then(parseText);
- pages.push(text);
- }
+ const json = await extractDataFromPDF.call(
+ this,
+ binaryPropertyName,
+ password,
+ undefined,
+ undefined,
+ itemIndex,
+ );
returnData.push({
binary: items[itemIndex].binary,
- json: {
- numpages: document.numPages,
- numrender: document.numPages,
- info,
- metadata: metadata?.getAll(),
- text: pages.join('\n\n'),
- version: pdfJsVersion,
- },
+ json,
});
} catch (error) {
- console.log(error);
if (this.continueOnFail()) {
returnData.push({
json: {
@@ -135,7 +95,7 @@ export class ReadPDF implements INodeType {
});
continue;
}
- throw error;
+ throw new NodeOperationError(this.getNode(), error, { itemIndex });
}
}
return [returnData];
diff --git a/packages/nodes-base/nodes/Salesforce/AttachmentDescription.ts b/packages/nodes-base/nodes/Salesforce/AttachmentDescription.ts
index d26a97d68b171..0a6c17968ca92 100644
--- a/packages/nodes-base/nodes/Salesforce/AttachmentDescription.ts
+++ b/packages/nodes-base/nodes/Salesforce/AttachmentDescription.ts
@@ -86,7 +86,7 @@ export const attachmentFields: INodeProperties[] = [
'Required. Name of the attached file. Maximum size is 255 characters. Label is File Name.',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -98,7 +98,7 @@ export const attachmentFields: INodeProperties[] = [
},
},
placeholder: '',
- description: 'Name of the binary property which contains the data for the file to be uploaded',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'Additional Fields',
@@ -173,13 +173,12 @@ export const attachmentFields: INodeProperties[] = [
},
options: [
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
placeholder: '',
- description:
- 'Name of the binary property which contains the data for the file to be uploaded',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'Description',
diff --git a/packages/nodes-base/nodes/Salesforce/DocumentDescription.ts b/packages/nodes-base/nodes/Salesforce/DocumentDescription.ts
index b2232fc3bdb4f..20b4062a47baf 100644
--- a/packages/nodes-base/nodes/Salesforce/DocumentDescription.ts
+++ b/packages/nodes-base/nodes/Salesforce/DocumentDescription.ts
@@ -42,7 +42,7 @@ export const documentFields: INodeProperties[] = [
description: 'Name of the file',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -54,7 +54,7 @@ export const documentFields: INodeProperties[] = [
},
},
placeholder: '',
- description: 'Name of the binary property which contains the data for the file to be uploaded',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'Additional Fields',
diff --git a/packages/nodes-base/nodes/SecurityScorecard/descriptions/ReportDescription.ts b/packages/nodes-base/nodes/SecurityScorecard/descriptions/ReportDescription.ts
index 805e6a7d7c8d9..f856770225837 100644
--- a/packages/nodes-base/nodes/SecurityScorecard/descriptions/ReportDescription.ts
+++ b/packages/nodes-base/nodes/SecurityScorecard/descriptions/ReportDescription.ts
@@ -299,7 +299,7 @@ export const reportFields: INodeProperties[] = [
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
@@ -310,6 +310,6 @@ export const reportFields: INodeProperties[] = [
operation: ['download'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
];
diff --git a/packages/nodes-base/nodes/Set/Set.node.json b/packages/nodes-base/nodes/Set/Set.node.json
index ec62590cae190..d6f5a78a97eab 100644
--- a/packages/nodes-base/nodes/Set/Set.node.json
+++ b/packages/nodes-base/nodes/Set/Set.node.json
@@ -121,7 +121,7 @@
}
]
},
- "alias": ["Set", "JSON", "Filter", "Transform", "Map"],
+ "alias": ["Set", "JS", "JSON", "Filter", "Transform", "Map"],
"subcategories": {
"Core Nodes": ["Data Transformation"]
}
diff --git a/packages/nodes-base/nodes/Set/v2/SetV2.node.ts b/packages/nodes-base/nodes/Set/v2/SetV2.node.ts
index 6bf13b9547297..1076c0d2d187d 100644
--- a/packages/nodes-base/nodes/Set/v2/SetV2.node.ts
+++ b/packages/nodes-base/nodes/Set/v2/SetV2.node.ts
@@ -157,7 +157,7 @@ const versionDescription: INodeTypeDescription = {
default: {},
options: [
{
- displayName: 'Include Binary Data',
+ displayName: 'Include Binary File',
name: 'includeBinary',
type: 'boolean',
default: true,
diff --git a/packages/nodes-base/nodes/Slack/V1/FileDescription.ts b/packages/nodes-base/nodes/Slack/V1/FileDescription.ts
index e22ad88882cbb..57ffb0ac1a925 100644
--- a/packages/nodes-base/nodes/Slack/V1/FileDescription.ts
+++ b/packages/nodes-base/nodes/Slack/V1/FileDescription.ts
@@ -40,7 +40,7 @@ export const fileFields: INodeProperties[] = [
/* file:upload */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
@@ -68,7 +68,7 @@ export const fileFields: INodeProperties[] = [
description: 'The text content of the file to upload',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -81,7 +81,7 @@ export const fileFields: INodeProperties[] = [
},
},
placeholder: '',
- description: 'Name of the binary property which contains the data for the file to be uploaded',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'Options',
diff --git a/packages/nodes-base/nodes/Slack/V2/FileDescription.ts b/packages/nodes-base/nodes/Slack/V2/FileDescription.ts
index fdd1aaf9beec1..aecf8e2bfb0cc 100644
--- a/packages/nodes-base/nodes/Slack/V2/FileDescription.ts
+++ b/packages/nodes-base/nodes/Slack/V2/FileDescription.ts
@@ -39,7 +39,7 @@ export const fileFields: INodeProperties[] = [
/* file:upload */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
@@ -66,7 +66,7 @@ export const fileFields: INodeProperties[] = [
placeholder: '',
},
{
- displayName: 'Binary Property',
+ displayName: 'File Property',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
diff --git a/packages/nodes-base/nodes/SpreadsheetFile/SpreadsheetFile.node.ts b/packages/nodes-base/nodes/SpreadsheetFile/SpreadsheetFile.node.ts
index e59692b423222..d1333c7a25317 100644
--- a/packages/nodes-base/nodes/SpreadsheetFile/SpreadsheetFile.node.ts
+++ b/packages/nodes-base/nodes/SpreadsheetFile/SpreadsheetFile.node.ts
@@ -7,6 +7,7 @@ import { SpreadsheetFileV2 } from './v2/SpreadsheetFileV2.node';
export class SpreadsheetFile extends VersionedNodeType {
constructor() {
const baseDescription: INodeTypeBaseDescription = {
+ hidden: true,
displayName: 'Spreadsheet File',
name: 'spreadsheetFile',
icon: 'fa:table',
diff --git a/packages/nodes-base/nodes/SpreadsheetFile/description.ts b/packages/nodes-base/nodes/SpreadsheetFile/description.ts
index 903245fc61db8..78c8952b90d35 100644
--- a/packages/nodes-base/nodes/SpreadsheetFile/description.ts
+++ b/packages/nodes-base/nodes/SpreadsheetFile/description.ts
@@ -1,97 +1,41 @@
import type { INodeProperties } from 'n8n-workflow';
-export const operationProperties: INodeProperties[] = [
- {
- displayName: 'Operation',
- name: 'operation',
- type: 'options',
- noDataExpression: true,
- options: [
- {
- name: 'Read From File',
- value: 'fromFile',
- description: 'Reads data from a spreadsheet file',
- action: 'Read data from a spreadsheet file',
- },
- {
- name: 'Write to File',
- value: 'toFile',
- description: 'Writes the workflow data to a spreadsheet file',
- action: 'Write data to a spreadsheet file',
- },
- ],
- default: 'fromFile',
- },
-];
-
-export const fromFileProperties: INodeProperties[] = [
- {
- displayName: 'Binary Property',
- name: 'binaryPropertyName',
- type: 'string',
- default: 'data',
- required: true,
- displayOptions: {
- show: {
- operation: ['fromFile'],
- },
+export const operationProperty: INodeProperties = {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ noDataExpression: true,
+ options: [
+ {
+ name: 'Read From File',
+ value: 'fromFile',
+ description: 'Reads data from a spreadsheet file',
+ action: 'Read data from a spreadsheet file',
},
- placeholder: '',
- description:
- 'Name of the binary property from which to read the binary data of the spreadsheet file',
- },
-];
+ {
+ name: 'Write to File',
+ value: 'toFile',
+ description: 'Writes the workflow data to a spreadsheet file',
+ action: 'Write data to a spreadsheet file',
+ },
+ ],
+ default: 'fromFile',
+};
-export const fromFileV2Properties: INodeProperties[] = [
- {
- displayName: 'File Format',
- name: 'fileFormat',
- type: 'options',
- options: [
- {
- name: 'Autodetect',
- value: 'autodetect',
- },
- {
- name: 'CSV',
- value: 'csv',
- description: 'Comma-separated values',
- },
- {
- name: 'HTML',
- value: 'html',
- description: 'HTML Table',
- },
- {
- name: 'ODS',
- value: 'ods',
- description: 'OpenDocument Spreadsheet',
- },
- {
- name: 'RTF',
- value: 'rtf',
- description: 'Rich Text Format',
- },
- {
- name: 'XLS',
- value: 'xls',
- description: 'Excel',
- },
- {
- name: 'XLSX',
- value: 'xlsx',
- description: 'Excel',
- },
- ],
- default: 'autodetect',
- displayOptions: {
- show: {
- operation: ['fromFile'],
- },
+export const binaryProperty: INodeProperties = {
+ displayName: 'Input Binary Field',
+ name: 'binaryPropertyName',
+ type: 'string',
+ default: 'data',
+ required: true,
+ placeholder: '',
+ hint: 'The name of the input field containing the file data to be processed',
+ displayOptions: {
+ show: {
+ operation: ['fromFile'],
},
- description: 'The format of the binary data to read from',
},
-];
+};
export const toFileProperties: INodeProperties[] = [
{
@@ -139,7 +83,7 @@ export const toFileProperties: INodeProperties[] = [
description: 'The format of the file to save the data as',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -150,189 +94,176 @@ export const toFileProperties: INodeProperties[] = [
},
},
placeholder: '',
- description:
- 'Name of the binary property in which to save the binary data of the spreadsheet file',
+ hint: 'The name of the output binary field to put the file in',
},
];
-export const optionsProperties: INodeProperties[] = [
- {
- displayName: 'Options',
- name: 'options',
- type: 'collection',
- placeholder: 'Add Option',
- default: {},
- options: [
- {
- displayName: 'Compression',
- name: 'compression',
- type: 'boolean',
- displayOptions: {
- show: {
- '/operation': ['toFile'],
- '/fileFormat': ['xlsx', 'ods'],
- },
- },
- default: false,
- description: 'Whether compression will be applied or not',
- },
- {
- displayName: 'File Name',
- name: 'fileName',
- type: 'string',
- displayOptions: {
- show: {
- '/operation': ['toFile'],
- },
- },
- default: '',
- description:
- 'File name to set in binary data. By default will "spreadsheet.<fileFormat>" be used.',
- },
- {
- displayName: 'Header Row',
- name: 'headerRow',
- type: 'boolean',
- displayOptions: {
- show: {
- '/operation': ['fromFile', 'toFile'],
- },
- },
- default: true,
- description: 'Whether the first row of the file contains the header names',
- },
- {
- displayName: 'Delimiter',
- name: 'delimiter',
- type: 'string',
- displayOptions: {
- show: {
- '/operation': ['fromFile'],
- '/fileFormat': ['csv'],
- },
- },
- default: ',',
- description: 'Set the field delimiter',
- },
- {
- displayName: 'Starting Line',
- name: 'fromLine',
- type: 'number',
- displayOptions: {
- show: {
- '/operation': ['fromFile'],
- '/fileFormat': ['csv'],
- },
- },
- default: 0,
- description: 'Start handling records from the requested line number',
- },
- {
- displayName: 'Max Number of Rows to Load',
- name: 'maxRowCount',
- type: 'number',
- displayOptions: {
- show: {
- '/operation': ['fromFile'],
- '/fileFormat': ['csv'],
- },
- },
- default: -1,
- description: 'Stop handling records after the requested number of rows are read',
- },
- {
- displayName: 'Exclude Byte Order Mark (BOM)',
- name: 'enableBOM',
- type: 'boolean',
- displayOptions: {
- show: {
- '/operation': ['fromFile'],
- '/fileFormat': ['csv'],
- },
- },
- default: false,
- description:
- 'Whether to detect and exclude the byte-order-mark from the CSV Input if present',
- },
- {
- displayName: 'Include Empty Cells',
- name: 'includeEmptyCells',
- type: 'boolean',
- displayOptions: {
- show: {
- '/operation': ['fromFile'],
- },
+export const toFileOptions: INodeProperties = {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ displayOptions: {
+ show: {
+ operation: ['toFile'],
+ },
+ },
+ options: [
+ {
+ displayName: 'Compression',
+ name: 'compression',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ '/fileFormat': ['xlsx', 'ods'],
},
- default: false,
- // eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
- description:
- 'When reading from file the empty cells will be filled with an empty string in the JSON',
},
- {
- displayName: 'RAW Data',
- name: 'rawData',
- type: 'boolean',
- displayOptions: {
- show: {
- '/operation': ['fromFile'],
- },
+ default: false,
+ description: 'Whether compression will be applied or not',
+ },
+ {
+ displayName: 'File Name',
+ name: 'fileName',
+ type: 'string',
+ default: '',
+ description:
+ 'File name to set in binary data. By default will "spreadsheet.<fileFormat>" be used.',
+ },
+ {
+ displayName: 'Header Row',
+ name: 'headerRow',
+ type: 'boolean',
+ default: true,
+ description: 'Whether the first row of the file contains the header names',
+ },
+ {
+ displayName: 'Sheet Name',
+ name: 'sheetName',
+ type: 'string',
+ displayOptions: {
+ show: {
+ '/fileFormat': ['ods', 'xls', 'xlsx'],
},
- default: false,
- description: 'Whether the data should be returned RAW instead of parsed',
},
- {
- displayName: 'Read As String',
- name: 'readAsString',
- type: 'boolean',
- displayOptions: {
- show: {
- '/operation': ['fromFile'],
- },
+ default: 'Sheet',
+ description: 'Name of the sheet to create in the spreadsheet',
+ },
+ ],
+};
+
+export const fromFileOptions: INodeProperties = {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ displayOptions: {
+ show: {
+ operation: ['fromFile'],
+ },
+ },
+ options: [
+ {
+ displayName: 'Delimiter',
+ name: 'delimiter',
+ type: 'string',
+ displayOptions: {
+ show: {
+ '/fileFormat': ['csv'],
},
- default: false,
- // eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
- description:
- 'In some cases and file formats, it is necessary to read specifically as string else some special character get interpreted wrong',
},
- {
- displayName: 'Range',
- name: 'range',
- type: 'string',
- displayOptions: {
- show: {
- '/operation': ['fromFile'],
- },
+ default: ',',
+ placeholder: 'e.g. ,',
+ description: 'Set the field delimiter, usually a comma',
+ },
+ {
+ displayName: 'Exclude Byte Order Mark (BOM)',
+ name: 'enableBOM',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ '/fileFormat': ['csv'],
},
- default: '',
- description:
- 'The range to read from the table. If set to a number it will be the starting row. If set to string it will be used as A1-style bounded range.',
},
- {
- displayName: 'Sheet Name',
- name: 'sheetName',
- type: 'string',
- displayOptions: {
- show: {
- '/operation': ['fromFile'],
- },
+ default: false,
+ description:
+ 'Whether to detect and exclude the byte-order-mark from the CSV Input if present',
+ },
+ {
+ displayName: 'Header Row',
+ name: 'headerRow',
+ type: 'boolean',
+ default: true,
+ description: 'Whether the first row of the file contains the header names',
+ },
+ {
+ displayName: 'Include Empty Cells',
+ name: 'includeEmptyCells',
+ type: 'boolean',
+ default: false,
+ description:
+ 'Whether to include empty cells when reading from file. They will be filled with an empty string.',
+ },
+ {
+ displayName: 'Max Number of Rows to Load',
+ name: 'maxRowCount',
+ type: 'number',
+ displayOptions: {
+ show: {
+ '/fileFormat': ['csv'],
},
- default: 'Sheet',
- description:
- 'Name of the sheet to read from in the spreadsheet (if supported). If not set, the first one gets chosen.',
},
- {
- displayName: 'Sheet Name',
- name: 'sheetName',
- type: 'string',
- displayOptions: {
- show: {
- '/operation': ['toFile'],
- '/fileFormat': ['ods', 'xls', 'xlsx'],
- },
+ default: -1,
+ placeholder: 'e.g. 10',
+ description:
+ 'Stop handling records after the requested number of rows are read. Use -1 if you want to load all rows.',
+ },
+ {
+ displayName: 'Range',
+ name: 'range',
+ type: 'string',
+ default: '',
+ description:
+ 'The range to read from the table. If set to a number it will be the starting row. If set to string it will be used as A1-style notation range.',
+ },
+ {
+ displayName: 'RAW Data',
+ name: 'rawData',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return RAW data, instead of parsing it',
+ },
+ {
+ displayName: 'Read As String',
+ name: 'readAsString',
+ type: 'boolean',
+ default: false,
+ // eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
+ description:
+ 'In some cases and file formats, it is necessary to read as string to ensure special characters are interpreted correctly',
+ },
+ {
+ displayName: 'Sheet Name',
+ name: 'sheetName',
+ type: 'string',
+ default: 'Sheet',
+ placeholder: 'e.g. mySheet',
+ description:
+ 'Name of the sheet to read from in the spreadsheet (if supported). If not set, the first one will be chosen.',
+ },
+ {
+ displayName: 'Starting Line',
+ name: 'fromLine',
+ type: 'number',
+ displayOptions: {
+ show: {
+ '/fileFormat': ['csv'],
},
- default: 'Sheet',
- description: 'Name of the sheet to create in the spreadsheet',
},
- ],
- },
-];
+ default: 0,
+ placeholder: 'e.g. 0',
+ description: 'Start handling records from the requested line number. Starts at 0.',
+ },
+ ],
+};
diff --git a/packages/nodes-base/nodes/SpreadsheetFile/v1/SpreadsheetFileV1.node.ts b/packages/nodes-base/nodes/SpreadsheetFile/v1/SpreadsheetFileV1.node.ts
index fa375859f7381..3a57d58c23f7e 100644
--- a/packages/nodes-base/nodes/SpreadsheetFile/v1/SpreadsheetFileV1.node.ts
+++ b/packages/nodes-base/nodes/SpreadsheetFile/v1/SpreadsheetFileV1.node.ts
@@ -23,10 +23,11 @@ import {
} from 'xlsx';
import {
- operationProperties,
- fromFileProperties,
+ operationProperty,
+ binaryProperty,
toFileProperties,
- optionsProperties,
+ fromFileOptions,
+ toFileOptions,
} from '../description';
import { flattenObject, generatePairedItemData } from '@utils/utilities';
import { oldVersionNotice } from '@utils/descriptions';
@@ -46,10 +47,11 @@ export class SpreadsheetFileV1 implements INodeType {
outputs: ['main'],
properties: [
oldVersionNotice,
- ...operationProperties,
- ...fromFileProperties,
+ operationProperty,
+ binaryProperty,
...toFileProperties,
- ...optionsProperties,
+ fromFileOptions,
+ toFileOptions,
],
};
}
diff --git a/packages/nodes-base/nodes/SpreadsheetFile/v2/SpreadsheetFileV2.node.ts b/packages/nodes-base/nodes/SpreadsheetFile/v2/SpreadsheetFileV2.node.ts
index 4c08bd2754c7f..c1a75b69a9828 100644
--- a/packages/nodes-base/nodes/SpreadsheetFile/v2/SpreadsheetFileV2.node.ts
+++ b/packages/nodes-base/nodes/SpreadsheetFile/v2/SpreadsheetFileV2.node.ts
@@ -1,37 +1,14 @@
import type {
- IDataObject,
IExecuteFunctions,
INodeExecutionData,
INodeType,
INodeTypeBaseDescription,
INodeTypeDescription,
} from 'n8n-workflow';
-import { BINARY_ENCODING, NodeOperationError } from 'n8n-workflow';
-import type {
- JSON2SheetOpts,
- Sheet2JSONOpts,
- WorkBook,
- WritingOptions,
- ParsingOptions,
-} from 'xlsx';
-
-import {
- read as xlsxRead,
- readFile as xlsxReadFile,
- utils as xlsxUtils,
- write as xlsxWrite,
-} from 'xlsx';
-import { parse as createCSVParser } from 'csv-parse';
-
-import {
- operationProperties,
- fromFileProperties,
- toFileProperties,
- optionsProperties,
- fromFileV2Properties,
-} from '../description';
-import { flattenObject, generatePairedItemData } from '@utils/utilities';
+import { operationProperty } from '../description';
+import * as fromFile from './fromFile.operation';
+import * as toFile from './toFile.operation';
export class SpreadsheetFileV2 implements INodeType {
description: INodeTypeDescription;
@@ -46,271 +23,23 @@ export class SpreadsheetFileV2 implements INodeType {
},
inputs: ['main'],
outputs: ['main'],
- properties: [
- ...operationProperties,
- ...fromFileProperties,
- ...fromFileV2Properties,
- ...toFileProperties,
- ...optionsProperties,
- ],
+ properties: [operationProperty, ...fromFile.description, ...toFile.description],
};
}
- async execute(this: IExecuteFunctions): Promise {
+ async execute(this: IExecuteFunctions) {
const items = this.getInputData();
-
const operation = this.getNodeParameter('operation', 0);
-
- const newItems: INodeExecutionData[] = [];
+ let returnData: INodeExecutionData[] = [];
if (operation === 'fromFile') {
- // Read data from spreadsheet file to workflow
- for (let i = 0; i < items.length; i++) {
- try {
- const options = this.getNodeParameter('options', i, {});
- let fileFormat = this.getNodeParameter('fileFormat', i, {});
- const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
- const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
-
- let rows: unknown[] = [];
-
- if (
- fileFormat === 'autodetect' &&
- (binaryData.mimeType === 'text/csv' ||
- (binaryData.mimeType === 'text/plain' && binaryData.fileExtension === 'csv'))
- ) {
- fileFormat = 'csv';
- }
-
- if (fileFormat === 'csv') {
- const maxRowCount = options.maxRowCount as number;
- const parser = createCSVParser({
- delimiter: options.delimiter as string,
- fromLine: options.fromLine as number,
- bom: options.enableBOM as boolean,
- to: maxRowCount > -1 ? maxRowCount : undefined,
- columns: options.headerRow !== false,
- onRecord: (record) => {
- if (!options.includeEmptyCells) {
- record = Object.fromEntries(
- Object.entries(record).filter(([_key, value]) => value !== ''),
- );
- }
- rows.push(record);
- },
- });
- if (binaryData.id) {
- const stream = await this.helpers.getBinaryStream(binaryData.id);
- await new Promise(async (resolve, reject) => {
- parser.on('error', reject);
- parser.on('readable', () => {
- stream.unpipe(parser);
- stream.destroy();
- resolve();
- });
- stream.pipe(parser);
- });
- } else {
- parser.write(binaryData.data, BINARY_ENCODING);
- parser.end();
- }
- } else {
- let workbook: WorkBook;
- const xlsxOptions: ParsingOptions = { raw: options.rawData as boolean };
- if (options.readAsString) xlsxOptions.type = 'string';
-
- if (binaryData.id) {
- const binaryPath = this.helpers.getBinaryPath(binaryData.id);
- workbook = xlsxReadFile(binaryPath, xlsxOptions);
- } else {
- const binaryDataBuffer = Buffer.from(binaryData.data, BINARY_ENCODING);
- workbook = xlsxRead(
- options.readAsString ? binaryDataBuffer.toString() : binaryDataBuffer,
- xlsxOptions,
- );
- }
-
- if (workbook.SheetNames.length === 0) {
- throw new NodeOperationError(
- this.getNode(),
- 'Spreadsheet does not have any sheets!',
- {
- itemIndex: i,
- },
- );
- }
-
- let sheetName = workbook.SheetNames[0];
- if (options.sheetName) {
- if (!workbook.SheetNames.includes(options.sheetName as string)) {
- throw new NodeOperationError(
- this.getNode(),
- `Spreadsheet does not contain sheet called "${options.sheetName}"!`,
- { itemIndex: i },
- );
- }
- sheetName = options.sheetName as string;
- }
-
- // Convert it to json
- const sheetToJsonOptions: Sheet2JSONOpts = {};
- if (options.range) {
- if (isNaN(options.range as number)) {
- sheetToJsonOptions.range = options.range;
- } else {
- sheetToJsonOptions.range = parseInt(options.range as string, 10);
- }
- }
-
- if (options.includeEmptyCells) {
- sheetToJsonOptions.defval = '';
- }
-
- if (options.headerRow === false) {
- sheetToJsonOptions.header = 1; // Consider the first row as a data row
- }
-
- rows = xlsxUtils.sheet_to_json(workbook.Sheets[sheetName], sheetToJsonOptions);
-
- // Check if data could be found in file
- if (rows.length === 0) {
- continue;
- }
- }
-
- // Add all the found data columns to the workflow data
- if (options.headerRow === false) {
- // Data was returned as an array - https://github.com/SheetJS/sheetjs#json
- for (const rowData of rows) {
- newItems.push({
- json: {
- row: rowData,
- },
- pairedItem: {
- item: i,
- },
- } as INodeExecutionData);
- }
- } else {
- for (const rowData of rows) {
- newItems.push({
- json: rowData,
- pairedItem: {
- item: i,
- },
- } as INodeExecutionData);
- }
- }
- } catch (error) {
- if (this.continueOnFail()) {
- newItems.push({
- json: {
- error: error.message,
- },
- pairedItem: {
- item: i,
- },
- });
- continue;
- }
- throw new NodeOperationError(this.getNode(), error, { itemIndex: i });
- }
- }
-
- return [newItems];
- } else if (operation === 'toFile') {
- const pairedItem = generatePairedItemData(items.length);
- try {
- // Write the workflow data to spreadsheet file
- const binaryPropertyName = this.getNodeParameter('binaryPropertyName', 0);
- const fileFormat = this.getNodeParameter('fileFormat', 0) as string;
- const options = this.getNodeParameter('options', 0, {});
- const sheetToJsonOptions: JSON2SheetOpts = {};
- if (options.headerRow === false) {
- sheetToJsonOptions.skipHeader = true;
- }
- // Get the json data of the items and flatten it
- let item: INodeExecutionData;
- const itemData: IDataObject[] = [];
- for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
- item = items[itemIndex];
- itemData.push(flattenObject(item.json));
- }
-
- const ws = xlsxUtils.json_to_sheet(itemData, sheetToJsonOptions);
-
- const wopts: WritingOptions = {
- bookSST: false,
- type: 'buffer',
- };
-
- if (fileFormat === 'csv') {
- wopts.bookType = 'csv';
- } else if (fileFormat === 'html') {
- wopts.bookType = 'html';
- } else if (fileFormat === 'rtf') {
- wopts.bookType = 'rtf';
- } else if (fileFormat === 'ods') {
- wopts.bookType = 'ods';
- if (options.compression) {
- wopts.compression = true;
- }
- } else if (fileFormat === 'xls') {
- wopts.bookType = 'xls';
- } else if (fileFormat === 'xlsx') {
- wopts.bookType = 'xlsx';
- if (options.compression) {
- wopts.compression = true;
- }
- }
-
- // Convert the data in the correct format
- const sheetName = (options.sheetName as string) || 'Sheet';
- const wb: WorkBook = {
- SheetNames: [sheetName],
- Sheets: {
- [sheetName]: ws,
- },
- };
- const wbout: Buffer = xlsxWrite(wb, wopts);
-
- // Create a new item with only the binary spreadsheet data
- const newItem: INodeExecutionData = {
- json: {},
- binary: {},
- pairedItem,
- };
-
- let fileName = `spreadsheet.${fileFormat}`;
- if (options.fileName !== undefined) {
- fileName = options.fileName as string;
- }
-
- newItem.binary![binaryPropertyName] = await this.helpers.prepareBinaryData(wbout, fileName);
+ returnData = await fromFile.execute.call(this, items);
+ }
- newItems.push(newItem);
- } catch (error) {
- if (this.continueOnFail()) {
- newItems.push({
- json: {
- error: error.message,
- },
- pairedItem,
- });
- } else {
- throw error;
- }
- }
- } else {
- if (this.continueOnFail()) {
- return [[{ json: { error: `The operation "${operation}" is not supported!` } }]];
- } else {
- throw new NodeOperationError(
- this.getNode(),
- `The operation "${operation}" is not supported!`,
- );
- }
+ if (operation === 'toFile') {
+ returnData = await toFile.execute.call(this, items);
}
- return [newItems];
+
+ return [returnData];
}
}
diff --git a/packages/nodes-base/nodes/SpreadsheetFile/v2/fromFile.operation.ts b/packages/nodes-base/nodes/SpreadsheetFile/v2/fromFile.operation.ts
new file mode 100644
index 0000000000000..ffafaa952ddcb
--- /dev/null
+++ b/packages/nodes-base/nodes/SpreadsheetFile/v2/fromFile.operation.ts
@@ -0,0 +1,230 @@
+import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
+import { BINARY_ENCODING, NodeOperationError } from 'n8n-workflow';
+
+import type { Sheet2JSONOpts, WorkBook, ParsingOptions } from 'xlsx';
+import { read as xlsxRead, readFile as xlsxReadFile, utils as xlsxUtils } from 'xlsx';
+
+import { parse as createCSVParser } from 'csv-parse';
+import { binaryProperty, fromFileOptions } from '../description';
+
+export const description: INodeProperties[] = [
+ binaryProperty,
+ {
+ displayName: 'File Format',
+ name: 'fileFormat',
+ type: 'options',
+ options: [
+ {
+ name: 'Autodetect',
+ value: 'autodetect',
+ },
+ {
+ name: 'CSV',
+ value: 'csv',
+ description: 'Comma-separated values',
+ },
+ {
+ name: 'HTML',
+ value: 'html',
+ description: 'HTML Table',
+ },
+ {
+ name: 'ODS',
+ value: 'ods',
+ description: 'OpenDocument Spreadsheet',
+ },
+ {
+ name: 'RTF',
+ value: 'rtf',
+ description: 'Rich Text Format',
+ },
+ {
+ name: 'XLS',
+ value: 'xls',
+ description: 'Excel',
+ },
+ {
+ name: 'XLSX',
+ value: 'xlsx',
+ description: 'Excel',
+ },
+ ],
+ default: 'autodetect',
+ description: 'The format of the binary data to read from',
+ displayOptions: {
+ show: {
+ operation: ['fromFile'],
+ },
+ },
+ },
+ fromFileOptions,
+];
+
+export async function execute(
+ this: IExecuteFunctions,
+ items: INodeExecutionData[],
+ fileFormatProperty = 'fileFormat',
+) {
+ const returnData: INodeExecutionData[] = [];
+ let fileExtension;
+ let fileFormat;
+
+ for (let i = 0; i < items.length; i++) {
+ try {
+ const options = this.getNodeParameter('options', i, {});
+ fileFormat = this.getNodeParameter(fileFormatProperty, i, '');
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
+ const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
+ fileExtension = binaryData.fileExtension;
+
+ let rows: unknown[] = [];
+
+ if (
+ fileFormat === 'autodetect' &&
+ (binaryData.mimeType === 'text/csv' ||
+ (binaryData.mimeType === 'text/plain' && binaryData.fileExtension === 'csv'))
+ ) {
+ fileFormat = 'csv';
+ }
+
+ if (fileFormat === 'csv') {
+ const maxRowCount = options.maxRowCount as number;
+ const parser = createCSVParser({
+ delimiter: options.delimiter as string,
+ fromLine: options.fromLine as number,
+ bom: options.enableBOM as boolean,
+ to: maxRowCount > -1 ? maxRowCount : undefined,
+ columns: options.headerRow !== false,
+ onRecord: (record) => {
+ if (!options.includeEmptyCells) {
+ record = Object.fromEntries(
+ Object.entries(record).filter(([_key, value]) => value !== ''),
+ );
+ }
+ rows.push(record);
+ },
+ });
+ if (binaryData.id) {
+ const stream = await this.helpers.getBinaryStream(binaryData.id);
+ await new Promise(async (resolve, reject) => {
+ parser.on('error', reject);
+ parser.on('readable', () => {
+ stream.unpipe(parser);
+ stream.destroy();
+ resolve();
+ });
+ stream.pipe(parser);
+ });
+ } else {
+ parser.write(binaryData.data, BINARY_ENCODING);
+ parser.end();
+ }
+ } else {
+ let workbook: WorkBook;
+ const xlsxOptions: ParsingOptions = { raw: options.rawData as boolean };
+ if (options.readAsString) xlsxOptions.type = 'string';
+
+ if (binaryData.id) {
+ const binaryPath = this.helpers.getBinaryPath(binaryData.id);
+ workbook = xlsxReadFile(binaryPath, xlsxOptions);
+ } else {
+ const binaryDataBuffer = Buffer.from(binaryData.data, BINARY_ENCODING);
+ workbook = xlsxRead(
+ options.readAsString ? binaryDataBuffer.toString() : binaryDataBuffer,
+ xlsxOptions,
+ );
+ }
+
+ if (workbook.SheetNames.length === 0) {
+ throw new NodeOperationError(this.getNode(), 'Spreadsheet does not have any sheets!', {
+ itemIndex: i,
+ });
+ }
+
+ let sheetName = workbook.SheetNames[0];
+ if (options.sheetName) {
+ if (!workbook.SheetNames.includes(options.sheetName as string)) {
+ throw new NodeOperationError(
+ this.getNode(),
+ `Spreadsheet does not contain sheet called "${options.sheetName}"!`,
+ { itemIndex: i },
+ );
+ }
+ sheetName = options.sheetName as string;
+ }
+
+ // Convert it to json
+ const sheetToJsonOptions: Sheet2JSONOpts = {};
+ if (options.range) {
+ if (isNaN(options.range as number)) {
+ sheetToJsonOptions.range = options.range;
+ } else {
+ sheetToJsonOptions.range = parseInt(options.range as string, 10);
+ }
+ }
+
+ if (options.includeEmptyCells) {
+ sheetToJsonOptions.defval = '';
+ }
+
+ if (options.headerRow === false) {
+ sheetToJsonOptions.header = 1; // Consider the first row as a data row
+ }
+
+ rows = xlsxUtils.sheet_to_json(workbook.Sheets[sheetName], sheetToJsonOptions);
+
+ // Check if data could be found in file
+ if (rows.length === 0) {
+ continue;
+ }
+ }
+
+ // Add all the found data columns to the workflow data
+ if (options.headerRow === false) {
+ // Data was returned as an array - https://github.com/SheetJS/sheetjs#json
+ for (const rowData of rows) {
+ returnData.push({
+ json: {
+ row: rowData,
+ },
+ pairedItem: {
+ item: i,
+ },
+ } as INodeExecutionData);
+ }
+ } else {
+ for (const rowData of rows) {
+ returnData.push({
+ json: rowData,
+ pairedItem: {
+ item: i,
+ },
+ } as INodeExecutionData);
+ }
+ }
+ } catch (error) {
+ let errorDescription = error.description;
+ if (fileExtension && fileExtension !== fileFormat) {
+ error.message = `The file selected in 'Input Binary Field' is not in ${fileFormat} format`;
+ errorDescription = `Try to change the operation or select a ${fileFormat} file in 'Input Binary Field'`;
+ }
+ if (this.continueOnFail()) {
+ returnData.push({
+ json: {
+ error: error.message,
+ },
+ pairedItem: {
+ item: i,
+ },
+ });
+ continue;
+ }
+ throw new NodeOperationError(this.getNode(), error, {
+ itemIndex: i,
+ description: errorDescription,
+ });
+ }
+ }
+
+ return returnData;
+}
diff --git a/packages/nodes-base/nodes/SpreadsheetFile/v2/toFile.operation.ts b/packages/nodes-base/nodes/SpreadsheetFile/v2/toFile.operation.ts
new file mode 100644
index 0000000000000..b59d1568f2ce4
--- /dev/null
+++ b/packages/nodes-base/nodes/SpreadsheetFile/v2/toFile.operation.ts
@@ -0,0 +1,44 @@
+import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
+
+import { generatePairedItemData } from '@utils/utilities';
+import type { JsonToSpreadsheetBinaryFormat, JsonToSpreadsheetBinaryOptions } from '@utils/binary';
+import { convertJsonToSpreadsheetBinary } from '@utils/binary';
+import { toFileOptions, toFileProperties } from '../description';
+
+export const description: INodeProperties[] = [...toFileProperties, toFileOptions];
+
+export async function execute(this: IExecuteFunctions, items: INodeExecutionData[]) {
+ const returnData: INodeExecutionData[] = [];
+
+ const pairedItem = generatePairedItemData(items.length);
+
+ try {
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', 0);
+ const fileFormat = this.getNodeParameter('fileFormat', 0) as JsonToSpreadsheetBinaryFormat;
+ const options = this.getNodeParameter('options', 0, {}) as JsonToSpreadsheetBinaryOptions;
+
+ const binaryData = await convertJsonToSpreadsheetBinary.call(this, items, fileFormat, options);
+
+ const newItem: INodeExecutionData = {
+ json: {},
+ binary: {
+ [binaryPropertyName]: binaryData,
+ },
+ pairedItem,
+ };
+
+ returnData.push(newItem);
+ } catch (error) {
+ if (this.continueOnFail()) {
+ returnData.push({
+ json: {
+ error: error.message,
+ },
+ pairedItem,
+ });
+ } else {
+ throw error;
+ }
+ }
+ return returnData;
+}
diff --git a/packages/nodes-base/nodes/Ssh/Ssh.node.ts b/packages/nodes-base/nodes/Ssh/Ssh.node.ts
index ebb30e3bcf6ae..7625289797609 100644
--- a/packages/nodes-base/nodes/Ssh/Ssh.node.ts
+++ b/packages/nodes-base/nodes/Ssh/Ssh.node.ts
@@ -191,7 +191,7 @@ export class Ssh implements INodeType {
default: 'upload',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -203,8 +203,7 @@ export class Ssh implements INodeType {
},
},
placeholder: '',
- description:
- 'Name of the binary property which contains the data for the file to be uploaded',
+ hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'Target Directory',
@@ -239,7 +238,7 @@ export class Ssh implements INodeType {
required: true,
},
{
- displayName: 'Binary Property',
+ displayName: 'File Property',
displayOptions: {
show: {
resource: ['file'],
diff --git a/packages/nodes-base/nodes/Telegram/Telegram.node.ts b/packages/nodes-base/nodes/Telegram/Telegram.node.ts
index 3b1f1b3082061..cf721c1adc7cb 100644
--- a/packages/nodes-base/nodes/Telegram/Telegram.node.ts
+++ b/packages/nodes-base/nodes/Telegram/Telegram.node.ts
@@ -688,7 +688,7 @@ export class Telegram implements INodeType {
// ----------------------------------
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
default: false,
@@ -709,11 +709,12 @@ export class Telegram implements INodeType {
description: 'Whether the data to upload should be taken from binary field',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
+ hint: 'The name of the input binary field containing the file to be written',
displayOptions: {
show: {
operation: [
diff --git a/packages/nodes-base/nodes/TheHive/descriptions/AlertDescription.ts b/packages/nodes-base/nodes/TheHive/descriptions/AlertDescription.ts
index 835d47bd563e9..2522094645a76 100644
--- a/packages/nodes-base/nodes/TheHive/descriptions/AlertDescription.ts
+++ b/packages/nodes-base/nodes/TheHive/descriptions/AlertDescription.ts
@@ -339,9 +339,10 @@ export const alertFields: INodeProperties[] = [
default: '',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryProperty',
type: 'string',
+ hint: 'The name of the input binary field containing the file to be written',
displayOptions: {
show: {
dataType: ['file'],
@@ -553,9 +554,10 @@ export const alertFields: INodeProperties[] = [
default: '',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryProperty',
type: 'string',
+ hint: 'The name of the input binary field containing the file to be written',
displayOptions: {
show: {
dataType: ['file'],
diff --git a/packages/nodes-base/nodes/TheHive/descriptions/LogDescription.ts b/packages/nodes-base/nodes/TheHive/descriptions/LogDescription.ts
index 8f8f8ed16b59d..834201ed65e63 100644
--- a/packages/nodes-base/nodes/TheHive/descriptions/LogDescription.ts
+++ b/packages/nodes-base/nodes/TheHive/descriptions/LogDescription.ts
@@ -206,11 +206,11 @@ export const logFields: INodeProperties[] = [
name: 'attachmentValues',
values: [
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryProperty',
type: 'string',
default: 'data',
- description: 'Object property name which holds binary data',
+ description: 'The name of the input binary field which holds binary data',
},
],
},
diff --git a/packages/nodes-base/nodes/TheHive/descriptions/ObservableDescription.ts b/packages/nodes-base/nodes/TheHive/descriptions/ObservableDescription.ts
index d4d9a82b2267b..747d6102222a0 100644
--- a/packages/nodes-base/nodes/TheHive/descriptions/ObservableDescription.ts
+++ b/packages/nodes-base/nodes/TheHive/descriptions/ObservableDescription.ts
@@ -120,12 +120,12 @@ export const observableFields: INodeProperties[] = [
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryProperty',
type: 'string',
required: true,
default: 'data',
- description: 'Binary Property that represent the attachment file',
+ description: 'The name of the input binary field that represent the attachment file',
displayOptions: {
show: {
resource: ['observable'],
diff --git a/packages/nodes-base/nodes/TheHiveProject/actions/alert/create.operation.ts b/packages/nodes-base/nodes/TheHiveProject/actions/alert/create.operation.ts
index f601184c135d3..e7a6fc6f9e955 100644
--- a/packages/nodes-base/nodes/TheHiveProject/actions/alert/create.operation.ts
+++ b/packages/nodes-base/nodes/TheHiveProject/actions/alert/create.operation.ts
@@ -59,9 +59,10 @@ const properties: INodeProperties[] = [
default: '',
},
{
- displayName: 'Binary Property',
+ displayName: 'Input Binary Field',
name: 'binaryProperty',
type: 'string',
+ hint: 'The name of the input binary field containing the file to be written',
displayOptions: {
show: {
dataType: ['file'],
diff --git a/packages/nodes-base/nodes/Vonage/Vonage.node.ts b/packages/nodes-base/nodes/Vonage/Vonage.node.ts
index 162b982c3aec2..b6944f4f1a2f7 100644
--- a/packages/nodes-base/nodes/Vonage/Vonage.node.ts
+++ b/packages/nodes-base/nodes/Vonage/Vonage.node.ts
@@ -133,7 +133,7 @@ export class Vonage implements INodeType {
// description: 'The format of the message body',
// },
// {
- // displayName: 'Binary Property',
+ // displayName: 'Input Binary Field',
// name: 'binaryPropertyName',
// displayOptions: {
// show: {
diff --git a/packages/nodes-base/nodes/Webhook/description.ts b/packages/nodes-base/nodes/Webhook/description.ts
index 4e3822c70ceb4..2f4cc3fdeb542 100644
--- a/packages/nodes-base/nodes/Webhook/description.ts
+++ b/packages/nodes-base/nodes/Webhook/description.ts
@@ -196,7 +196,7 @@ export const optionsProperty: INodeProperties = {
default: {},
options: [
{
- displayName: 'Binary Data',
+ displayName: 'Binary File',
name: 'binaryData',
type: 'boolean',
displayOptions: {
@@ -209,7 +209,7 @@ export const optionsProperty: INodeProperties = {
description: 'Whether the webhook will receive binary data',
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
@@ -219,8 +219,9 @@ export const optionsProperty: INodeProperties = {
'@version': [1],
},
},
+ hint: 'The name of the output binary field to put the file in',
description:
- 'Name of the binary property to write the data of the received file to. If the data gets received via "Form-Data Multipart" it will be the prefix and a number starting with 0 will be attached to it.',
+ 'If the data gets received via "Form-Data Multipart" it will be the prefix and a number starting with 0 will be attached to it',
},
{
displayName: 'Binary Property',
diff --git a/packages/nodes-base/nodes/Wise/descriptions/AccountDescription.ts b/packages/nodes-base/nodes/Wise/descriptions/AccountDescription.ts
index 00101da4b3985..3f149307b8352 100644
--- a/packages/nodes-base/nodes/Wise/descriptions/AccountDescription.ts
+++ b/packages/nodes-base/nodes/Wise/descriptions/AccountDescription.ts
@@ -139,12 +139,12 @@ export const accountFields: INodeProperties[] = [
],
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryProperty',
type: 'string',
required: true,
default: 'data',
- description: 'Name of the binary property to which to write to',
+ hint: 'The name of the output binary field to put the file in',
displayOptions: {
show: {
resource: ['account'],
diff --git a/packages/nodes-base/nodes/Wise/descriptions/TransferDescription.ts b/packages/nodes-base/nodes/Wise/descriptions/TransferDescription.ts
index 5d9f0a2d95153..5f743cc997f78 100644
--- a/packages/nodes-base/nodes/Wise/descriptions/TransferDescription.ts
+++ b/packages/nodes-base/nodes/Wise/descriptions/TransferDescription.ts
@@ -207,12 +207,12 @@ export const transferFields: INodeProperties[] = [
},
},
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'binaryProperty',
type: 'string',
required: true,
default: 'data',
- description: 'Name of the binary property to which to write to',
+ hint: 'The name of the output binary field to put the file in',
displayOptions: {
show: {
resource: ['transfer'],
diff --git a/packages/nodes-base/nodes/WriteBinaryFile/WriteBinaryFile.node.ts b/packages/nodes-base/nodes/WriteBinaryFile/WriteBinaryFile.node.ts
index fb5ea7b1e37d7..0938a31488a55 100644
--- a/packages/nodes-base/nodes/WriteBinaryFile/WriteBinaryFile.node.ts
+++ b/packages/nodes-base/nodes/WriteBinaryFile/WriteBinaryFile.node.ts
@@ -10,6 +10,7 @@ import type {
export class WriteBinaryFile implements INodeType {
description: INodeTypeDescription = {
+ hidden: true,
displayName: 'Write Binary File',
name: 'writeBinaryFile',
icon: 'fa:file-export',
diff --git a/packages/nodes-base/nodes/Xml/Xml.node.json b/packages/nodes-base/nodes/Xml/Xml.node.json
index 0291e03833d9b..09fc208546187 100644
--- a/packages/nodes-base/nodes/Xml/Xml.node.json
+++ b/packages/nodes-base/nodes/Xml/Xml.node.json
@@ -19,6 +19,6 @@
},
"alias": ["Parse"],
"subcategories": {
- "Core Nodes": ["Files", "Data Transformation"]
+ "Core Nodes": ["Data Transformation"]
}
}
diff --git a/packages/nodes-base/nodes/Xml/Xml.node.ts b/packages/nodes-base/nodes/Xml/Xml.node.ts
index eca66e0b06d9e..d80d97f9f5056 100644
--- a/packages/nodes-base/nodes/Xml/Xml.node.ts
+++ b/packages/nodes-base/nodes/Xml/Xml.node.ts
@@ -42,6 +42,18 @@ export class Xml implements INodeType {
default: 'xmlToJson',
description: 'From and to what format the data should be converted',
},
+ {
+ displayName:
+ "If your XML is inside a binary file, use the 'Extract From File' node to convert it to text first",
+ name: 'xmlNotice',
+ type: 'notice',
+ default: '',
+ displayOptions: {
+ show: {
+ mode: ['xmlToJson'],
+ },
+ },
+ },
// ----------------------------------
// option:jsonToxml
diff --git a/packages/nodes-base/nodes/Zulip/MessageDescription.ts b/packages/nodes-base/nodes/Zulip/MessageDescription.ts
index 786cf7c7450e8..c8f0efc3393d0 100644
--- a/packages/nodes-base/nodes/Zulip/MessageDescription.ts
+++ b/packages/nodes-base/nodes/Zulip/MessageDescription.ts
@@ -249,7 +249,7 @@ export const messageFields: INodeProperties[] = [
/* message:updateFile */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Binary Property',
+ displayName: 'Put Output File in Field',
name: 'dataBinaryProperty',
type: 'string',
required: true,
@@ -260,6 +260,6 @@ export const messageFields: INodeProperties[] = [
operation: ['updateFile'],
},
},
- description: 'Name of the binary property to which to write the data of the read file',
+ hint: 'The name of the output binary field to put the file in',
},
];
diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json
index 0781170e50cfa..ac6c02c44ac08 100644
--- a/packages/nodes-base/package.json
+++ b/packages/nodes-base/package.json
@@ -492,6 +492,9 @@
"dist/nodes/FacebookLeadAds/FacebookLeadAdsTrigger.node.js",
"dist/nodes/Figma/FigmaTrigger.node.js",
"dist/nodes/FileMaker/FileMaker.node.js",
+ "dist/nodes/Files/ReadWriteFile/ReadWriteFile.node.js",
+ "dist/nodes/Files/ConvertToFile/ConvertToFile.node.js",
+ "dist/nodes/Files/ExtractFromFile/ExtractFromFile.node.js",
"dist/nodes/Filter/Filter.node.js",
"dist/nodes/Flow/Flow.node.js",
"dist/nodes/Flow/FlowTrigger.node.js",
@@ -885,6 +888,7 @@
"snowflake-sdk": "1.9.2",
"ssh2-sftp-client": "7.2.3",
"tmp-promise": "3.0.3",
+ "ts-ics": "^1.2.2",
"typedi": "0.10.0",
"uuid": "8.3.2",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz",
diff --git a/packages/nodes-base/utils/binary.ts b/packages/nodes-base/utils/binary.ts
new file mode 100644
index 0000000000000..825a0ca08ec33
--- /dev/null
+++ b/packages/nodes-base/utils/binary.ts
@@ -0,0 +1,194 @@
+import type { IBinaryData, IDataObject, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
+import { NodeOperationError, BINARY_ENCODING } from 'n8n-workflow';
+import type { WorkBook, WritingOptions } from 'xlsx';
+import { utils as xlsxUtils, write as xlsxWrite } from 'xlsx';
+import { flattenObject } from '@utils/utilities';
+
+import get from 'lodash/get';
+import iconv from 'iconv-lite';
+
+import { getDocument as readPDF, version as pdfJsVersion } from 'pdfjs-dist';
+
+export type JsonToSpreadsheetBinaryFormat = 'csv' | 'html' | 'rtf' | 'ods' | 'xls' | 'xlsx';
+
+export type JsonToSpreadsheetBinaryOptions = {
+ headerRow?: boolean;
+ compression?: boolean;
+ fileName?: string;
+ sheetName?: string;
+};
+
+export type JsonToBinaryOptions = {
+ fileName?: string;
+ sourceKey?: string;
+ encoding?: string;
+ addBOM?: boolean;
+ mimeType?: string;
+ dataIsBase64?: boolean;
+ itemIndex?: number;
+};
+
+type PdfDocument = Awaited>['promise']>;
+type PdfPage = Awaited>>;
+type PdfTextContent = Awaited>;
+
+export async function convertJsonToSpreadsheetBinary(
+ this: IExecuteFunctions,
+ items: INodeExecutionData[],
+ fileFormat: JsonToSpreadsheetBinaryFormat,
+ options: JsonToSpreadsheetBinaryOptions,
+ defaultFileName = 'spreadsheet',
+): Promise {
+ const itemData: IDataObject[] = [];
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
+ itemData.push(flattenObject(items[itemIndex].json));
+ }
+
+ let sheetToJsonOptions;
+ if (options.headerRow === false) {
+ sheetToJsonOptions = { skipHeader: true };
+ }
+
+ const sheet = xlsxUtils.json_to_sheet(itemData, sheetToJsonOptions);
+
+ const writingOptions: WritingOptions = {
+ bookType: fileFormat,
+ bookSST: false,
+ type: 'buffer',
+ };
+
+ if (['xlsx', 'ods'].includes(fileFormat) && options.compression) {
+ writingOptions.compression = true;
+ }
+
+ // Convert the data in the correct format
+ const sheetName = (options.sheetName as string) || 'Sheet';
+ const workbook: WorkBook = {
+ SheetNames: [sheetName],
+ Sheets: {
+ [sheetName]: sheet,
+ },
+ };
+
+ const buffer: Buffer = xlsxWrite(workbook, writingOptions);
+ const fileName =
+ options.fileName !== undefined ? options.fileName : `${defaultFileName}.${fileFormat}`;
+ const binaryData = await this.helpers.prepareBinaryData(buffer, fileName);
+
+ return binaryData;
+}
+
+export async function createBinaryFromJson(
+ this: IExecuteFunctions,
+ data: IDataObject | IDataObject[],
+ options: JsonToBinaryOptions,
+): Promise {
+ let value;
+ if (options.sourceKey) {
+ value = get(data, options.sourceKey) as IDataObject;
+ } else {
+ value = data;
+ }
+
+ if (value === undefined) {
+ throw new NodeOperationError(this.getNode(), `The value in "${options.sourceKey}" is not set`, {
+ itemIndex: options.itemIndex || 0,
+ });
+ }
+
+ let buffer: Buffer;
+ if (!options.dataIsBase64) {
+ let valueAsString = value as unknown as string;
+
+ if (typeof value === 'object') {
+ options.mimeType = 'application/json';
+ valueAsString = JSON.stringify(value);
+ }
+
+ buffer = iconv.encode(valueAsString, options.encoding || 'utf8', {
+ addBOM: options.addBOM,
+ });
+ } else {
+ buffer = Buffer.from(value as unknown as string, BINARY_ENCODING);
+ }
+
+ const binaryData = await this.helpers.prepareBinaryData(
+ buffer,
+ options.fileName,
+ options.mimeType,
+ );
+
+ if (!binaryData.fileName) {
+ const fileExtension = binaryData.fileExtension ? `.${binaryData.fileExtension}` : '';
+ binaryData.fileName = `file${fileExtension}`;
+ }
+
+ return binaryData;
+}
+
+const parseText = (textContent: PdfTextContent) => {
+ let lastY = undefined;
+ const text = [];
+ for (const item of textContent.items) {
+ if ('str' in item) {
+ if (lastY == item.transform[5] || !lastY) {
+ text.push(item.str);
+ } else {
+ text.push(`\n${item.str}`);
+ }
+ lastY = item.transform[5];
+ }
+ }
+ return text.join('');
+};
+
+export async function extractDataFromPDF(
+ this: IExecuteFunctions,
+ binaryPropertyName: string,
+ password?: string,
+ maxPages?: number,
+ joinPages = true,
+ itemIndex = 0,
+) {
+ const binaryData = this.helpers.assertBinaryData(itemIndex, binaryPropertyName);
+
+ const params: { password?: string; url?: URL; data?: ArrayBuffer } = { password };
+
+ if (binaryData.id) {
+ const binaryPath = this.helpers.getBinaryPath(binaryData.id);
+ params.url = new URL(`file://${binaryPath}`);
+ } else {
+ params.data = Buffer.from(binaryData.data, BINARY_ENCODING).buffer;
+ }
+
+ const document = await readPDF(params).promise;
+ const { info, metadata } = await document
+ .getMetadata()
+ .catch(() => ({ info: null, metadata: null }));
+
+ const pages = [];
+ if (maxPages !== 0) {
+ let pagesToRead = document.numPages;
+ if (maxPages && maxPages < document.numPages) {
+ pagesToRead = maxPages;
+ }
+ for (let i = 1; i <= pagesToRead; i++) {
+ const page = await document.getPage(i);
+ const text = await page.getTextContent().then(parseText);
+ pages.push(text);
+ }
+ }
+
+ const text = joinPages ? pages.join('\n\n') : pages;
+
+ const returnData = {
+ numpages: document.numPages,
+ numrender: document.numPages,
+ info,
+ metadata: metadata?.getAll(),
+ text,
+ version: pdfJsVersion,
+ };
+
+ return returnData;
+}
diff --git a/packages/nodes-base/utils/descriptions.ts b/packages/nodes-base/utils/descriptions.ts
index 614858a28d0b0..1465d3e31476c 100644
--- a/packages/nodes-base/utils/descriptions.ts
+++ b/packages/nodes-base/utils/descriptions.ts
@@ -1,4 +1,4 @@
-import type { INodeProperties } from 'n8n-workflow';
+import type { INodeProperties, INodePropertyOptions } from 'n8n-workflow';
export const oldVersionNotice: INodeProperties = {
displayName:
@@ -32,3 +32,414 @@ export const returnAllOrLimit: INodeProperties[] = [
description: 'Max number of results to return',
},
];
+
+export const encodeDecodeOptions: INodePropertyOptions[] = [
+ {
+ name: 'armscii8',
+ value: 'armscii8',
+ },
+ {
+ name: 'ascii',
+ value: 'ascii',
+ },
+ {
+ name: 'base64',
+ value: 'base64',
+ },
+ {
+ name: 'big5hkscs',
+ value: 'big5hkscs',
+ },
+ {
+ name: 'binary',
+ value: 'binary',
+ },
+ {
+ name: 'cesu8',
+ value: 'cesu8',
+ },
+ {
+ name: 'cp1046',
+ value: 'cp1046',
+ },
+ {
+ name: 'cp1124',
+ value: 'cp1124',
+ },
+ {
+ name: 'cp1125',
+ value: 'cp1125',
+ },
+ {
+ name: 'cp1129',
+ value: 'cp1129',
+ },
+ {
+ name: 'cp1133',
+ value: 'cp1133',
+ },
+ {
+ name: 'cp1161',
+ value: 'cp1161',
+ },
+ {
+ name: 'cp1162',
+ value: 'cp1162',
+ },
+ {
+ name: 'cp1163',
+ value: 'cp1163',
+ },
+ {
+ name: 'cp437',
+ value: 'cp437',
+ },
+ {
+ name: 'cp720',
+ value: 'cp720',
+ },
+ {
+ name: 'cp737',
+ value: 'cp737',
+ },
+ {
+ name: 'cp775',
+ value: 'cp775',
+ },
+ {
+ name: 'cp808',
+ value: 'cp808',
+ },
+ {
+ name: 'cp850',
+ value: 'cp850',
+ },
+ {
+ name: 'cp852',
+ value: 'cp852',
+ },
+ {
+ name: 'cp855',
+ value: 'cp855',
+ },
+ {
+ name: 'cp856',
+ value: 'cp856',
+ },
+ {
+ name: 'cp857',
+ value: 'cp857',
+ },
+ {
+ name: 'cp858',
+ value: 'cp858',
+ },
+ {
+ name: 'cp860',
+ value: 'cp860',
+ },
+ {
+ name: 'cp861',
+ value: 'cp861',
+ },
+ {
+ name: 'cp862',
+ value: 'cp862',
+ },
+ {
+ name: 'cp863',
+ value: 'cp863',
+ },
+ {
+ name: 'cp864',
+ value: 'cp864',
+ },
+ {
+ name: 'cp865',
+ value: 'cp865',
+ },
+ {
+ name: 'cp866',
+ value: 'cp866',
+ },
+ {
+ name: 'cp869',
+ value: 'cp869',
+ },
+ {
+ name: 'cp922',
+ value: 'cp922',
+ },
+ {
+ name: 'cp936',
+ value: 'cp936',
+ },
+ {
+ name: 'cp949',
+ value: 'cp949',
+ },
+ {
+ name: 'cp950',
+ value: 'cp950',
+ },
+ {
+ name: 'eucjp',
+ value: 'eucjp',
+ },
+ {
+ name: 'gb18030',
+ value: 'gb18030',
+ },
+ {
+ name: 'gbk',
+ value: 'gbk',
+ },
+ {
+ name: 'georgianacademy',
+ value: 'georgianacademy',
+ },
+ {
+ name: 'georgianps',
+ value: 'georgianps',
+ },
+ {
+ name: 'hex',
+ value: 'hex',
+ },
+ {
+ name: 'hproman8',
+ value: 'hproman8',
+ },
+ {
+ name: 'iso646cn',
+ value: 'iso646cn',
+ },
+ {
+ name: 'iso646jp',
+ value: 'iso646jp',
+ },
+ {
+ name: 'iso88591',
+ value: 'iso88591',
+ },
+ {
+ name: 'iso885910',
+ value: 'iso885910',
+ },
+ {
+ name: 'iso885911',
+ value: 'iso885911',
+ },
+ {
+ name: 'iso885913',
+ value: 'iso885913',
+ },
+ {
+ name: 'iso885914',
+ value: 'iso885914',
+ },
+ {
+ name: 'iso885915',
+ value: 'iso885915',
+ },
+ {
+ name: 'iso885916',
+ value: 'iso885916',
+ },
+ {
+ name: 'iso88592',
+ value: 'iso88592',
+ },
+ {
+ name: 'iso88593',
+ value: 'iso88593',
+ },
+ {
+ name: 'iso88594',
+ value: 'iso88594',
+ },
+ {
+ name: 'iso88595',
+ value: 'iso88595',
+ },
+ {
+ name: 'iso88596',
+ value: 'iso88596',
+ },
+ {
+ name: 'iso88597',
+ value: 'iso88597',
+ },
+ {
+ name: 'iso88598',
+ value: 'iso88598',
+ },
+ {
+ name: 'iso88599',
+ value: 'iso88599',
+ },
+ {
+ name: 'koi8r',
+ value: 'koi8r',
+ },
+ {
+ name: 'koi8ru',
+ value: 'koi8ru',
+ },
+ {
+ name: 'koi8t',
+ value: 'koi8t',
+ },
+ {
+ name: 'koi8u',
+ value: 'koi8u',
+ },
+ {
+ name: 'maccenteuro',
+ value: 'maccenteuro',
+ },
+ {
+ name: 'maccroatian',
+ value: 'maccroatian',
+ },
+ {
+ name: 'maccyrillic',
+ value: 'maccyrillic',
+ },
+ {
+ name: 'macgreek',
+ value: 'macgreek',
+ },
+ {
+ name: 'maciceland',
+ value: 'maciceland',
+ },
+ {
+ name: 'macintosh',
+ value: 'macintosh',
+ },
+ {
+ name: 'macroman',
+ value: 'macroman',
+ },
+ {
+ name: 'macromania',
+ value: 'macromania',
+ },
+ {
+ name: 'macthai',
+ value: 'macthai',
+ },
+ {
+ name: 'macturkish',
+ value: 'macturkish',
+ },
+ {
+ name: 'macukraine',
+ value: 'macukraine',
+ },
+ {
+ name: 'mik',
+ value: 'mik',
+ },
+ {
+ name: 'pt154',
+ value: 'pt154',
+ },
+ {
+ name: 'rk1048',
+ value: 'rk1048',
+ },
+ {
+ name: 'shiftjis',
+ value: 'shiftjis',
+ },
+ {
+ name: 'tcvn',
+ value: 'tcvn',
+ },
+ {
+ name: 'tis620',
+ value: 'tis620',
+ },
+ {
+ name: 'ucs2',
+ value: 'ucs2',
+ },
+ {
+ name: 'utf16',
+ value: 'utf16',
+ },
+ {
+ name: 'utf16be',
+ value: 'utf16be',
+ },
+ {
+ name: 'utf32',
+ value: 'utf32',
+ },
+ {
+ name: 'utf32be',
+ value: 'utf32be',
+ },
+ {
+ name: 'utf32le',
+ value: 'utf32le',
+ },
+ {
+ name: 'utf7',
+ value: 'utf7',
+ },
+ {
+ name: 'utf7imap',
+ value: 'utf7imap',
+ },
+ {
+ name: 'utf8',
+ value: 'utf8',
+ },
+ {
+ name: 'viscii',
+ value: 'viscii',
+ },
+ {
+ name: 'windows1250',
+ value: 'windows1250',
+ },
+ {
+ name: 'windows1251',
+ value: 'windows1251',
+ },
+ {
+ name: 'windows1252',
+ value: 'windows1252',
+ },
+ {
+ name: 'windows1253',
+ value: 'windows1253',
+ },
+ {
+ name: 'windows1254',
+ value: 'windows1254',
+ },
+ {
+ name: 'windows1255',
+ value: 'windows1255',
+ },
+ {
+ name: 'windows1256',
+ value: 'windows1256',
+ },
+ {
+ name: 'windows1257',
+ value: 'windows1257',
+ },
+ {
+ name: 'windows1258',
+ value: 'windows1258',
+ },
+ {
+ name: 'windows874',
+ value: 'windows874',
+ },
+];
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f1b794a418812..380275f8476be 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1367,6 +1367,9 @@ importers:
tmp-promise:
specifier: 3.0.3
version: 3.0.3
+ ts-ics:
+ specifier: ^1.2.2
+ version: 1.2.2(date-fns@2.30.0)(lodash@4.17.21)(zod@3.22.4)
typedi:
specifier: 0.10.0
version: 0.10.0(patch_hash=sk6omkefrosihg7lmqbzh7vfxe)
@@ -13696,6 +13699,14 @@ packages:
whatwg-url: 14.0.0
dev: true
+ /date-fns-tz@2.0.0(date-fns@2.30.0):
+ resolution: {integrity: sha512-OAtcLdB9vxSXTWHdT8b398ARImVwQMyjfYGkKD2zaGpHseG2UPHbHjXELReErZFxWdSLph3c2zOaaTyHfOhERQ==}
+ peerDependencies:
+ date-fns: '>=2.0.0'
+ dependencies:
+ date-fns: 2.30.0
+ dev: false
+
/date-fns@2.30.0:
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
engines: {node: '>=0.11'}
@@ -24607,6 +24618,19 @@ packages:
typescript: 5.3.2
dev: true
+ /ts-ics@1.2.2(date-fns@2.30.0)(lodash@4.17.21)(zod@3.22.4):
+ resolution: {integrity: sha512-L7T5JQi99qQ2Uv7AoCHUZ8Mx1bJYo7qBZtBckuHueR90I3WVdW5NC/tOqTVgu18c3zj08du+xlgWlTIcE+Foxw==}
+ peerDependencies:
+ date-fns: ^2
+ lodash: ^4
+ zod: ^3
+ dependencies:
+ date-fns: 2.30.0
+ date-fns-tz: 2.0.0(date-fns@2.30.0)
+ lodash: 4.17.21
+ zod: 3.22.4
+ dev: false
+
/ts-jest@29.1.1(@babel/core@7.22.9)(jest@29.6.2)(typescript@5.3.2):
resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}