mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-20 11:22:15 +00:00
## Summary Enable users to open credential setup for workflows that have been created from templates if they skip it. Next steps (will be their own PRs): - Add telemetry events - Add e2e test - Hide the button when user sets up all the credentials - Change the feature flag to a new one ## Related tickets and issues https://linear.app/n8n/issue/ADO-1637/feature-support-template-credential-setup-for-http-request-nodes-that
136 lines
4.4 KiB
TypeScript
136 lines
4.4 KiB
TypeScript
import type { IWorkflowTemplateNode, IWorkflowTemplateNodeCredentials } from '@/Interface';
|
|
import type { NodeTypeProvider } from '@/utils/nodeTypes/nodeTypeTransforms';
|
|
import { getNodeTypeDisplayableCredentials } from '@/utils/nodes/nodeTransforms';
|
|
import type { NormalizedTemplateNodeCredentials } from '@/utils/templates/templateTypes';
|
|
import type {
|
|
INodeCredentialDescription,
|
|
INodeCredentials,
|
|
INodeCredentialsDetails,
|
|
} from 'n8n-workflow';
|
|
|
|
export type IWorkflowTemplateNodeWithCredentials = IWorkflowTemplateNode &
|
|
Required<Pick<IWorkflowTemplateNode, 'credentials'>>;
|
|
|
|
const credentialKeySymbol = Symbol('credentialKey');
|
|
|
|
/**
|
|
* A key that uniquely identifies a credential in a template node.
|
|
* It encodes the credential type name and the credential name.
|
|
* Uses a symbol typing trick to get nominal typing.
|
|
* Use `keyFromCredentialTypeAndName` to create a key.
|
|
*/
|
|
export type TemplateCredentialKey = string & { [credentialKeySymbol]: never };
|
|
|
|
export type TemplateNodeWithRequiredCredential = {
|
|
node: IWorkflowTemplateNode;
|
|
requiredCredentials: INodeCredentialDescription[];
|
|
};
|
|
|
|
/**
|
|
* Forms a key from credential type name and credential name
|
|
*/
|
|
export const keyFromCredentialTypeAndName = (
|
|
credentialTypeName: string,
|
|
credentialName: string,
|
|
): TemplateCredentialKey => `${credentialTypeName}-${credentialName}` as TemplateCredentialKey;
|
|
|
|
/**
|
|
* Normalizes the credentials of a template node. Templates created with
|
|
* different versions of n8n may have different credential formats.
|
|
*/
|
|
export const normalizeTemplateNodeCredentials = (
|
|
credentials?: IWorkflowTemplateNodeCredentials,
|
|
): NormalizedTemplateNodeCredentials => {
|
|
if (!credentials) {
|
|
return {};
|
|
}
|
|
|
|
return Object.fromEntries(
|
|
Object.entries(credentials).map(([key, value]) => {
|
|
return typeof value === 'string' ? [key, value] : [key, value.name];
|
|
}),
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Replaces the credentials of a node with the given replacements
|
|
*
|
|
* @example
|
|
* const nodeCredentials = { twitterOAuth1Api: "twitter" };
|
|
* const toReplaceByKey = { 'twitterOAuth1Api-twitter': {
|
|
* id: "BrEOZ5Cje6VYh9Pc",
|
|
* name: "X OAuth account"
|
|
* }};
|
|
* replaceTemplateNodeCredentials(nodeCredentials, toReplaceByKey);
|
|
* // => { twitterOAuth1Api: { id: "BrEOZ5Cje6VYh9Pc", name: "X OAuth account" } }
|
|
*/
|
|
export const getReplacedTemplateNodeCredentials = (
|
|
nodeCredentials: IWorkflowTemplateNodeCredentials | undefined,
|
|
toReplaceByKey: Record<TemplateCredentialKey, INodeCredentialsDetails>,
|
|
) => {
|
|
if (!nodeCredentials) {
|
|
return undefined;
|
|
}
|
|
|
|
const replacedNodeCredentials: INodeCredentials = {};
|
|
const normalizedCredentials = normalizeTemplateNodeCredentials(nodeCredentials);
|
|
for (const credentialType in normalizedCredentials) {
|
|
const credentialNameInTemplate = normalizedCredentials[credentialType];
|
|
const key = keyFromCredentialTypeAndName(credentialType, credentialNameInTemplate);
|
|
const toReplaceWith = toReplaceByKey[key];
|
|
if (toReplaceWith) {
|
|
replacedNodeCredentials[credentialType] = toReplaceWith;
|
|
}
|
|
}
|
|
|
|
return replacedNodeCredentials;
|
|
};
|
|
|
|
/**
|
|
* Returns credentials for the given node that are missing from it
|
|
* but are present in the given replacements
|
|
*/
|
|
export const getMissingTemplateNodeCredentials = (
|
|
nodeTypeProvider: NodeTypeProvider,
|
|
node: IWorkflowTemplateNode,
|
|
replacementsByKey: Record<TemplateCredentialKey, INodeCredentialsDetails>,
|
|
): INodeCredentials => {
|
|
const nodeCredentialsToAdd: INodeCredentials = {};
|
|
const usableCredentials = getNodeTypeDisplayableCredentials(nodeTypeProvider, node);
|
|
|
|
for (const usableCred of usableCredentials) {
|
|
const credentialKey = keyFromCredentialTypeAndName(usableCred.name, '');
|
|
|
|
if (replacementsByKey[credentialKey]) {
|
|
nodeCredentialsToAdd[usableCred.name] = replacementsByKey[credentialKey];
|
|
}
|
|
}
|
|
|
|
return nodeCredentialsToAdd;
|
|
};
|
|
|
|
/**
|
|
* Replaces the credentials of all template workflow nodes with the given
|
|
* replacements
|
|
*/
|
|
export const replaceAllTemplateNodeCredentials = (
|
|
nodeTypeProvider: NodeTypeProvider,
|
|
nodes: IWorkflowTemplateNode[],
|
|
toReplaceWith: Record<TemplateCredentialKey, INodeCredentialsDetails>,
|
|
) => {
|
|
return nodes.map((node) => {
|
|
const replacedCredentials = getReplacedTemplateNodeCredentials(node.credentials, toReplaceWith);
|
|
const newCredentials = getMissingTemplateNodeCredentials(nodeTypeProvider, node, toReplaceWith);
|
|
|
|
const credentials = {
|
|
...replacedCredentials,
|
|
...newCredentials,
|
|
};
|
|
|
|
return {
|
|
...node,
|
|
credentials: Object.keys(credentials).length > 0 ? credentials : undefined,
|
|
};
|
|
});
|
|
};
|