feat(Set Node): Preserve binary data by default (#9668)

Co-authored-by: Michael Kret <michael.k@radency.com>
This commit is contained in:
Elias Meire 2024-06-18 10:32:51 +02:00 committed by GitHub
parent 0524f588f6
commit d1163533a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 154 additions and 12 deletions

View file

@ -577,17 +577,17 @@ describe('NDV', () => {
workflowPage.actions.openNode('Edit Fields (old)');
ndv.actions.openSettings();
ndv.getters.nodeVersion().should('have.text', 'Set node version 2 (Latest version: 3.3)');
ndv.getters.nodeVersion().should('have.text', 'Set node version 2 (Latest version: 3.4)');
ndv.actions.close();
workflowPage.actions.openNode('Edit Fields (latest)');
ndv.actions.openSettings();
ndv.getters.nodeVersion().should('have.text', 'Edit Fields (Set) node version 3.3 (Latest)');
ndv.getters.nodeVersion().should('have.text', 'Edit Fields (Set) node version 3.4 (Latest)');
ndv.actions.close();
workflowPage.actions.openNode('Edit Fields (no typeVersion)');
ndv.actions.openSettings();
ndv.getters.nodeVersion().should('have.text', 'Edit Fields (Set) node version 3.3 (Latest)');
ndv.getters.nodeVersion().should('have.text', 'Edit Fields (Set) node version 3.4 (Latest)');
ndv.actions.close();
workflowPage.actions.openNode('Function');

View file

@ -32,7 +32,7 @@
"id": "273f60c9-08e7-457e-b01d-31e16c565171",
"name": "Edit Fields (latest)",
"type": "n8n-nodes-base.set",
"typeVersion": 3.3,
"typeVersion": 3.4,
"position": [640, 460]
}
],

View file

@ -102,6 +102,7 @@ const iconSource = computed<NodeIconSource>(() => {
// Otherwise, extract it from icon prop
if (nodeType.icon) {
const icon = getNodeIcon(nodeType, uiStore.appliedTheme);
if (icon) {
const [type, path] = icon.split(':');
if (type === 'file') {

View file

@ -12,7 +12,7 @@ export class Set extends VersionedNodeType {
icon: 'fa:pen',
group: ['input'],
description: 'Add or edit fields on an input item and optionally remove other fields',
defaultVersion: 3.3,
defaultVersion: 3.4,
};
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
@ -22,6 +22,7 @@ export class Set extends VersionedNodeType {
3.1: new SetV2(baseDescription),
3.2: new SetV2(baseDescription),
3.3: new SetV2(baseDescription),
3.4: new SetV2(baseDescription),
};
super(nodeVersions, baseDescription);

View file

@ -69,7 +69,7 @@ describe('test Set2, composeReturnItem', () => {
include: 'none',
};
const result = composeReturnItem.call(fakeExecuteFunction, 0, inputItem, newData, options);
const result = composeReturnItem.call(fakeExecuteFunction, 0, inputItem, newData, options, 3.4);
expect(result).toEqual({
json: {
@ -114,7 +114,7 @@ describe('test Set2, composeReturnItem', () => {
include: 'selected',
};
const result = composeReturnItem.call(fakeExecuteFunction, 0, inputItem, newData, options);
const result = composeReturnItem.call(fakeExecuteFunction, 0, inputItem, newData, options, 3.4);
expect(result).toEqual({
json: {
@ -132,6 +132,122 @@ describe('test Set2, composeReturnItem', () => {
},
});
});
it('should include binary when expected in version <3.4', () => {
const fakeExecuteFunction = createMockExecuteFunction({});
const inputItem = {
json: {
input1: 'value1',
input2: 2,
input3: [1, 2, 3],
},
pairedItem: {
item: 0,
input: undefined,
},
binary: {
data: {
data: 'content',
mimeType: 'image/jpg',
},
},
};
const newData = {
num1: 55,
str1: '42',
arr1: ['foo', 'bar'],
obj: {
key: 'value',
},
};
const resultWithIncludeBinary = composeReturnItem.call(
fakeExecuteFunction,
0,
inputItem,
newData,
{
include: 'all',
includeBinary: true,
},
3.3,
);
expect(resultWithIncludeBinary.binary).toEqual(inputItem.binary);
const resultWithoutIncludeBinary = composeReturnItem.call(
fakeExecuteFunction,
0,
inputItem,
newData,
{
include: 'all',
},
3.3,
);
expect(resultWithoutIncludeBinary.binary).toBeUndefined();
});
it('should include binary when expected in version >=3.4', () => {
const fakeExecuteFunction = createMockExecuteFunction({});
const inputItem = {
json: {
input1: 'value1',
input2: 2,
input3: [1, 2, 3],
},
pairedItem: {
item: 0,
input: undefined,
},
binary: {
data: {
data: 'content',
mimeType: 'image/jpg',
},
},
};
const newData = {
num1: 55,
str1: '42',
arr1: ['foo', 'bar'],
obj: {
key: 'value',
},
};
const resultWithStripBinary = composeReturnItem.call(
fakeExecuteFunction,
0,
inputItem,
newData,
{
include: 'all',
stripBinary: true,
},
3.4,
);
expect(resultWithStripBinary.binary).toBeUndefined();
const resultWithoutStripBinary = composeReturnItem.call(
fakeExecuteFunction,
0,
inputItem,
newData,
{
include: 'all',
},
3.4,
);
expect(resultWithoutStripBinary.binary).toEqual(inputItem.binary);
});
});
describe('test Set2, parseJsonParameter', () => {

View file

@ -21,7 +21,7 @@ const versionDescription: INodeTypeDescription = {
name: 'set',
iconColor: 'blue',
group: ['input'],
version: [3, 3.1, 3.2, 3.3],
version: [3, 3.1, 3.2, 3.3, 3.4],
description: 'Modify, add, or remove item fields',
subtitle: '={{$parameter["mode"]}}',
defaults: {
@ -208,8 +208,27 @@ const versionDescription: INodeTypeDescription = {
name: 'includeBinary',
type: 'boolean',
default: true,
displayOptions: {
hide: {
'@version': [{ _cnd: { gte: 3.4 } }],
},
},
description: 'Whether binary data should be included if present in the input item',
},
{
displayName: 'Strip Binary Data',
name: 'stripBinary',
type: 'boolean',
default: true,
description:
'Whether binary data should be stripped from the input item. Only applies when "Include Other Input Fields" is enabled.',
displayOptions: {
show: {
'@version': [{ _cnd: { gte: 3.4 } }],
'/includeOtherFields': [true],
},
},
},
{
displayName: 'Ignore Type Conversion Errors',
name: 'ignoreConversionErrors',

View file

@ -5,6 +5,7 @@ export type SetNodeOptions = {
ignoreConversionErrors?: boolean;
include?: IncludeMods;
includeBinary?: boolean;
stripBinary?: boolean;
};
export type SetField = {

View file

@ -56,13 +56,17 @@ export function composeReturnItem(
inputItem: INodeExecutionData,
newFields: IDataObject,
options: SetNodeOptions,
nodeVersion: number,
) {
const newItem: INodeExecutionData = {
json: {},
pairedItem: { item: itemIndex },
};
if (options.includeBinary && inputItem.binary !== undefined) {
const includeBinary =
(nodeVersion >= 3.4 && !options.stripBinary && options.include !== 'none') ||
(nodeVersion < 3.4 && !!options.includeBinary);
if (includeBinary && inputItem.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.

View file

@ -225,7 +225,7 @@ export async function execute(
newData[name] = value;
}
return composeReturnItem.call(this, i, item, newData, options);
return composeReturnItem.call(this, i, item, newData, options, node.typeVersion);
}
const assignmentCollection = this.getNodeParameter(
@ -247,7 +247,7 @@ export async function execute(
return [name, value];
}),
);
return composeReturnItem.call(this, i, item, newData, options);
return composeReturnItem.call(this, i, item, newData, options, node.typeVersion);
} catch (error) {
if (this.continueOnFail()) {
return { json: { error: (error as Error).message, pairedItem: { item: i } } };

View file

@ -54,7 +54,7 @@ export async function execute(
);
}
return composeReturnItem.call(this, i, item, newData, options);
return composeReturnItem.call(this, i, item, newData, options, node.typeVersion);
} catch (error) {
if (this.continueOnFail()) {
return { json: { error: (error as Error).message }, pairedItem: { item: i } };