fix(HTTP Request Node): Fix paginated requests with HttpBearerAuth (#17005)

This commit is contained in:
Tomi Turtiainen
2025-07-04 21:53:01 +03:00
committed by GitHub
parent 621745e291
commit 3b14830966
7 changed files with 49 additions and 10 deletions

View File

@@ -28,7 +28,13 @@ import type {
IExecuteData, IExecuteData,
IDataObject, IDataObject,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { ICredentialsHelper, NodeHelpers, Workflow, UnexpectedError } from 'n8n-workflow'; import {
ICredentialsHelper,
NodeHelpers,
Workflow,
UnexpectedError,
isExpression,
} from 'n8n-workflow';
import { CredentialTypes } from '@/credential-types'; import { CredentialTypes } from '@/credential-types';
import { CredentialsOverwrites } from '@/credentials-overwrites'; import { CredentialsOverwrites } from '@/credentials-overwrites';
@@ -208,7 +214,7 @@ export class CredentialsHelper extends ICredentialsHelper {
workflow: Workflow, workflow: Workflow,
node: INode, node: INode,
): string { ): string {
if (typeof parameterValue !== 'string' || parameterValue.charAt(0) !== '=') { if (!isExpression(parameterValue)) {
return parameterValue; return parameterValue;
} }

View File

@@ -1,12 +1,9 @@
import { i18n } from '@n8n/i18n'; import { i18n } from '@n8n/i18n';
import { useWorkflowsStore } from '@/stores/workflows.store'; import { useWorkflowsStore } from '@/stores/workflows.store';
import type { ResolvableState } from '@/types/expressions'; import type { ResolvableState } from '@/types/expressions';
import { ExpressionError, ExpressionParser, type Result } from 'n8n-workflow'; import { ExpressionError, ExpressionParser, isExpression, type Result } from 'n8n-workflow';
export const isExpression = (expr: unknown) => { export { isExpression };
if (typeof expr !== 'string') return false;
return expr.startsWith('=');
};
export const isEmptyExpression = (expr: string) => { export const isEmptyExpression = (expr: string) => {
return /\{\{\s*\}\}/.test(expr); return /\{\{\s*\}\}/.test(expr);
@@ -17,7 +14,7 @@ export const unwrapExpression = (expr: string) => {
}; };
export const removeExpressionPrefix = <T = unknown>(expr: T): T | string => { export const removeExpressionPrefix = <T = unknown>(expr: T): T | string => {
return typeof expr === 'string' && expr.startsWith('=') ? expr.slice(1) : (expr ?? ''); return isExpression(expr) ? expr.slice(1) : (expr ?? '');
}; };
export const isTestableExpression = (expr: string) => { export const isTestableExpression = (expr: string) => {

View File

@@ -36,7 +36,7 @@ export class HttpBearerAuth implements ICredentialType {
type: 'generic', type: 'generic',
properties: { properties: {
headers: { headers: {
Authorization: 'Bearer ={{$credentials.token}}', Authorization: '=Bearer {{$credentials.token}}',
}, },
}, },
}; };

View File

@@ -25,6 +25,7 @@ import type {
} from './interfaces'; } from './interfaces';
import type { Workflow } from './workflow'; import type { Workflow } from './workflow';
import { WorkflowDataProxy } from './workflow-data-proxy'; import { WorkflowDataProxy } from './workflow-data-proxy';
import { isExpression } from './expressions/expression-helpers';
const IS_FRONTEND_IN_DEV_MODE = const IS_FRONTEND_IN_DEV_MODE =
typeof process === 'object' && typeof process === 'object' &&
@@ -118,7 +119,7 @@ export class Expression {
contextNodeName?: string, contextNodeName?: string,
): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] { ): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] {
// Check if it is an expression // Check if it is an expression
if (typeof parameterValue !== 'string' || parameterValue.charAt(0) !== '=') { if (!isExpression(parameterValue)) {
// Is no expression so return value // Is no expression so return value
return parameterValue; return parameterValue;
} }

View File

@@ -0,0 +1,9 @@
/**
* Checks if the given value is an expression. An expression is a string that
* starts with '='.
*/
export const isExpression = (expr: unknown): expr is string => {
if (typeof expr !== 'string') return false;
return expr.charAt(0) === '=';
};

View File

@@ -12,6 +12,7 @@ export * from './interfaces';
export * from './message-event-bus'; export * from './message-event-bus';
export * from './execution-status'; export * from './execution-status';
export * from './expression'; export * from './expression';
export * from './expressions/expression-helpers';
export * from './from-ai-parse-utils'; export * from './from-ai-parse-utils';
export * from './node-helpers'; export * from './node-helpers';
export * from './node-reference-parser-utils'; export * from './node-reference-parser-utils';

View File

@@ -0,0 +1,25 @@
import { isExpression } from '../../src/expressions/expression-helpers';
describe('ExpressionHelpers', () => {
describe('isExpression', () => {
describe('should return true for valid expressions', () => {
test.each([
['=1', 'simple number expression'],
['=true', 'boolean expression'],
['="hello"', 'string expression'],
['={{ $json.field }}', 'complex expression with spaces'],
])('"$s" should be an expression', (expr) => {
expect(isExpression(expr)).toBe(true);
});
});
describe('should return false for invalid expressions', () => {
test.each([[null], [undefined], [1], [true], [''], ['hello']])(
'"$s" should not be an expression',
(expr) => {
expect(isExpression(expr)).toBe(false);
},
);
});
});
});