diff --git a/packages/cli/src/CredentialsHelper.ts b/packages/cli/src/CredentialsHelper.ts index 0d20753ff9..4f57bae4dd 100644 --- a/packages/cli/src/CredentialsHelper.ts +++ b/packages/cli/src/CredentialsHelper.ts @@ -137,10 +137,7 @@ export class CredentialsHelper extends ICredentialsHelper { if (expressionResolveValues) { try { const workflow = new Workflow({ nodes: Object.values(expressionResolveValues.workflow.nodes), connections: expressionResolveValues.workflow.connectionsBySourceNode, active: false, nodeTypes: expressionResolveValues.workflow.nodeTypes }); - // TODO: Find a better way for that! - // Add the credential data to the parameters of the node so that they can get accessed by expressions - Object.assign(workflow.nodes[expressionResolveValues.node.name].parameters, decryptedData); - decryptedData = workflow.expression.getParameterValue(decryptedData as INodeParameters, expressionResolveValues.runExecutionData, expressionResolveValues.runIndex, expressionResolveValues.itemIndex, expressionResolveValues.node.name, expressionResolveValues.connectionInputData) as ICredentialDataDecryptedObject; + decryptedData = workflow.expression.getParameterValue(decryptedData as INodeParameters, expressionResolveValues.runExecutionData, expressionResolveValues.runIndex, expressionResolveValues.itemIndex, expressionResolveValues.node.name, expressionResolveValues.connectionInputData, false, decryptedData) as ICredentialDataDecryptedObject; } catch (e) { e.message += ' [Error resolving credentials]'; throw e; @@ -151,13 +148,13 @@ export class CredentialsHelper extends ICredentialsHelper { typeVersion: 1, type: 'mock', position: [0, 0], - parameters: decryptedData as INodeParameters, + parameters: {} as INodeParameters, } as INode; const workflow = new Workflow({ nodes: [node!], connections: {}, active: false, nodeTypes: mockNodeTypes }); // Resolve expressions if any are set - decryptedData = workflow.expression.getComplexParameterValue(node!, decryptedData as INodeParameters, undefined) as ICredentialDataDecryptedObject; + decryptedData = workflow.expression.getComplexParameterValue(node!, decryptedData as INodeParameters, undefined, decryptedData) as ICredentialDataDecryptedObject; } // Load and apply the credentials overwrites if any exist diff --git a/packages/nodes-base/credentials/GithubOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GithubOAuth2Api.credentials.ts index ed5238353b..88a1ab0fd0 100644 --- a/packages/nodes-base/credentials/GithubOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/GithubOAuth2Api.credentials.ts @@ -23,14 +23,14 @@ export class GithubOAuth2Api implements ICredentialType { displayName: 'Authorization URL', name: 'authUrl', type: 'hidden' as NodePropertyTypes, - default: '={{$parameter["server"] === "https://api.github.com" ? "https://github.com" : $parameter["server"]}}/login/oauth/authorize', + default: '={{$self["server"] === "https://api.github.com" ? "https://github.com" : $self["server"]}}/login/oauth/authorize', required: true, }, { displayName: 'Access Token URL', name: 'accessTokenUrl', type: 'hidden' as NodePropertyTypes, - default: '={{$parameter["server"] === "https://api.github.com" ? "https://github.com" : $parameter["server"]}}/login/oauth/access_token', + default: '={{$self["server"] === "https://api.github.com" ? "https://github.com" : $self["server"]}}/login/oauth/access_token', required: true, }, { diff --git a/packages/nodes-base/credentials/GitlabOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GitlabOAuth2Api.credentials.ts index 21a17cb2d1..8a1afbfd7a 100644 --- a/packages/nodes-base/credentials/GitlabOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/GitlabOAuth2Api.credentials.ts @@ -22,14 +22,14 @@ export class GitlabOAuth2Api implements ICredentialType { displayName: 'Authorization URL', name: 'authUrl', type: 'hidden' as NodePropertyTypes, - default: '={{$parameter["server"]}}/oauth/authorize', + default: '={{$self["server"]}}/oauth/authorize', required: true, }, { displayName: 'Access Token URL', name: 'accessTokenUrl', type: 'hidden' as NodePropertyTypes, - default: '={{$parameter["server"]}}/oauth/token', + default: '={{$self["server"]}}/oauth/token', required: true, }, { diff --git a/packages/nodes-base/credentials/LinkedInOAuth2Api.credentials.ts b/packages/nodes-base/credentials/LinkedInOAuth2Api.credentials.ts index bad0c3d830..0e8ffb2086 100644 --- a/packages/nodes-base/credentials/LinkedInOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/LinkedInOAuth2Api.credentials.ts @@ -37,7 +37,7 @@ export class LinkedInOAuth2Api implements ICredentialType { displayName: 'Scope', name: 'scope', type: 'hidden' as NodePropertyTypes, - default: '=r_liteprofile,r_emailaddress,w_member_social{{$parameter["organizationSupport"] === true ? ",w_organization_social":""}}', + default: '=r_liteprofile,r_emailaddress,w_member_social{{$self["organizationSupport"] === true ? ",w_organization_social":""}}', description: 'Standard scopes for posting on behalf of a user or organization. See this resource .', }, { diff --git a/packages/nodes-base/credentials/MauticOAuth2Api.credentials.ts b/packages/nodes-base/credentials/MauticOAuth2Api.credentials.ts index a5f658205a..6b8949c935 100644 --- a/packages/nodes-base/credentials/MauticOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/MauticOAuth2Api.credentials.ts @@ -22,14 +22,14 @@ export class MauticOAuth2Api implements ICredentialType { displayName: 'Authorization URL', name: 'authUrl', type: 'hidden' as NodePropertyTypes, - default: '={{$parameter["url"]}}/oauth/v2/authorize', + default: '={{$self["url"]}}/oauth/v2/authorize', required: true, }, { displayName: 'Access Token URL', name: 'accessTokenUrl', type: 'hidden' as NodePropertyTypes, - default: '={{$parameter["url"]}}/oauth/v2/token', + default: '={{$self["url"]}}/oauth/v2/token', required: true, }, { diff --git a/packages/nodes-base/credentials/PhilipsHueOAuth2Api.credentials.ts b/packages/nodes-base/credentials/PhilipsHueOAuth2Api.credentials.ts index f2f31fe582..519800f494 100644 --- a/packages/nodes-base/credentials/PhilipsHueOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/PhilipsHueOAuth2Api.credentials.ts @@ -33,7 +33,7 @@ export class PhilipsHueOAuth2Api implements ICredentialType { displayName: 'Auth URI Query Parameters', name: 'authQueryParameters', type: 'hidden' as NodePropertyTypes, - default: '={{"appid="+$parameter["appId"]}}', + default: '={{"appid="+$self["appId"]}}', }, { displayName: 'Scope', diff --git a/packages/nodes-base/credentials/ZendeskOAuth2Api.credentials.ts b/packages/nodes-base/credentials/ZendeskOAuth2Api.credentials.ts index f57ed33c39..a835d1072b 100644 --- a/packages/nodes-base/credentials/ZendeskOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/ZendeskOAuth2Api.credentials.ts @@ -29,7 +29,7 @@ export class ZendeskOAuth2Api implements ICredentialType { displayName: 'Authorization URL', name: 'authUrl', type: 'hidden' as NodePropertyTypes, - default: '=https://{{$parameter["subdomain"]}}.zendesk.com/oauth/authorizations/new', + default: '=https://{{$self["subdomain"]}}.zendesk.com/oauth/authorizations/new', description: 'URL to get authorization code. Replace {SUBDOMAIN_HERE} with your subdomain.', required: true, }, @@ -37,7 +37,7 @@ export class ZendeskOAuth2Api implements ICredentialType { displayName: 'Access Token URL', name: 'accessTokenUrl', type: 'hidden' as NodePropertyTypes, - default: '=https://{{$parameter["subdomain"]}}.zendesk.com/oauth/tokens', + default: '=https://{{$self["subdomain"]}}.zendesk.com/oauth/tokens', description: 'URL to get access token. Replace {SUBDOMAIN_HERE} with your subdomain.', required: true, }, diff --git a/packages/workflow/src/Expression.ts b/packages/workflow/src/Expression.ts index 7ad9a84ee9..1ae4234250 100644 --- a/packages/workflow/src/Expression.ts +++ b/packages/workflow/src/Expression.ts @@ -1,5 +1,6 @@ import { + IDataObject, INode, INodeExecutionData, INodeParameters, @@ -58,7 +59,7 @@ export class Expression { * @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])} * @memberof Workflow */ - resolveSimpleParameterValue(parameterValue: NodeParameterValue, runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], returnObjectAsString = false): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] { + resolveSimpleParameterValue(parameterValue: NodeParameterValue, runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], returnObjectAsString = false, selfData = {}): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] { // Check if it is an expression if (typeof parameterValue !== 'string' || parameterValue.charAt(0) !== '=') { // Is no expression so return value @@ -71,7 +72,7 @@ export class Expression { parameterValue = parameterValue.substr(1); // Generate a data proxy which allows to query workflow data - const dataProxy = new WorkflowDataProxy(this.workflow, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData); + const dataProxy = new WorkflowDataProxy(this.workflow, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, -1, selfData); const data = dataProxy.getDataProxy(); // Execute the expression @@ -131,7 +132,7 @@ export class Expression { * @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined)} * @memberof Workflow */ - getComplexParameterValue(node: INode, parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], defaultValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined = undefined): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined { + getComplexParameterValue(node: INode, parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], defaultValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined = undefined, selfData = {}): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined { if (parameterValue === undefined) { // Value is not set so return the default return defaultValue; @@ -148,10 +149,10 @@ export class Expression { }; // Resolve the "outer" main values - const returnData = this.getParameterValue(parameterValue, runData, runIndex, itemIndex, node.name, connectionInputData); + const returnData = this.getParameterValue(parameterValue, runData, runIndex, itemIndex, node.name, connectionInputData, false, selfData); // Resolve the "inner" values - return this.getParameterValue(returnData, runData, runIndex, itemIndex, node.name, connectionInputData); + return this.getParameterValue(returnData, runData, runIndex, itemIndex, node.name, connectionInputData, false, selfData); } @@ -171,7 +172,7 @@ export class Expression { * @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])} * @memberof Workflow */ - getParameterValue(parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], returnObjectAsString = false): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] { + getParameterValue(parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], returnObjectAsString = false, selfData = {}): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] { // Helper function which returns true when the parameter is a complex one or array const isComplexParameter = (value: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[]) => { return typeof value === 'object'; @@ -180,15 +181,15 @@ export class Expression { // Helper function which resolves a parameter value depending on if it is simply or not const resolveParameterValue = (value: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[]) => { if (isComplexParameter(value)) { - return this.getParameterValue(value, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString); + return this.getParameterValue(value, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString, selfData); } else { - return this.resolveSimpleParameterValue(value as NodeParameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString); + return this.resolveSimpleParameterValue(value as NodeParameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString, selfData); } }; // Check if it value is a simple one that we can get it resolved directly if (!isComplexParameter(parameterValue)) { - return this.resolveSimpleParameterValue(parameterValue as NodeParameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString); + return this.resolveSimpleParameterValue(parameterValue as NodeParameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString, selfData); } // The parameter value is complex so resolve depending on type diff --git a/packages/workflow/src/WorkflowDataProxy.ts b/packages/workflow/src/WorkflowDataProxy.ts index 6ba756e022..1b44f0342b 100644 --- a/packages/workflow/src/WorkflowDataProxy.ts +++ b/packages/workflow/src/WorkflowDataProxy.ts @@ -17,10 +17,11 @@ export class WorkflowDataProxy { private itemIndex: number; private activeNodeName: string; private connectionInputData: INodeExecutionData[]; + private selfData: IDataObject; - constructor(workflow: Workflow, runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], defaultReturnRunIndex = -1) { + constructor(workflow: Workflow, runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], defaultReturnRunIndex = -1, selfData = {}) { this.workflow = workflow; this.runExecutionData = runExecutionData; this.defaultReturnRunIndex = defaultReturnRunIndex; @@ -28,6 +29,7 @@ export class WorkflowDataProxy { this.itemIndex = itemIndex; this.activeNodeName = activeNodeName; this.connectionInputData = connectionInputData; + this.selfData = selfData; } @@ -69,6 +71,21 @@ export class WorkflowDataProxy { + private selfGetter() { + const that = this; + + return new Proxy({}, { + ownKeys(target) { + return Reflect.ownKeys(target); + }, + get(target, name, receiver) { + name = name.toString(); + return that.selfData[name]; + }, + }); + } + + /** * Returns a proxy which allows to query parameter data of a given node * @@ -359,6 +376,7 @@ export class WorkflowDataProxy { }, $json: {}, // Placeholder $node: this.nodeGetter(), + $self: this.selfGetter(), $parameter: this.nodeParameterGetter(this.activeNodeName), $runIndex: this.runIndex, $workflow: this.workflowGetter(),