fix: Resolve expressions in credentials following paired item (#8250)

## Summary
Fixes the issue that pairedItem information was not available in
expressions that got used in credentials


## Related tickets and issues

[PAY-1207](https://linear.app/n8n/issue/PAY-1207/paireditem-expressions-not-working-correctly-in-credentials)


## Review / Merge checklist
- [x] PR title and summary are descriptive. **Remember, the title
automatically goes into the changelog. Use `(no-changelog)` otherwise.**
([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md))
- [ ] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up
ticket created.
- [ ] Tests included.
> A bug is not considered fixed, unless a test is added to prevent it
from happening again.
   > A feature is not complete without tests.

---------

Co-authored-by: Omar Ajoue <krynble@gmail.com>
This commit is contained in:
Jan Oberhauser 2024-01-08 10:48:20 +01:00 committed by GitHub
parent 008fd5a917
commit ccb2b076f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 56 additions and 39 deletions

View file

@ -8,10 +8,14 @@ This list shows all the versions which include breaking changes and how to upgra
The flag `N8N_CACHE_ENABLED` was removed. The cache is now always enabled.
Additionally, expressions in credentials now follow the paired item, so if you have multiple input items, n8n will try to pair the matching row to fill in the credential details.
### When is action necessary?
If you are using the flag `N8N_CACHE_ENABLED`, remove it from your settings.
In regards to credentials, if you use expression in credentials, you might want to revisit them. Previously, n8n would stick to the first item only, but now it will try to match the proper paired item.
## 1.22.0
### What changed?

View file

@ -32,6 +32,7 @@ import type {
INodeTypes,
IWorkflowExecuteAdditionalData,
ICredentialTestFunctions,
IExecuteData,
} from 'n8n-workflow';
import {
ICredentialsHelper,
@ -339,6 +340,7 @@ export class CredentialsHelper extends ICredentialsHelper {
nodeCredentials: INodeCredentialsDetails,
type: string,
mode: WorkflowExecuteMode,
executeData?: IExecuteData,
raw?: boolean,
expressionResolveValues?: ICredentialsExpressionResolveValues,
): Promise<ICredentialDataDecryptedObject> {
@ -358,6 +360,7 @@ export class CredentialsHelper extends ICredentialsHelper {
decryptedDataOriginal,
type,
mode,
executeData,
expressionResolveValues,
canUseSecrets,
);
@ -371,6 +374,7 @@ export class CredentialsHelper extends ICredentialsHelper {
decryptedDataOriginal: ICredentialDataDecryptedObject,
type: string,
mode: WorkflowExecuteMode,
executeData?: IExecuteData,
expressionResolveValues?: ICredentialsExpressionResolveValues,
canUseSecrets?: boolean,
): ICredentialDataDecryptedObject {
@ -412,7 +416,7 @@ export class CredentialsHelper extends ICredentialsHelper {
expressionResolveValues.connectionInputData,
mode,
additionalKeys,
undefined,
executeData,
false,
decryptedData,
) as ICredentialDataDecryptedObject;
@ -579,6 +583,7 @@ export class CredentialsHelper extends ICredentialsHelper {
credentialType,
'internal' as WorkflowExecuteMode,
undefined,
undefined,
user.hasGlobalScope('externalSecret:use'),
);
} catch (error) {

View file

@ -73,6 +73,7 @@ export abstract class AbstractOAuthController {
credential,
credential.type,
'internal',
undefined,
true,
);
}

View file

@ -107,6 +107,7 @@ export class MessageEventBusDestinationWebhook
foundCredential[1],
foundCredential[0],
'internal',
undefined,
true,
);
return credentialsDecrypted;

View file

@ -1882,6 +1882,7 @@ export async function getCredentials(
type: string,
additionalData: IWorkflowExecuteAdditionalData,
mode: WorkflowExecuteMode,
executeData?: IExecuteData,
runExecutionData?: IRunExecutionData | null,
runIndex?: number,
connectionInputData?: INodeExecutionData[],
@ -2001,6 +2002,7 @@ export async function getCredentials(
nodeCredentials,
type,
mode,
executeData,
false,
expressionResolveValues,
);
@ -3149,6 +3151,7 @@ export function getExecuteFunctions(
type,
additionalData,
mode,
executeData,
runExecutionData,
runIndex,
connectionInputData,
@ -3281,6 +3284,7 @@ export function getExecuteFunctions(
key,
additionalData,
mode,
executeData,
runExecutionData,
runIndex,
connectionInputData,
@ -3608,6 +3612,7 @@ export function getExecuteSingleFunctions(
type,
additionalData,
mode,
executeData,
runExecutionData,
runIndex,
connectionInputData,

View file

@ -1221,44 +1221,6 @@ export class HttpRequestV3 implements INodeType {
let nodeCredentialType: string | undefined;
let genericCredentialType: string | undefined;
if (authentication === 'genericCredentialType') {
genericCredentialType = this.getNodeParameter('genericAuthType', 0) as string;
if (genericCredentialType === 'httpBasicAuth') {
try {
httpBasicAuth = await this.getCredentials('httpBasicAuth');
} catch {}
} else if (genericCredentialType === 'httpDigestAuth') {
try {
httpDigestAuth = await this.getCredentials('httpDigestAuth');
} catch {}
} else if (genericCredentialType === 'httpHeaderAuth') {
try {
httpHeaderAuth = await this.getCredentials('httpHeaderAuth');
} catch {}
} else if (genericCredentialType === 'httpQueryAuth') {
try {
httpQueryAuth = await this.getCredentials('httpQueryAuth');
} catch {}
} else if (genericCredentialType === 'httpCustomAuth') {
try {
httpCustomAuth = await this.getCredentials('httpCustomAuth');
} catch {}
} else if (genericCredentialType === 'oAuth1Api') {
try {
oAuth1Api = await this.getCredentials('oAuth1Api');
} catch {}
} else if (genericCredentialType === 'oAuth2Api') {
try {
oAuth2Api = await this.getCredentials('oAuth2Api');
} catch {}
}
} else if (authentication === 'predefinedCredentialType') {
try {
nodeCredentialType = this.getNodeParameter('nodeCredentialType', 0) as string;
} catch {}
}
type RequestOptions = OptionsWithUri & { useStream?: boolean };
let requestOptions: RequestOptions = {
uri: '',
@ -1293,6 +1255,44 @@ export class HttpRequestV3 implements INodeType {
};
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
if (authentication === 'genericCredentialType') {
genericCredentialType = this.getNodeParameter('genericAuthType', 0) as string;
if (genericCredentialType === 'httpBasicAuth') {
try {
httpBasicAuth = await this.getCredentials('httpBasicAuth', itemIndex);
} catch {}
} else if (genericCredentialType === 'httpDigestAuth') {
try {
httpDigestAuth = await this.getCredentials('httpDigestAuth', itemIndex);
} catch {}
} else if (genericCredentialType === 'httpHeaderAuth') {
try {
httpHeaderAuth = await this.getCredentials('httpHeaderAuth', itemIndex);
} catch {}
} else if (genericCredentialType === 'httpQueryAuth') {
try {
httpQueryAuth = await this.getCredentials('httpQueryAuth', itemIndex);
} catch {}
} else if (genericCredentialType === 'httpCustomAuth') {
try {
httpCustomAuth = await this.getCredentials('httpCustomAuth', itemIndex);
} catch {}
} else if (genericCredentialType === 'oAuth1Api') {
try {
oAuth1Api = await this.getCredentials('oAuth1Api', itemIndex);
} catch {}
} else if (genericCredentialType === 'oAuth2Api') {
try {
oAuth2Api = await this.getCredentials('oAuth2Api', itemIndex);
} catch {}
}
} else if (authentication === 'predefinedCredentialType') {
try {
nodeCredentialType = this.getNodeParameter('nodeCredentialType', 0) as string;
} catch {}
}
const requestMethod = this.getNodeParameter('method', itemIndex) as string;
const sendQuery = this.getNodeParameter('sendQuery', itemIndex, false) as boolean;

View file

@ -217,6 +217,7 @@ export abstract class ICredentialsHelper {
nodeCredentials: INodeCredentialsDetails,
type: string,
mode: WorkflowExecuteMode,
executeData?: IExecuteData,
raw?: boolean,
expressionResolveValues?: ICredentialsExpressionResolveValues,
): Promise<ICredentialDataDecryptedObject>;