mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-02 07:01:30 -08:00
fix(HTTP Request Tool Node): Fix the undefined response issue when authentication is enabled (#11343)
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
parent
cade9b2d91
commit
094ec68d4c
|
@ -281,6 +281,7 @@ export class ToolHttpRequest implements INodeType {
|
||||||
'User-Agent': undefined,
|
'User-Agent': undefined,
|
||||||
},
|
},
|
||||||
body: {},
|
body: {},
|
||||||
|
// We will need a full response object later to extract the headers and check the response's content type.
|
||||||
returnFullResponse: true,
|
returnFullResponse: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,165 +1,240 @@
|
||||||
import get from 'lodash/get';
|
import { mock } from 'jest-mock-extended';
|
||||||
import type { IDataObject, IExecuteFunctions } from 'n8n-workflow';
|
import type { IExecuteFunctions, INode } from 'n8n-workflow';
|
||||||
import { jsonParse } from 'n8n-workflow';
|
import { jsonParse } from 'n8n-workflow';
|
||||||
|
|
||||||
import type { N8nTool } from '../../../../utils/N8nTool';
|
import type { N8nTool } from '../../../../utils/N8nTool';
|
||||||
import { ToolHttpRequest } from '../ToolHttpRequest.node';
|
import { ToolHttpRequest } from '../ToolHttpRequest.node';
|
||||||
|
|
||||||
const createExecuteFunctionsMock = (parameters: IDataObject, requestMock: any) => {
|
|
||||||
const nodeParameters = parameters;
|
|
||||||
|
|
||||||
return {
|
|
||||||
getNodeParameter(parameter: string) {
|
|
||||||
return get(nodeParameters, parameter);
|
|
||||||
},
|
|
||||||
getNode() {
|
|
||||||
return {
|
|
||||||
name: 'HTTP Request',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getInputData() {
|
|
||||||
return [{ json: {} }];
|
|
||||||
},
|
|
||||||
getWorkflow() {
|
|
||||||
return {
|
|
||||||
name: 'Test Workflow',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
continueOnFail() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
addInputData() {
|
|
||||||
return { index: 0 };
|
|
||||||
},
|
|
||||||
addOutputData() {
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
helpers: {
|
|
||||||
httpRequest: requestMock,
|
|
||||||
},
|
|
||||||
} as unknown as IExecuteFunctions;
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('ToolHttpRequest', () => {
|
describe('ToolHttpRequest', () => {
|
||||||
let httpTool: ToolHttpRequest;
|
const httpTool = new ToolHttpRequest();
|
||||||
let mockRequest: jest.Mock;
|
const helpers = mock<IExecuteFunctions['helpers']>();
|
||||||
|
const executeFunctions = mock<IExecuteFunctions>({ helpers });
|
||||||
|
|
||||||
describe('Binary response', () => {
|
describe('Binary response', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
httpTool = new ToolHttpRequest();
|
jest.resetAllMocks();
|
||||||
mockRequest = jest.fn();
|
executeFunctions.getNode.mockReturnValue(
|
||||||
|
mock<INode>({
|
||||||
|
type: 'n8n-nodes-base.httpRequest',
|
||||||
|
name: 'HTTP Request',
|
||||||
|
typeVersion: 1.1,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
executeFunctions.addInputData.mockReturnValue({ index: 0 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the error when receiving a binary response', async () => {
|
it('should return the error when receiving a binary response', async () => {
|
||||||
mockRequest.mockResolvedValue({
|
helpers.httpRequest.mockResolvedValue({
|
||||||
body: Buffer.from(''),
|
body: Buffer.from(''),
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'image/jpeg',
|
'content-type': 'image/jpeg',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { response } = await httpTool.supplyData.call(
|
executeFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||||
createExecuteFunctionsMock(
|
switch (paramName) {
|
||||||
{
|
case 'method':
|
||||||
method: 'GET',
|
return 'GET';
|
||||||
url: 'https://httpbin.org/image/jpeg',
|
case 'url':
|
||||||
options: {},
|
return 'https://httpbin.org/image/jpeg';
|
||||||
placeholderDefinitions: {
|
case 'options':
|
||||||
values: [],
|
return {};
|
||||||
},
|
case 'placeholderDefinitions.values':
|
||||||
},
|
return [];
|
||||||
mockRequest,
|
default:
|
||||||
),
|
return undefined;
|
||||||
0,
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
const res = await (response as N8nTool).invoke('');
|
const { response } = await httpTool.supplyData.call(executeFunctions, 0);
|
||||||
|
|
||||||
|
const res = await (response as N8nTool).invoke({});
|
||||||
|
expect(helpers.httpRequest).toHaveBeenCalled();
|
||||||
expect(res).toContain('error');
|
expect(res).toContain('error');
|
||||||
expect(res).toContain('Binary data is not supported');
|
expect(res).toContain('Binary data is not supported');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the response text when receiving a text response', async () => {
|
it('should return the response text when receiving a text response', async () => {
|
||||||
mockRequest.mockResolvedValue({
|
helpers.httpRequest.mockResolvedValue({
|
||||||
body: 'Hello World',
|
body: 'Hello World',
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'text/plain',
|
'content-type': 'text/plain',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { response } = await httpTool.supplyData.call(
|
executeFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||||
createExecuteFunctionsMock(
|
switch (paramName) {
|
||||||
{
|
case 'method':
|
||||||
method: 'GET',
|
return 'GET';
|
||||||
url: 'https://httpbin.org/text/plain',
|
case 'url':
|
||||||
options: {},
|
return 'https://httpbin.org/text/plain';
|
||||||
placeholderDefinitions: {
|
case 'options':
|
||||||
values: [],
|
return {};
|
||||||
},
|
case 'placeholderDefinitions.values':
|
||||||
},
|
return [];
|
||||||
mockRequest,
|
default:
|
||||||
),
|
return undefined;
|
||||||
0,
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
const res = await (response as N8nTool).invoke('');
|
const { response } = await httpTool.supplyData.call(executeFunctions, 0);
|
||||||
|
|
||||||
|
const res = await (response as N8nTool).invoke({});
|
||||||
|
expect(helpers.httpRequest).toHaveBeenCalled();
|
||||||
expect(res).toEqual('Hello World');
|
expect(res).toEqual('Hello World');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the response text when receiving a text response with a charset', async () => {
|
it('should return the response text when receiving a text response with a charset', async () => {
|
||||||
mockRequest.mockResolvedValue({
|
helpers.httpRequest.mockResolvedValue({
|
||||||
body: 'こんにちは世界',
|
body: 'こんにちは世界',
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'text/plain; charset=iso-2022-jp',
|
'content-type': 'text/plain; charset=iso-2022-jp',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { response } = await httpTool.supplyData.call(
|
executeFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||||
createExecuteFunctionsMock(
|
switch (paramName) {
|
||||||
{
|
case 'method':
|
||||||
method: 'GET',
|
return 'GET';
|
||||||
url: 'https://httpbin.org/text/plain',
|
case 'url':
|
||||||
options: {},
|
return 'https://httpbin.org/text/plain';
|
||||||
placeholderDefinitions: {
|
case 'options':
|
||||||
values: [],
|
return {};
|
||||||
},
|
case 'placeholderDefinitions.values':
|
||||||
},
|
return [];
|
||||||
mockRequest,
|
default:
|
||||||
),
|
return undefined;
|
||||||
0,
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
const res = await (response as N8nTool).invoke('');
|
const { response } = await httpTool.supplyData.call(executeFunctions, 0);
|
||||||
|
|
||||||
|
const res = await (response as N8nTool).invoke({});
|
||||||
|
expect(helpers.httpRequest).toHaveBeenCalled();
|
||||||
expect(res).toEqual('こんにちは世界');
|
expect(res).toEqual('こんにちは世界');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the response object when receiving a JSON response', async () => {
|
it('should return the response object when receiving a JSON response', async () => {
|
||||||
const mockJson = { hello: 'world' };
|
const mockJson = { hello: 'world' };
|
||||||
|
|
||||||
mockRequest.mockResolvedValue({
|
helpers.httpRequest.mockResolvedValue({
|
||||||
body: mockJson,
|
body: JSON.stringify(mockJson),
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { response } = await httpTool.supplyData.call(
|
executeFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||||
createExecuteFunctionsMock(
|
switch (paramName) {
|
||||||
{
|
case 'method':
|
||||||
method: 'GET',
|
return 'GET';
|
||||||
url: 'https://httpbin.org/json',
|
case 'url':
|
||||||
options: {},
|
return 'https://httpbin.org/json';
|
||||||
placeholderDefinitions: {
|
case 'options':
|
||||||
values: [],
|
return {};
|
||||||
},
|
case 'placeholderDefinitions.values':
|
||||||
},
|
return [];
|
||||||
mockRequest,
|
default:
|
||||||
),
|
return undefined;
|
||||||
0,
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
const res = await (response as N8nTool).invoke('');
|
const { response } = await httpTool.supplyData.call(executeFunctions, 0);
|
||||||
|
|
||||||
|
const res = await (response as N8nTool).invoke({});
|
||||||
|
expect(helpers.httpRequest).toHaveBeenCalled();
|
||||||
expect(jsonParse(res)).toEqual(mockJson);
|
expect(jsonParse(res)).toEqual(mockJson);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle authentication with predefined credentials', async () => {
|
||||||
|
helpers.httpRequestWithAuthentication.mockResolvedValue({
|
||||||
|
body: 'Hello World',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'text/plain',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
executeFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||||
|
switch (paramName) {
|
||||||
|
case 'method':
|
||||||
|
return 'GET';
|
||||||
|
case 'url':
|
||||||
|
return 'https://httpbin.org/text/plain';
|
||||||
|
case 'authentication':
|
||||||
|
return 'predefinedCredentialType';
|
||||||
|
case 'nodeCredentialType':
|
||||||
|
return 'linearApi';
|
||||||
|
case 'options':
|
||||||
|
return {};
|
||||||
|
case 'placeholderDefinitions.values':
|
||||||
|
return [];
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { response } = await httpTool.supplyData.call(executeFunctions, 0);
|
||||||
|
|
||||||
|
const res = await (response as N8nTool).invoke({});
|
||||||
|
|
||||||
|
expect(res).toEqual('Hello World');
|
||||||
|
|
||||||
|
expect(helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
|
||||||
|
'linearApi',
|
||||||
|
expect.objectContaining({
|
||||||
|
returnFullResponse: true,
|
||||||
|
}),
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle authentication with generic credentials', async () => {
|
||||||
|
helpers.httpRequest.mockResolvedValue({
|
||||||
|
body: 'Hello World',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'text/plain',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
executeFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||||
|
switch (paramName) {
|
||||||
|
case 'method':
|
||||||
|
return 'GET';
|
||||||
|
case 'url':
|
||||||
|
return 'https://httpbin.org/text/plain';
|
||||||
|
case 'authentication':
|
||||||
|
return 'genericCredentialType';
|
||||||
|
case 'genericAuthType':
|
||||||
|
return 'httpBasicAuth';
|
||||||
|
case 'options':
|
||||||
|
return {};
|
||||||
|
case 'placeholderDefinitions.values':
|
||||||
|
return [];
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
executeFunctions.getCredentials.mockResolvedValue({
|
||||||
|
user: 'username',
|
||||||
|
password: 'password',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { response } = await httpTool.supplyData.call(executeFunctions, 0);
|
||||||
|
|
||||||
|
const res = await (response as N8nTool).invoke({});
|
||||||
|
|
||||||
|
expect(res).toEqual('Hello World');
|
||||||
|
|
||||||
|
expect(helpers.httpRequest).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
returnFullResponse: true,
|
||||||
|
auth: expect.objectContaining({
|
||||||
|
username: 'username',
|
||||||
|
password: 'password',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -109,12 +109,11 @@ const predefinedCredentialRequest = async (ctx: IExecuteFunctions, itemIndex: nu
|
||||||
const additionalOptions = getOAuth2AdditionalParameters(predefinedType);
|
const additionalOptions = getOAuth2AdditionalParameters(predefinedType);
|
||||||
|
|
||||||
return async (options: IHttpRequestOptions) => {
|
return async (options: IHttpRequestOptions) => {
|
||||||
return await ctx.helpers.requestWithAuthentication.call(
|
return await ctx.helpers.httpRequestWithAuthentication.call(
|
||||||
ctx,
|
ctx,
|
||||||
predefinedType,
|
predefinedType,
|
||||||
options,
|
options,
|
||||||
additionalOptions && { oauth2: additionalOptions },
|
additionalOptions && { oauth2: additionalOptions },
|
||||||
itemIndex,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue