mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
feat: Respond to chat and wait for response (#12546)
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in> Co-authored-by: Shireen Missi <94372015+ShireenMissi@users.noreply.github.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
FORM_TRIGGER_NODE_TYPE,
|
||||
CHAT_TRIGGER_NODE_TYPE,
|
||||
WAIT_NODE_TYPE,
|
||||
WAIT_INDEFINITELY,
|
||||
} from 'n8n-workflow';
|
||||
import type { Readable } from 'stream';
|
||||
|
||||
@@ -334,6 +335,14 @@ export class RespondToWebhook implements INodeType {
|
||||
],
|
||||
};
|
||||
|
||||
async onMessage(
|
||||
context: IExecuteFunctions,
|
||||
_data: INodeExecutionData,
|
||||
): Promise<INodeExecutionData[][]> {
|
||||
const inputData = context.getInputData();
|
||||
return [inputData];
|
||||
}
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const nodeVersion = this.getNode().typeVersion;
|
||||
@@ -347,6 +356,10 @@ export class RespondToWebhook implements INodeType {
|
||||
|
||||
let response: IN8nHttpFullResponse;
|
||||
|
||||
const connectedNodes = this.getParentNodes(this.getNode().name, {
|
||||
includeNodeParameters: true,
|
||||
});
|
||||
|
||||
const options = this.getNodeParameter('options', 0, {});
|
||||
|
||||
const shouldStream =
|
||||
@@ -354,7 +367,6 @@ export class RespondToWebhook implements INodeType {
|
||||
|
||||
try {
|
||||
if (nodeVersion >= 1.1) {
|
||||
const connectedNodes = this.getParentNodes(this.getNode().name);
|
||||
if (!connectedNodes.some(({ type }) => WEBHOOK_NODE_TYPES.includes(type))) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
@@ -507,6 +519,40 @@ export class RespondToWebhook implements INodeType {
|
||||
);
|
||||
}
|
||||
|
||||
const chatTrigger = connectedNodes.find(
|
||||
(node) => node.type === CHAT_TRIGGER_NODE_TYPE && !node.disabled,
|
||||
);
|
||||
|
||||
const parameters = chatTrigger?.parameters as {
|
||||
options: { responseMode: string };
|
||||
};
|
||||
|
||||
// if workflow is started from chat trigger and responseMode is set to "responseNodes"
|
||||
// response to chat will be send by ChatService
|
||||
if (
|
||||
chatTrigger &&
|
||||
!chatTrigger.disabled &&
|
||||
parameters.options.responseMode === 'responseNodes'
|
||||
) {
|
||||
let message = '';
|
||||
|
||||
if (responseBody && typeof responseBody === 'object' && !Array.isArray(responseBody)) {
|
||||
message =
|
||||
(((responseBody as IDataObject).output ??
|
||||
(responseBody as IDataObject).text ??
|
||||
(responseBody as IDataObject).message) as string) ?? '';
|
||||
|
||||
if (message === '' && Object.keys(responseBody).length > 0) {
|
||||
try {
|
||||
message = JSON.stringify(responseBody, null, 2);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
await this.putExecutionToWait(WAIT_INDEFINITELY);
|
||||
return [[{ json: {}, sendMessage: message }]];
|
||||
}
|
||||
|
||||
if (
|
||||
hasHtmlContentType &&
|
||||
respondWith !== 'text' &&
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
type INode,
|
||||
type INodeExecutionData,
|
||||
type NodeTypeAndVersion,
|
||||
CHAT_TRIGGER_NODE_TYPE,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { RespondToWebhook } from '../RespondToWebhook.node';
|
||||
@@ -23,6 +24,78 @@ describe('RespondToWebhook Node', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('chatTrigger response', () => {
|
||||
it('should handle chatTrigger correctly when enabled and responseBody is an object', async () => {
|
||||
mockExecuteFunctions.getInputData.mockReturnValue([{ json: { input: true } }]);
|
||||
mockExecuteFunctions.getNode.mockReturnValue(mock<INode>({ typeVersion: 1.4 }));
|
||||
mockExecuteFunctions.getParentNodes.mockReturnValue([
|
||||
mock<NodeTypeAndVersion>({
|
||||
type: CHAT_TRIGGER_NODE_TYPE,
|
||||
disabled: false,
|
||||
parameters: { options: { responseMode: 'responseNodes' } },
|
||||
}),
|
||||
]);
|
||||
|
||||
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName) => {
|
||||
if (paramName === 'respondWith') return 'json';
|
||||
if (paramName === 'responseBody') return { message: 'Hello World' };
|
||||
if (paramName === 'options') return {};
|
||||
});
|
||||
mockExecuteFunctions.putExecutionToWait.mockResolvedValue();
|
||||
|
||||
const result = await respondToWebhook.execute.call(mockExecuteFunctions);
|
||||
expect(result).toEqual([[{ json: {}, sendMessage: 'Hello World' }]]);
|
||||
});
|
||||
|
||||
it('should handle chatTrigger correctly when enabled and responseBody is not an object', async () => {
|
||||
mockExecuteFunctions.getInputData.mockReturnValue([{ json: { input: true } }]);
|
||||
mockExecuteFunctions.getNode.mockReturnValue(mock<INode>({ typeVersion: 1.1 }));
|
||||
mockExecuteFunctions.getParentNodes.mockReturnValue([
|
||||
mock<NodeTypeAndVersion>({
|
||||
type: CHAT_TRIGGER_NODE_TYPE,
|
||||
disabled: false,
|
||||
parameters: { options: { responseMode: 'responseNodes' } },
|
||||
}),
|
||||
]);
|
||||
|
||||
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName) => {
|
||||
if (paramName === 'respondWith') return 'text';
|
||||
if (paramName === 'responseBody') return 'Just a string';
|
||||
if (paramName === 'options') return {};
|
||||
});
|
||||
mockExecuteFunctions.putExecutionToWait.mockResolvedValue();
|
||||
|
||||
const result = await respondToWebhook.execute.call(mockExecuteFunctions);
|
||||
expect(result).toEqual([[{ json: {}, sendMessage: '' }]]);
|
||||
});
|
||||
|
||||
it('should not handle chatTrigger when disabled', async () => {
|
||||
mockExecuteFunctions.getInputData.mockReturnValue([{ json: { input: true } }]);
|
||||
mockExecuteFunctions.getNode.mockReturnValue(mock<INode>({ typeVersion: 1.1 }));
|
||||
mockExecuteFunctions.getParentNodes.mockReturnValue([
|
||||
mock<NodeTypeAndVersion>({ type: CHAT_TRIGGER_NODE_TYPE, disabled: true }),
|
||||
]);
|
||||
|
||||
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName) => {
|
||||
if (paramName === 'respondWith') return 'json';
|
||||
if (paramName === 'responseBody') return { message: 'Hello World' };
|
||||
if (paramName === 'options') return {};
|
||||
});
|
||||
mockExecuteFunctions.sendResponse.mockReturnValue();
|
||||
|
||||
await expect(respondToWebhook.execute.call(mockExecuteFunctions)).resolves.not.toThrow();
|
||||
expect(mockExecuteFunctions.sendResponse).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return input data onMessage call', async () => {
|
||||
mockExecuteFunctions.getInputData.mockReturnValue([{ json: { input: true } }]);
|
||||
const result = await respondToWebhook.onMessage(mockExecuteFunctions, {
|
||||
json: { message: '' },
|
||||
});
|
||||
expect(result).toEqual([[{ json: { input: true } }]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute method', () => {
|
||||
it('should throw an error if no WEBHOOK_NODE_TYPES in parents', async () => {
|
||||
mockExecuteFunctions.getInputData.mockReturnValue([]);
|
||||
|
||||
Reference in New Issue
Block a user