mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 22:24:05 -08:00
fix: Remove Request Options from sub nodes (no-changelog) (#9853)
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
parent
2c0df8d467
commit
19213efc30
|
@ -25,18 +25,6 @@ describe('useNodeType()', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isSubNodeType', () => {
|
|
||||||
it('identifies sub node type correctly', () => {
|
|
||||||
const nodeTypeOption = {
|
|
||||||
name: 'testNodeType',
|
|
||||||
outputs: ['Main', 'Other'],
|
|
||||||
} as unknown as SimplifiedNodeType;
|
|
||||||
const { isSubNodeType } = useNodeType({ nodeType: nodeTypeOption });
|
|
||||||
|
|
||||||
expect(isSubNodeType.value).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('isMultipleOutputsNodeType', () => {
|
describe('isMultipleOutputsNodeType', () => {
|
||||||
it('identifies multiple outputs node type correctly', () => {
|
it('identifies multiple outputs node type correctly', () => {
|
||||||
const nodeTypeOption = {
|
const nodeTypeOption = {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { computed, unref } from 'vue';
|
||||||
import type { INodeTypeDescription } from 'n8n-workflow';
|
import type { INodeTypeDescription } from 'n8n-workflow';
|
||||||
import type { INodeUi, SimplifiedNodeType } from '@/Interface';
|
import type { INodeUi, SimplifiedNodeType } from '@/Interface';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
import { NodeConnectionType, NodeHelpers } from 'n8n-workflow';
|
import { NodeHelpers } from 'n8n-workflow';
|
||||||
|
|
||||||
export function useNodeType(
|
export function useNodeType(
|
||||||
options: {
|
options: {
|
||||||
|
@ -26,15 +26,7 @@ export function useNodeType(
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const isSubNodeType = computed(() => {
|
const isSubNodeType = computed(() => NodeHelpers.isSubNodeType(nodeType.value));
|
||||||
if (!nodeType.value?.outputs || typeof nodeType.value?.outputs === 'string') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const outputTypes = NodeHelpers.getConnectionTypes(nodeType.value?.outputs);
|
|
||||||
return outputTypes
|
|
||||||
? outputTypes.filter((output) => output !== NodeConnectionType.Main).length > 0
|
|
||||||
: false;
|
|
||||||
});
|
|
||||||
|
|
||||||
const isMultipleOutputsNodeType = computed(() => {
|
const isMultipleOutputsNodeType = computed(() => {
|
||||||
const outputs = nodeType.value?.outputs;
|
const outputs = nodeType.value?.outputs;
|
||||||
|
|
|
@ -530,13 +530,13 @@ describe('mapCanvasConnectionToLegacyConnection', () => {
|
||||||
describe('mapLegacyEndpointsToCanvasConnectionPort', () => {
|
describe('mapLegacyEndpointsToCanvasConnectionPort', () => {
|
||||||
it('should return an empty array and log a warning when inputs is a string', () => {
|
it('should return an empty array and log a warning when inputs is a string', () => {
|
||||||
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||||
const endpoints: INodeTypeDescription['inputs'] = 'some code';
|
const endpoints: INodeTypeDescription['inputs'] = '={{some code}}';
|
||||||
const result = mapLegacyEndpointsToCanvasConnectionPort(endpoints);
|
const result = mapLegacyEndpointsToCanvasConnectionPort(endpoints);
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
expect(result).toEqual([]);
|
||||||
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
||||||
'Node endpoints have not been evaluated',
|
'Node endpoints have not been evaluated',
|
||||||
'some code',
|
'={{some code}}',
|
||||||
);
|
);
|
||||||
|
|
||||||
consoleWarnSpy.mockRestore();
|
consoleWarnSpy.mockRestore();
|
||||||
|
|
|
@ -1766,21 +1766,24 @@ export interface INodeInputConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INodeOutputConfiguration {
|
export interface INodeOutputConfiguration {
|
||||||
category?: string;
|
category?: 'error';
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
|
maxConnections?: number;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
type: ConnectionTypes;
|
type: ConnectionTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ExpressionString = `={{${string}}}`;
|
||||||
|
|
||||||
export interface INodeTypeDescription extends INodeTypeBaseDescription {
|
export interface INodeTypeDescription extends INodeTypeBaseDescription {
|
||||||
version: number | number[];
|
version: number | number[];
|
||||||
defaults: INodeParameters;
|
defaults: INodeParameters;
|
||||||
eventTriggerDescription?: string;
|
eventTriggerDescription?: string;
|
||||||
activationMessage?: string;
|
activationMessage?: string;
|
||||||
inputs: Array<ConnectionTypes | INodeInputConfiguration> | string;
|
inputs: Array<ConnectionTypes | INodeInputConfiguration> | ExpressionString;
|
||||||
requiredInputs?: string | number[] | number; // Ony available with executionOrder => "v1"
|
requiredInputs?: string | number[] | number; // Ony available with executionOrder => "v1"
|
||||||
inputNames?: string[];
|
inputNames?: string[];
|
||||||
outputs: Array<ConnectionTypes | INodeInputConfiguration> | string;
|
outputs: Array<ConnectionTypes | INodeOutputConfiguration> | ExpressionString;
|
||||||
outputNames?: string[];
|
outputNames?: string[];
|
||||||
properties: INodeProperties[];
|
properties: INodeProperties[];
|
||||||
credentials?: INodeCredentialDescription[];
|
credentials?: INodeCredentialDescription[];
|
||||||
|
|
|
@ -10,6 +10,7 @@ import get from 'lodash/get';
|
||||||
import isEqual from 'lodash/isEqual';
|
import isEqual from 'lodash/isEqual';
|
||||||
import uniqBy from 'lodash/uniqBy';
|
import uniqBy from 'lodash/uniqBy';
|
||||||
|
|
||||||
|
import { NodeConnectionType } from './Interfaces';
|
||||||
import type {
|
import type {
|
||||||
FieldType,
|
FieldType,
|
||||||
IContextObject,
|
IContextObject,
|
||||||
|
@ -351,8 +352,31 @@ const declarativeNodeOptionParameters: INodeProperties = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the provided node type has any output types other than the main connection type.
|
||||||
|
* @param typeDescription The node's type description to check.
|
||||||
|
*/
|
||||||
|
export function isSubNodeType(
|
||||||
|
typeDescription: Pick<INodeTypeDescription, 'outputs'> | null,
|
||||||
|
): boolean {
|
||||||
|
if (!typeDescription || !typeDescription.outputs || typeof typeDescription.outputs === 'string') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const outputTypes = getConnectionTypes(typeDescription.outputs);
|
||||||
|
return outputTypes
|
||||||
|
? outputTypes.filter((output) => output !== NodeConnectionType.Main).length > 0
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Augments additional `Request Options` property on declarative node-type */
|
||||||
export function applyDeclarativeNodeOptionParameters(nodeType: INodeType): void {
|
export function applyDeclarativeNodeOptionParameters(nodeType: INodeType): void {
|
||||||
if (nodeType.execute || nodeType.trigger || nodeType.webhook || nodeType.description.polling) {
|
if (
|
||||||
|
nodeType.execute ||
|
||||||
|
nodeType.trigger ||
|
||||||
|
nodeType.webhook ||
|
||||||
|
nodeType.description.polling ||
|
||||||
|
isSubNodeType(nodeType.description)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
import type { INode, INodeParameters, INodeProperties, INodeTypeDescription } from '@/Interfaces';
|
import type {
|
||||||
|
INode,
|
||||||
|
INodeParameters,
|
||||||
|
INodeProperties,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
} from '@/Interfaces';
|
||||||
import type { Workflow } from '@/Workflow';
|
import type { Workflow } from '@/Workflow';
|
||||||
|
import {
|
||||||
import { getNodeParameters, getNodeHints, isSingleExecution } from '@/NodeHelpers';
|
getNodeParameters,
|
||||||
|
getNodeHints,
|
||||||
|
isSingleExecution,
|
||||||
|
isSubNodeType,
|
||||||
|
applyDeclarativeNodeOptionParameters,
|
||||||
|
} from '@/NodeHelpers';
|
||||||
|
|
||||||
describe('NodeHelpers', () => {
|
describe('NodeHelpers', () => {
|
||||||
describe('getNodeParameters', () => {
|
describe('getNodeParameters', () => {
|
||||||
|
@ -3528,6 +3539,7 @@ describe('NodeHelpers', () => {
|
||||||
expect(hints).toHaveLength(1);
|
expect(hints).toHaveLength(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isSingleExecution', () => {
|
describe('isSingleExecution', () => {
|
||||||
test('should determine based on node parameters if it would be executed once', () => {
|
test('should determine based on node parameters if it would be executed once', () => {
|
||||||
expect(isSingleExecution('n8n-nodes-base.code', {})).toEqual(true);
|
expect(isSingleExecution('n8n-nodes-base.code', {})).toEqual(true);
|
||||||
|
@ -3555,4 +3567,72 @@ describe('NodeHelpers', () => {
|
||||||
expect(isSingleExecution('n8n-nodes-base.redis', {})).toEqual(true);
|
expect(isSingleExecution('n8n-nodes-base.redis', {})).toEqual(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('isSubNodeType', () => {
|
||||||
|
const tests: Array<[boolean, Pick<INodeTypeDescription, 'outputs'> | null]> = [
|
||||||
|
[false, null],
|
||||||
|
[false, { outputs: '={{random_expression}}' }],
|
||||||
|
[false, { outputs: [] }],
|
||||||
|
[false, { outputs: ['main'] }],
|
||||||
|
[true, { outputs: ['ai_agent'] }],
|
||||||
|
[true, { outputs: ['main', 'ai_agent'] }],
|
||||||
|
];
|
||||||
|
test.each(tests)('should return %p for %o', (expected, nodeType) => {
|
||||||
|
expect(isSubNodeType(nodeType)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('applyDeclarativeNodeOptionParameters', () => {
|
||||||
|
test.each([
|
||||||
|
[
|
||||||
|
'node with execute method',
|
||||||
|
{
|
||||||
|
execute: jest.fn(),
|
||||||
|
description: {
|
||||||
|
properties: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'node with trigger method',
|
||||||
|
{
|
||||||
|
trigger: jest.fn(),
|
||||||
|
description: {
|
||||||
|
properties: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'node with webhook method',
|
||||||
|
{
|
||||||
|
webhook: jest.fn(),
|
||||||
|
description: {
|
||||||
|
properties: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'a polling node-type',
|
||||||
|
{
|
||||||
|
description: {
|
||||||
|
polling: true,
|
||||||
|
properties: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'a node-type with a non-main output',
|
||||||
|
{
|
||||||
|
description: {
|
||||||
|
outputs: ['main', 'ai_agent'],
|
||||||
|
properties: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])('should not modify properties on node with %s method', (_, nodeTypeName) => {
|
||||||
|
const nodeType = nodeTypeName as unknown as INodeType;
|
||||||
|
applyDeclarativeNodeOptionParameters(nodeType);
|
||||||
|
expect(nodeType.description.properties).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue