mirror of
https://github.com/n8n-io/n8n.git
synced 2026-06-01 09:17:08 +02:00
fix: Set Content-Type for Meta-family trigger node responses (#31354)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
068547b500
commit
16728b301c
|
|
@ -12,7 +12,6 @@ import type {
|
|||
JsonObject,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError, NodeConnectionTypes, NodeOperationError } from 'n8n-workflow';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { facebookApiRequest, getAllFields, getFields } from './GenericFunctions';
|
||||
import type { FacebookWebhookSubscription } from './types';
|
||||
|
|
@ -241,7 +240,6 @@ export class FacebookTrigger implements INodeType {
|
|||
return true;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const webhookUrl = this.getNodeWebhookUrl('default') as string;
|
||||
const object = this.getNodeParameter('object') as string;
|
||||
const appId = this.getNodeParameter('appId') as string;
|
||||
|
|
@ -251,7 +249,7 @@ export class FacebookTrigger implements INodeType {
|
|||
const body = {
|
||||
object: snakeCase(object),
|
||||
callback_url: webhookUrl,
|
||||
verify_token: uuid(),
|
||||
verify_token: this.getNode().id,
|
||||
fields: fields.includes('*') ? getAllFields(object) : fields,
|
||||
} as IDataObject;
|
||||
|
||||
|
|
@ -266,8 +264,6 @@ export class FacebookTrigger implements INodeType {
|
|||
body,
|
||||
);
|
||||
|
||||
webhookData.verifyToken = body.verify_token;
|
||||
|
||||
if (responseData.success !== true) {
|
||||
// Facebook did not return success, so something went wrong
|
||||
throw new NodeApiError(this.getNode(), responseData as JsonObject, {
|
||||
|
|
@ -305,13 +301,11 @@ export class FacebookTrigger implements INodeType {
|
|||
// Check if we're getting facebook's challenge request (https://developers.facebook.com/docs/graph-api/webhooks/getting-started)
|
||||
if (this.getWebhookName() === 'setup') {
|
||||
if (query['hub.challenge']) {
|
||||
//TODO
|
||||
//compare hub.verify_token with the saved token
|
||||
//const webhookData = this.getWorkflowStaticData('node');
|
||||
// if (webhookData.verifyToken !== query['hub.verify_token']) {
|
||||
// return {};
|
||||
// }
|
||||
res.status(200).send(query['hub.challenge']).end();
|
||||
if (this.getNode().id !== query['hub.verify_token']) {
|
||||
return {};
|
||||
}
|
||||
|
||||
res.status(200).type('text/plain').send(query['hub.challenge']).end();
|
||||
return {
|
||||
noWebhookResponse: true,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
import type { Response } from 'express';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import type { IDataObject, IWebhookFunctions } from 'n8n-workflow';
|
||||
|
||||
import { FacebookTrigger } from '../FacebookTrigger.node';
|
||||
|
||||
describe('FacebookTrigger', () => {
|
||||
let node: FacebookTrigger;
|
||||
let mockWebhookFunctions: jest.Mocked<IWebhookFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
node = new FacebookTrigger();
|
||||
mockWebhookFunctions = mock<IWebhookFunctions>();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('webhook', () => {
|
||||
const createMockResponse = () =>
|
||||
({
|
||||
status: jest.fn().mockReturnThis(),
|
||||
type: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
}) as unknown as Response;
|
||||
|
||||
it('should respond to verification challenge as text/plain when the verify token matches', async () => {
|
||||
const mockResponse = createMockResponse();
|
||||
|
||||
mockWebhookFunctions.getNode.mockReturnValue({ id: 'test-token' } as any);
|
||||
mockWebhookFunctions.getWebhookName.mockReturnValue('setup');
|
||||
mockWebhookFunctions.getQueryData.mockReturnValue({
|
||||
'hub.challenge': 'test-challenge',
|
||||
'hub.verify_token': 'test-token',
|
||||
} as IDataObject);
|
||||
mockWebhookFunctions.getBodyData.mockReturnValue({});
|
||||
mockWebhookFunctions.getHeaderData.mockReturnValue({});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({ rawBody: Buffer.from('') } as any);
|
||||
mockWebhookFunctions.getResponseObject.mockReturnValue(mockResponse);
|
||||
mockWebhookFunctions.getNodeParameter.mockReturnValue('accessToken');
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({ appSecret: '' });
|
||||
|
||||
const result = await node.webhook.call(mockWebhookFunctions);
|
||||
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(200);
|
||||
expect(mockResponse.type).toHaveBeenCalledWith('text/plain');
|
||||
expect(mockResponse.send).toHaveBeenCalledWith('test-challenge');
|
||||
expect(mockResponse.end).toHaveBeenCalled();
|
||||
expect(result).toEqual({ noWebhookResponse: true });
|
||||
});
|
||||
|
||||
it('should reject the verification challenge when the verify token does not match', async () => {
|
||||
const mockResponse = createMockResponse();
|
||||
|
||||
mockWebhookFunctions.getNode.mockReturnValue({ id: 'expected-token' } as any);
|
||||
mockWebhookFunctions.getWebhookName.mockReturnValue('setup');
|
||||
mockWebhookFunctions.getQueryData.mockReturnValue({
|
||||
'hub.challenge': 'test-challenge',
|
||||
'hub.verify_token': 'attacker-supplied-token',
|
||||
} as IDataObject);
|
||||
mockWebhookFunctions.getBodyData.mockReturnValue({});
|
||||
mockWebhookFunctions.getHeaderData.mockReturnValue({});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({ rawBody: Buffer.from('') } as any);
|
||||
mockWebhookFunctions.getResponseObject.mockReturnValue(mockResponse);
|
||||
mockWebhookFunctions.getNodeParameter.mockReturnValue('accessToken');
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({ appSecret: '' });
|
||||
|
||||
const result = await node.webhook.call(mockWebhookFunctions);
|
||||
|
||||
expect(mockResponse.send).not.toHaveBeenCalled();
|
||||
expect(mockResponse.status).not.toHaveBeenCalled();
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -224,7 +224,7 @@ export class FacebookLeadAdsTrigger implements INodeType {
|
|||
return {};
|
||||
}
|
||||
|
||||
res.status(200).send(query['hub.challenge']).end();
|
||||
res.status(200).type('text/plain').send(query['hub.challenge']).end();
|
||||
|
||||
return { noWebhookResponse: true };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
import type { Response } from 'express';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import type { IDataObject, INode, IWebhookFunctions } from 'n8n-workflow';
|
||||
|
||||
import { FacebookLeadAdsTrigger } from '../FacebookLeadAdsTrigger.node';
|
||||
|
||||
describe('FacebookLeadAdsTrigger', () => {
|
||||
let node: FacebookLeadAdsTrigger;
|
||||
let mockWebhookFunctions: jest.Mocked<IWebhookFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
node = new FacebookLeadAdsTrigger();
|
||||
mockWebhookFunctions = mock<IWebhookFunctions>();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('webhook', () => {
|
||||
it('should respond to verification challenge as text/plain', async () => {
|
||||
const mockResponse = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
type: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
} as unknown as Response;
|
||||
|
||||
mockWebhookFunctions.getWebhookName.mockReturnValue('setup');
|
||||
mockWebhookFunctions.getQueryData.mockReturnValue({
|
||||
'hub.challenge': 'test-challenge',
|
||||
'hub.verify_token': 'test-node-id',
|
||||
} as IDataObject);
|
||||
mockWebhookFunctions.getBodyData.mockReturnValue({});
|
||||
mockWebhookFunctions.getHeaderData.mockReturnValue({});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({ rawBody: Buffer.from('') } as any);
|
||||
mockWebhookFunctions.getResponseObject.mockReturnValue(mockResponse);
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({ clientSecret: 'secret' });
|
||||
mockWebhookFunctions.getNodeParameter.mockReturnValue('');
|
||||
mockWebhookFunctions.getNode.mockReturnValue(mock<INode>({ id: 'test-node-id' }));
|
||||
|
||||
const result = await node.webhook.call(mockWebhookFunctions);
|
||||
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(200);
|
||||
expect(mockResponse.type).toHaveBeenCalledWith('text/plain');
|
||||
expect(mockResponse.send).toHaveBeenCalledWith('test-challenge');
|
||||
expect(mockResponse.end).toHaveBeenCalled();
|
||||
expect(result).toEqual({ noWebhookResponse: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -398,7 +398,7 @@ export class MicrosoftTeamsTrigger implements INodeType {
|
|||
|
||||
// Handle Microsoft Graph validation request
|
||||
if (req.query.validationToken) {
|
||||
res.status(200).send(req.query.validationToken);
|
||||
res.status(200).type('text/plain').send(req.query.validationToken);
|
||||
return { noWebhookResponse: true };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -217,6 +217,7 @@ describe('Microsoft Teams Trigger Node', () => {
|
|||
};
|
||||
const mockResponse = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
type: jest.fn().mockReturnThis(),
|
||||
send: jest.fn(),
|
||||
};
|
||||
|
||||
|
|
@ -225,6 +226,7 @@ describe('Microsoft Teams Trigger Node', () => {
|
|||
|
||||
const result = await new MicrosoftTeamsTrigger().webhook.call(mockWebhookFunctions);
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(200);
|
||||
expect(mockResponse.type).toHaveBeenCalledWith('text/plain');
|
||||
expect(mockResponse.send).toHaveBeenCalledWith('validation-token');
|
||||
expect(result.noWebhookResponse).toBe(true);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ export class WhatsAppTrigger implements INodeType {
|
|||
return {};
|
||||
}
|
||||
|
||||
res.status(200).send(query['hub.challenge']).end();
|
||||
res.status(200).type('text/plain').send(query['hub.challenge']).end();
|
||||
|
||||
return { noWebhookResponse: true };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,6 +229,7 @@ describe('WhatsAppTrigger', () => {
|
|||
};
|
||||
const mockResponse = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
type: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
} as unknown as express.Response;
|
||||
|
|
@ -245,6 +246,7 @@ describe('WhatsAppTrigger', () => {
|
|||
|
||||
expect(result).toEqual({ noWebhookResponse: true });
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(200);
|
||||
expect(mockResponse.type).toHaveBeenCalledWith('text/plain');
|
||||
expect(mockResponse.send).toHaveBeenCalledWith('test-challenge');
|
||||
expect(mockResponse.end).toHaveBeenCalled();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user