fix(editor): Condition to show the warning after workflow activation for free AI credits (no-changelog) (#12518)

This commit is contained in:
Ricardo Espinoza 2025-01-09 05:53:05 -05:00 committed by GitHub
parent 8fab98f3f1
commit 46f13cfca9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 157 additions and 11 deletions

View file

@ -95,17 +95,17 @@ describe('WorkflowActivator', () => {
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();
mockWorkflowsStore.workflow.usedCredentials = [
{
mockWorkflowsStore.usedCredentials = {
'1': {
id: '1',
name: '',
credentialType: '',
currentUserHasAccess: false,
},
];
};
mockCredentialsStore.state.credentials = {
'1': {
@ -123,6 +123,24 @@ describe('WorkflowActivator', () => {
{ 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({
props: {
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();
});
});

View file

@ -10,7 +10,8 @@ import type { PermissionsRecord } from '@/permissions';
import { EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE, PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants';
import WorkflowActivationErrorMessage from './WorkflowActivationErrorMessage.vue';
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<{
workflowActive: boolean;
@ -72,12 +73,39 @@ const disabled = computed((): boolean => {
return false;
});
const currentWorkflowHasFreeAiCredits = computed((): boolean => {
if (!workflowsStore?.workflow?.usedCredentials) return false;
return workflowsStore.workflow.usedCredentials.some(
(usedCredential: IUsedCredential) =>
credentialsStore.state.credentials[usedCredential.id].isManaged,
function findManagedOpenAiCredentialId(
usedCredentials: Record<string, IUsedCredential>,
): string | undefined {
return Object.keys(usedCredentials).find((credentialId) => {
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) {
@ -115,7 +143,7 @@ async function displayActivationError() {
watch(
() => props.workflowActive,
(workflowActive) => {
if (workflowActive && currentWorkflowHasFreeAiCredits.value) {
if (workflowActive && shouldShowFreeAiCreditsWarning.value) {
showMessage({
title: i18n.baseText('freeAi.credits.showWarning.workflow.activation.title'),
message: i18n.baseText('freeAi.credits.showWarning.workflow.activation.description'),