mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(editor): Condition to show the warning after workflow activation for free AI credits (no-changelog) (#12518)
This commit is contained in:
parent
8fab98f3f1
commit
46f13cfca9
|
@ -95,17 +95,17 @@ describe('WorkflowActivator', () => {
|
||||||
expect(getByTestId('workflow-activator-status')).toHaveTextContent('Inactive');
|
expect(getByTestId('workflow-activator-status')).toHaveTextContent('Inactive');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should show warning toast if the workflow to be activated has free OpenAI credentials', async () => {
|
it('Should show warning toast if the workflow to be activated has non-disabled node using free OpenAI credentials', async () => {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
mockWorkflowsStore.workflow.usedCredentials = [
|
mockWorkflowsStore.usedCredentials = {
|
||||||
{
|
'1': {
|
||||||
id: '1',
|
id: '1',
|
||||||
name: '',
|
name: '',
|
||||||
credentialType: '',
|
credentialType: '',
|
||||||
currentUserHasAccess: false,
|
currentUserHasAccess: false,
|
||||||
},
|
},
|
||||||
];
|
};
|
||||||
|
|
||||||
mockCredentialsStore.state.credentials = {
|
mockCredentialsStore.state.credentials = {
|
||||||
'1': {
|
'1': {
|
||||||
|
@ -123,6 +123,24 @@ describe('WorkflowActivator', () => {
|
||||||
{ type: WOOCOMMERCE_TRIGGER_NODE_TYPE, disabled: false } as never,
|
{ type: WOOCOMMERCE_TRIGGER_NODE_TYPE, disabled: false } as never,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
mockWorkflowsStore.allNodes = [
|
||||||
|
{
|
||||||
|
credentials: {
|
||||||
|
openAiApi: {
|
||||||
|
name: 'OpenAI',
|
||||||
|
id: '1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
disabled: false,
|
||||||
|
position: [1, 1],
|
||||||
|
name: '',
|
||||||
|
id: '',
|
||||||
|
typeVersion: 0,
|
||||||
|
type: '',
|
||||||
|
parameters: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const { rerender } = renderComponent({
|
const { rerender } = renderComponent({
|
||||||
props: {
|
props: {
|
||||||
workflowActive: false,
|
workflowActive: false,
|
||||||
|
@ -143,4 +161,104 @@ describe('WorkflowActivator', () => {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Should not show warning toast if the workflow to be activated has disabled node using free OpenAI credentials', async () => {
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
mockWorkflowsStore.usedCredentials = {
|
||||||
|
'1': {
|
||||||
|
id: '1',
|
||||||
|
name: '',
|
||||||
|
credentialType: '',
|
||||||
|
currentUserHasAccess: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockCredentialsStore.state.credentials = {
|
||||||
|
'1': {
|
||||||
|
id: '1',
|
||||||
|
name: 'OpenAI',
|
||||||
|
type: 'openAiApi',
|
||||||
|
data: '',
|
||||||
|
isManaged: true,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockWorkflowsStore.workflowTriggerNodes = [
|
||||||
|
{ type: WOOCOMMERCE_TRIGGER_NODE_TYPE, disabled: false } as never,
|
||||||
|
];
|
||||||
|
|
||||||
|
mockWorkflowsStore.allNodes = [
|
||||||
|
{
|
||||||
|
credentials: {
|
||||||
|
openAiApi: {
|
||||||
|
name: 'OpenAI',
|
||||||
|
id: '1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
disabled: true,
|
||||||
|
position: [1, 1],
|
||||||
|
name: '',
|
||||||
|
id: '',
|
||||||
|
typeVersion: 0,
|
||||||
|
type: '',
|
||||||
|
parameters: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { rerender } = renderComponent({
|
||||||
|
props: {
|
||||||
|
workflowActive: false,
|
||||||
|
workflowId: '1',
|
||||||
|
workflowPermissions: { update: true },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await rerender({ workflowActive: true });
|
||||||
|
|
||||||
|
expect(toast.showMessage).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should not show warning toast if the workflow to be activated has no node with free OpenAI credential', async () => {
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
mockWorkflowsStore.usedCredentials = {
|
||||||
|
'1': {
|
||||||
|
id: '1',
|
||||||
|
name: '',
|
||||||
|
credentialType: '',
|
||||||
|
currentUserHasAccess: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockCredentialsStore.state.credentials = {
|
||||||
|
'1': {
|
||||||
|
id: '1',
|
||||||
|
name: 'Jira',
|
||||||
|
type: 'jiraApi',
|
||||||
|
data: '',
|
||||||
|
isManaged: true,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockWorkflowsStore.workflowTriggerNodes = [
|
||||||
|
{ type: WOOCOMMERCE_TRIGGER_NODE_TYPE, disabled: false } as never,
|
||||||
|
];
|
||||||
|
|
||||||
|
const { rerender } = renderComponent({
|
||||||
|
props: {
|
||||||
|
workflowActive: false,
|
||||||
|
workflowId: '1',
|
||||||
|
workflowPermissions: { update: true },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await rerender({ workflowActive: true });
|
||||||
|
|
||||||
|
expect(toast.showMessage).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,7 +10,8 @@ import type { PermissionsRecord } from '@/permissions';
|
||||||
import { EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE, PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants';
|
import { EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE, PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants';
|
||||||
import WorkflowActivationErrorMessage from './WorkflowActivationErrorMessage.vue';
|
import WorkflowActivationErrorMessage from './WorkflowActivationErrorMessage.vue';
|
||||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||||
import type { IUsedCredential } from '@/Interface';
|
import type { INodeUi, IUsedCredential } from '@/Interface';
|
||||||
|
import { OPEN_AI_API_CREDENTIAL_TYPE } from 'n8n-workflow';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
workflowActive: boolean;
|
workflowActive: boolean;
|
||||||
|
@ -72,12 +73,39 @@ const disabled = computed((): boolean => {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentWorkflowHasFreeAiCredits = computed((): boolean => {
|
function findManagedOpenAiCredentialId(
|
||||||
if (!workflowsStore?.workflow?.usedCredentials) return false;
|
usedCredentials: Record<string, IUsedCredential>,
|
||||||
return workflowsStore.workflow.usedCredentials.some(
|
): string | undefined {
|
||||||
(usedCredential: IUsedCredential) =>
|
return Object.keys(usedCredentials).find((credentialId) => {
|
||||||
credentialsStore.state.credentials[usedCredential.id].isManaged,
|
const credential = credentialsStore.state.credentials[credentialId];
|
||||||
|
return credential.isManaged && credential.type === OPEN_AI_API_CREDENTIAL_TYPE;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasActiveNodeUsingCredential(nodes: INodeUi[], credentialId: string): boolean {
|
||||||
|
return nodes.some(
|
||||||
|
(node) =>
|
||||||
|
node?.credentials?.[OPEN_AI_API_CREDENTIAL_TYPE]?.id === credentialId && !node.disabled,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the warning for free AI credits should be shown in the workflow.
|
||||||
|
*
|
||||||
|
* This computed property evaluates whether to display a warning about free AI credits
|
||||||
|
* in the workflow. The warning is shown when both conditions are met:
|
||||||
|
* 1. The workflow uses managed OpenAI API credentials
|
||||||
|
* 2. Those credentials are associated with at least one enabled node
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const shouldShowFreeAiCreditsWarning = computed((): boolean => {
|
||||||
|
const usedCredentials = workflowsStore?.usedCredentials;
|
||||||
|
if (!usedCredentials) return false;
|
||||||
|
|
||||||
|
const managedOpenAiCredentialId = findManagedOpenAiCredentialId(usedCredentials);
|
||||||
|
if (!managedOpenAiCredentialId) return false;
|
||||||
|
|
||||||
|
return hasActiveNodeUsingCredential(workflowsStore.allNodes, managedOpenAiCredentialId);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function activeChanged(newActiveState: boolean) {
|
async function activeChanged(newActiveState: boolean) {
|
||||||
|
@ -115,7 +143,7 @@ async function displayActivationError() {
|
||||||
watch(
|
watch(
|
||||||
() => props.workflowActive,
|
() => props.workflowActive,
|
||||||
(workflowActive) => {
|
(workflowActive) => {
|
||||||
if (workflowActive && currentWorkflowHasFreeAiCredits.value) {
|
if (workflowActive && shouldShowFreeAiCreditsWarning.value) {
|
||||||
showMessage({
|
showMessage({
|
||||||
title: i18n.baseText('freeAi.credits.showWarning.workflow.activation.title'),
|
title: i18n.baseText('freeAi.credits.showWarning.workflow.activation.title'),
|
||||||
message: i18n.baseText('freeAi.credits.showWarning.workflow.activation.description'),
|
message: i18n.baseText('freeAi.credits.showWarning.workflow.activation.description'),
|
||||||
|
|
Loading…
Reference in a new issue