mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
refactor: Simplify agent request store (#15743)
This commit is contained in:
7
packages/frontend/@n8n/stores/src/__tests__/setup.ts
Normal file
7
packages/frontend/@n8n/stores/src/__tests__/setup.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import { configure } from '@testing-library/vue';
|
||||
|
||||
// Avoid tests failing because of difference between local and GitHub actions timezone
|
||||
process.env.TZ = 'UTC';
|
||||
|
||||
configure({ testIdAttribute: 'data-test-id' });
|
||||
@@ -2,208 +2,180 @@ import { setActivePinia, createPinia } from 'pinia';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
import { useAgentRequestStore } from './useAgentRequestStore';
|
||||
import {
|
||||
type IAgentRequestStoreState,
|
||||
type IAgentRequest,
|
||||
useAgentRequestStore,
|
||||
} from './useAgentRequestStore';
|
||||
|
||||
// Mock localStorage
|
||||
const localStorageMock = {
|
||||
getItem: vi.fn(),
|
||||
setItem: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
};
|
||||
let mockLocalStorageValue: IAgentRequestStoreState = {};
|
||||
|
||||
Object.defineProperty(window, 'localStorage', {
|
||||
value: localStorageMock,
|
||||
writable: true,
|
||||
});
|
||||
const NODE_ID_1 = '123e4567-e89b-12d3-a456-426614174000';
|
||||
const NODE_ID_2 = '987fcdeb-51a2-43d7-b654-987654321000';
|
||||
const NODE_ID_3 = '456abcde-f789-12d3-a456-426614174000';
|
||||
|
||||
describe('parameterOverrides.store', () => {
|
||||
vi.mock('@vueuse/core', () => ({
|
||||
useLocalStorage: vi.fn((_key, defaultValue) => {
|
||||
if (Object.keys(mockLocalStorageValue).length === 0) {
|
||||
Object.assign(mockLocalStorageValue, structuredClone(defaultValue));
|
||||
}
|
||||
return {
|
||||
value: mockLocalStorageValue,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('agentRequest.store', () => {
|
||||
beforeEach(() => {
|
||||
mockLocalStorageValue = {};
|
||||
setActivePinia(createPinia());
|
||||
localStorageMock.getItem.mockReset();
|
||||
localStorageMock.setItem.mockReset();
|
||||
localStorageMock.clear.mockReset();
|
||||
});
|
||||
|
||||
describe('Initialization', () => {
|
||||
it('initializes with empty state when localStorage is empty', () => {
|
||||
localStorageMock.getItem.mockReturnValue(null);
|
||||
const store = useAgentRequestStore();
|
||||
expect(store.agentRequests).toEqual({});
|
||||
expect(store.agentRequests.value).toEqual({});
|
||||
});
|
||||
|
||||
it('initializes with data from localStorage', () => {
|
||||
const mockData = {
|
||||
const mockData: IAgentRequestStoreState = {
|
||||
'workflow-1': {
|
||||
'node-1': { param1: 'value1' },
|
||||
[NODE_ID_1]: { query: { param1: 'value1' } },
|
||||
},
|
||||
};
|
||||
localStorageMock.getItem.mockReturnValue(JSON.stringify(mockData));
|
||||
const store = useAgentRequestStore();
|
||||
expect(store.agentRequests).toEqual(mockData);
|
||||
});
|
||||
mockLocalStorageValue = mockData;
|
||||
|
||||
it('handles localStorage errors gracefully', () => {
|
||||
localStorageMock.getItem.mockImplementation(() => {
|
||||
throw new Error('Storage error');
|
||||
});
|
||||
const store = useAgentRequestStore();
|
||||
expect(store.agentRequests).toEqual({});
|
||||
expect(store.agentRequests.value).toEqual(mockData);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Getters', () => {
|
||||
it('gets parameter overrides for a node', () => {
|
||||
const mockData = {
|
||||
'workflow-1': {
|
||||
'node-1': { param1: 'value1', param2: 'value2' },
|
||||
},
|
||||
};
|
||||
localStorageMock.getItem.mockReturnValue(JSON.stringify(mockData));
|
||||
const store = useAgentRequestStore();
|
||||
|
||||
const overrides = store.getAgentRequests('workflow-1', 'node-1');
|
||||
store.setAgentRequestForNode('workflow-1', NODE_ID_1, {
|
||||
query: { param1: 'value1', param2: 'value2' },
|
||||
});
|
||||
|
||||
const overrides = store.getAgentRequests('workflow-1', NODE_ID_1);
|
||||
expect(overrides).toEqual({ param1: 'value1', param2: 'value2' });
|
||||
});
|
||||
|
||||
it('returns empty object for non-existent workflow/node', () => {
|
||||
const store = useAgentRequestStore();
|
||||
|
||||
const overrides = store.getAgentRequests('non-existent', 'node-1');
|
||||
const overrides = store.getAgentRequests('non-existent', NODE_ID_1);
|
||||
expect(overrides).toEqual({});
|
||||
});
|
||||
|
||||
it('gets a specific parameter override', () => {
|
||||
const mockData = {
|
||||
'workflow-1': {
|
||||
'node-1': { param1: 'value1', param2: 'value2' },
|
||||
},
|
||||
};
|
||||
localStorageMock.getItem.mockReturnValue(JSON.stringify(mockData));
|
||||
const store = useAgentRequestStore();
|
||||
store.setAgentRequestForNode('workflow-1', NODE_ID_1, {
|
||||
query: { param1: 'value1', param2: 'value2' },
|
||||
});
|
||||
|
||||
const override = store.getAgentRequest('workflow-1', 'node-1', 'param1');
|
||||
const override = store.getQueryValue('workflow-1', NODE_ID_1, 'param1');
|
||||
expect(override).toBe('value1');
|
||||
});
|
||||
|
||||
it('returns undefined for non-existent parameter', () => {
|
||||
const mockData = {
|
||||
'workflow-1': {
|
||||
'node-1': { param1: 'value1' },
|
||||
},
|
||||
};
|
||||
localStorageMock.getItem.mockReturnValue(JSON.stringify(mockData));
|
||||
const store = useAgentRequestStore();
|
||||
store.setAgentRequestForNode('workflow-1', NODE_ID_1, {
|
||||
query: { param1: 'value1' },
|
||||
});
|
||||
|
||||
const override = store.getAgentRequest('workflow-1', 'node-1', 'non-existent');
|
||||
const override = store.getQueryValue('workflow-1', NODE_ID_1, 'non-existent');
|
||||
expect(override).toBeUndefined();
|
||||
});
|
||||
|
||||
it('handles string query type', () => {
|
||||
const store = useAgentRequestStore();
|
||||
store.setAgentRequestForNode('workflow-1', NODE_ID_1, {
|
||||
query: 'string-query',
|
||||
});
|
||||
|
||||
const query = store.getAgentRequests('workflow-1', NODE_ID_1);
|
||||
expect(query).toBe('string-query');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Actions', () => {
|
||||
it('adds a parameter override', () => {
|
||||
it('sets parameter overrides for a node', () => {
|
||||
const store = useAgentRequestStore();
|
||||
|
||||
store.addAgentRequest('workflow-1', 'node-1', 'param1', 'value1');
|
||||
|
||||
expect(store.agentRequests['workflow-1']['node-1'].param1).toBe('value1');
|
||||
});
|
||||
|
||||
it('adds multiple parameter overrides', () => {
|
||||
const store = useAgentRequestStore();
|
||||
|
||||
store.addAgentRequests('workflow-1', 'node-1', {
|
||||
param1: 'value1',
|
||||
param2: 'value2',
|
||||
store.setAgentRequestForNode('workflow-1', NODE_ID_1, {
|
||||
query: { param1: 'value1', param2: 'value2' },
|
||||
});
|
||||
|
||||
expect(store.agentRequests['workflow-1']['node-1']).toEqual({
|
||||
expect(
|
||||
(store.agentRequests.value['workflow-1'] as unknown as { [key: string]: IAgentRequest })[
|
||||
NODE_ID_1
|
||||
].query,
|
||||
).toEqual({
|
||||
param1: 'value1',
|
||||
param2: 'value2',
|
||||
});
|
||||
});
|
||||
|
||||
it('clears parameter overrides for a node', () => {
|
||||
const mockData = {
|
||||
'workflow-1': {
|
||||
'node-1': { param1: 'value1', param2: 'value2' },
|
||||
'node-2': { param3: 'value3' },
|
||||
},
|
||||
};
|
||||
localStorageMock.getItem.mockReturnValue(JSON.stringify(mockData));
|
||||
const store = useAgentRequestStore();
|
||||
store.setAgentRequestForNode('workflow-1', NODE_ID_1, {
|
||||
query: { param1: 'value1', param2: 'value2' },
|
||||
});
|
||||
store.setAgentRequestForNode('workflow-1', NODE_ID_2, {
|
||||
query: { param3: 'value3' },
|
||||
});
|
||||
|
||||
store.clearAgentRequests('workflow-1', 'node-1');
|
||||
store.clearAgentRequests('workflow-1', NODE_ID_1);
|
||||
|
||||
expect(store.agentRequests['workflow-1']['node-1']).toEqual({});
|
||||
expect(store.agentRequests['workflow-1']['node-2']).toEqual({ param3: 'value3' });
|
||||
expect(
|
||||
(store.agentRequests.value['workflow-1'] as unknown as { [key: string]: IAgentRequest })[
|
||||
NODE_ID_1
|
||||
].query,
|
||||
).toEqual({});
|
||||
expect(
|
||||
(store.agentRequests.value['workflow-1'] as unknown as { [key: string]: IAgentRequest })[
|
||||
NODE_ID_2
|
||||
].query,
|
||||
).toEqual({
|
||||
param3: 'value3',
|
||||
});
|
||||
});
|
||||
|
||||
it('clears all parameter overrides for a workflow', () => {
|
||||
const mockData = {
|
||||
'workflow-1': {
|
||||
'node-1': { param1: 'value1' },
|
||||
'node-2': { param2: 'value2' },
|
||||
},
|
||||
'workflow-2': {
|
||||
'node-3': { param3: 'value3' },
|
||||
},
|
||||
};
|
||||
localStorageMock.getItem.mockReturnValue(JSON.stringify(mockData));
|
||||
const store = useAgentRequestStore();
|
||||
store.setAgentRequestForNode('workflow-1', NODE_ID_1, {
|
||||
query: { param1: 'value1' },
|
||||
});
|
||||
store.setAgentRequestForNode('workflow-1', NODE_ID_2, {
|
||||
query: { param2: 'value2' },
|
||||
});
|
||||
store.setAgentRequestForNode('workflow-2', NODE_ID_3, {
|
||||
query: { param3: 'value3' },
|
||||
});
|
||||
|
||||
store.clearAllAgentRequests('workflow-1');
|
||||
|
||||
expect(store.agentRequests['workflow-1']).toEqual({});
|
||||
expect(store.agentRequests['workflow-2']).toEqual({
|
||||
'node-3': { param3: 'value3' },
|
||||
expect(store.agentRequests.value['workflow-1']).toEqual({});
|
||||
expect(store.agentRequests.value['workflow-2']).toEqual({
|
||||
[NODE_ID_3]: { query: { param3: 'value3' } },
|
||||
});
|
||||
});
|
||||
|
||||
it('clears all parameter overrides when no workflowId is provided', () => {
|
||||
const mockData = {
|
||||
'workflow-1': {
|
||||
'node-1': { param1: 'value1' },
|
||||
},
|
||||
'workflow-2': {
|
||||
'node-2': { param2: 'value2' },
|
||||
},
|
||||
};
|
||||
localStorageMock.getItem.mockReturnValue(JSON.stringify(mockData));
|
||||
const store = useAgentRequestStore();
|
||||
store.setAgentRequestForNode('workflow-1', NODE_ID_1, {
|
||||
query: { param1: 'value1' },
|
||||
});
|
||||
store.setAgentRequestForNode('workflow-2', NODE_ID_2, {
|
||||
query: { param2: 'value2' },
|
||||
});
|
||||
|
||||
store.clearAllAgentRequests();
|
||||
|
||||
expect(store.agentRequests).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateAgentRequest', () => {
|
||||
it('generateAgentRequest', () => {
|
||||
const store = useAgentRequestStore();
|
||||
|
||||
store.addAgentRequests('workflow-1', 'id1', {
|
||||
param1: 'override1',
|
||||
'parent.child': 'override2',
|
||||
'parent.array[0].value': 'overrideArray1',
|
||||
'parent.array[1].value': 'overrideArray2',
|
||||
});
|
||||
|
||||
const result = store.generateAgentRequest('workflow-1', 'id1');
|
||||
|
||||
expect(result).toEqual({
|
||||
param1: 'override1',
|
||||
parent: {
|
||||
child: 'override2',
|
||||
array: [
|
||||
{
|
||||
value: 'overrideArray1',
|
||||
},
|
||||
{
|
||||
value: 'overrideArray2',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(store.agentRequests.value).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -211,37 +183,17 @@ describe('parameterOverrides.store', () => {
|
||||
it('saves to localStorage when state changes', async () => {
|
||||
const store = useAgentRequestStore();
|
||||
|
||||
localStorageMock.setItem.mockReset();
|
||||
|
||||
store.addAgentRequest('workflow-1', 'node-1', 'param1', 'value1');
|
||||
|
||||
// Wait for the next tick to allow the watch to execute
|
||||
await nextTick();
|
||||
|
||||
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
||||
'n8n-agent-requests',
|
||||
JSON.stringify({
|
||||
'workflow-1': {
|
||||
'node-1': { param1: 'value1' },
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle localStorage errors when saving', async () => {
|
||||
const store = useAgentRequestStore();
|
||||
|
||||
localStorageMock.setItem.mockReset();
|
||||
|
||||
localStorageMock.setItem.mockImplementation(() => {
|
||||
throw new Error('Storage error');
|
||||
store.setAgentRequestForNode('workflow-1', NODE_ID_1, {
|
||||
query: { param1: 'value1' },
|
||||
});
|
||||
|
||||
store.addAgentRequest('workflow-1', 'node-1', 'param1', 'value1');
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(store.agentRequests['workflow-1']['node-1'].param1).toBe('value1');
|
||||
expect(mockLocalStorageValue).toEqual({
|
||||
'workflow-1': {
|
||||
[NODE_ID_1]: { query: { param1: 'value1' } },
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,41 +1,23 @@
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import type { INodeParameters, NodeParameterValueType } from 'n8n-workflow';
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
interface IAgentRequestStoreState {
|
||||
const LOCAL_STORAGE_AGENT_REQUESTS = 'N8N_AGENT_REQUESTS';
|
||||
|
||||
export interface IAgentRequest {
|
||||
query: INodeParameters | string;
|
||||
toolName?: string;
|
||||
}
|
||||
|
||||
export interface IAgentRequestStoreState {
|
||||
[workflowId: string]: {
|
||||
[nodeName: string]: INodeParameters;
|
||||
[nodeName: string]: IAgentRequest;
|
||||
};
|
||||
}
|
||||
|
||||
const STORAGE_KEY = 'n8n-agent-requests';
|
||||
|
||||
export const useAgentRequestStore = defineStore('agentRequest', () => {
|
||||
// State
|
||||
const agentRequests = ref<IAgentRequestStoreState>(loadFromLocalStorage());
|
||||
|
||||
// Load initial state from localStorage
|
||||
function loadFromLocalStorage(): IAgentRequestStoreState {
|
||||
try {
|
||||
const storedData = localStorage.getItem(STORAGE_KEY);
|
||||
return storedData ? JSON.parse(storedData) : {};
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Save state to localStorage whenever it changes
|
||||
watch(
|
||||
agentRequests,
|
||||
(newValue) => {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(newValue));
|
||||
} catch (error) {
|
||||
console.error('Failed to save agent requests to localStorage:', error);
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
const agentRequests = useLocalStorage<IAgentRequestStoreState>(LOCAL_STORAGE_AGENT_REQUESTS, {});
|
||||
|
||||
// Helper function to ensure workflow and node entries exist
|
||||
const ensureWorkflowAndNodeExist = (workflowId: string, nodeId: string): void => {
|
||||
@@ -44,167 +26,66 @@ export const useAgentRequestStore = defineStore('agentRequest', () => {
|
||||
}
|
||||
|
||||
if (!agentRequests.value[workflowId][nodeId]) {
|
||||
agentRequests.value[workflowId][nodeId] = {};
|
||||
agentRequests.value[workflowId][nodeId] = { query: {} };
|
||||
}
|
||||
};
|
||||
|
||||
// Getters
|
||||
const getAgentRequests = (workflowId: string, nodeId: string): INodeParameters => {
|
||||
return agentRequests.value[workflowId]?.[nodeId] || {};
|
||||
const getAgentRequests = (workflowId: string, nodeId: string): INodeParameters | string => {
|
||||
return agentRequests.value[workflowId]?.[nodeId]?.query || {};
|
||||
};
|
||||
|
||||
const getAgentRequest = (
|
||||
const getQueryValue = (
|
||||
workflowId: string,
|
||||
nodeId: string,
|
||||
paramName: string,
|
||||
): NodeParameterValueType | undefined => {
|
||||
return agentRequests.value[workflowId]?.[nodeId]?.[paramName];
|
||||
const query = agentRequests.value[workflowId]?.[nodeId]?.query;
|
||||
if (typeof query === 'string') {
|
||||
return undefined;
|
||||
}
|
||||
return query?.[paramName] as NodeParameterValueType;
|
||||
};
|
||||
|
||||
// Actions
|
||||
const addAgentRequest = (
|
||||
const setAgentRequestForNode = (
|
||||
workflowId: string,
|
||||
nodeId: string,
|
||||
paramName: string,
|
||||
paramValues: NodeParameterValueType,
|
||||
): INodeParameters => {
|
||||
request: IAgentRequest,
|
||||
): void => {
|
||||
ensureWorkflowAndNodeExist(workflowId, nodeId);
|
||||
|
||||
agentRequests.value[workflowId][nodeId] = {
|
||||
...agentRequests.value[workflowId][nodeId],
|
||||
[paramName]: paramValues,
|
||||
};
|
||||
|
||||
return agentRequests.value[workflowId][nodeId];
|
||||
};
|
||||
|
||||
const addAgentRequests = (workflowId: string, nodeId: string, params: INodeParameters): void => {
|
||||
ensureWorkflowAndNodeExist(workflowId, nodeId);
|
||||
|
||||
agentRequests.value[workflowId][nodeId] = {
|
||||
...agentRequests.value[workflowId][nodeId],
|
||||
...params,
|
||||
...request,
|
||||
query: typeof request.query === 'string' ? request.query : { ...request.query },
|
||||
};
|
||||
};
|
||||
|
||||
const clearAgentRequests = (workflowId: string, nodeId: string): void => {
|
||||
if (agentRequests.value[workflowId]) {
|
||||
agentRequests.value[workflowId][nodeId] = {};
|
||||
agentRequests.value[workflowId][nodeId] = { query: {} };
|
||||
}
|
||||
};
|
||||
|
||||
const clearAllAgentRequests = (workflowId?: string): void => {
|
||||
if (workflowId) {
|
||||
// Clear requests for a specific workflow
|
||||
agentRequests.value[workflowId] = {};
|
||||
} else {
|
||||
// Clear all requests
|
||||
agentRequests.value = {};
|
||||
}
|
||||
};
|
||||
|
||||
function parsePath(path: string): string[] {
|
||||
return path.split('.').reduce((acc: string[], part) => {
|
||||
if (part.includes('[')) {
|
||||
const [arrayName, index] = part.split('[');
|
||||
if (arrayName) acc.push(arrayName);
|
||||
if (index) acc.push(index.replace(']', ''));
|
||||
} else {
|
||||
acc.push(part);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function buildRequestObject(path: string[], value: NodeParameterValueType): INodeParameters {
|
||||
const result: INodeParameters = {};
|
||||
let current = result;
|
||||
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
const part = path[i];
|
||||
const nextPart = path[i + 1];
|
||||
const isArrayIndex = nextPart && !isNaN(Number(nextPart));
|
||||
|
||||
if (isArrayIndex) {
|
||||
if (!current[part]) {
|
||||
current[part] = [];
|
||||
}
|
||||
while ((current[part] as NodeParameterValueType[]).length <= Number(nextPart)) {
|
||||
(current[part] as NodeParameterValueType[]).push({});
|
||||
}
|
||||
} else if (!current[part]) {
|
||||
current[part] = {};
|
||||
}
|
||||
|
||||
current = current[part] as INodeParameters;
|
||||
}
|
||||
|
||||
current[path[path.length - 1]] = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Helper function to deep merge objects
|
||||
function deepMerge(target: INodeParameters, source: INodeParameters): INodeParameters {
|
||||
const result = { ...target };
|
||||
|
||||
for (const key in source) {
|
||||
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
||||
// Recursively merge nested objects
|
||||
result[key] = deepMerge(
|
||||
(result[key] as INodeParameters) || {},
|
||||
source[key] as INodeParameters,
|
||||
);
|
||||
} else if (Array.isArray(source[key])) {
|
||||
// For arrays, merge by index
|
||||
if (Array.isArray(result[key])) {
|
||||
const targetArray = result[key] as NodeParameterValueType[];
|
||||
const sourceArray = source[key] as NodeParameterValueType[];
|
||||
|
||||
// Ensure target array has enough elements
|
||||
while (targetArray.length < sourceArray.length) {
|
||||
targetArray.push({});
|
||||
}
|
||||
|
||||
// Merge each array item
|
||||
sourceArray.forEach((item, index) => {
|
||||
if (item && typeof item === 'object') {
|
||||
targetArray[index] = deepMerge(
|
||||
(targetArray[index] as INodeParameters) || {},
|
||||
item as INodeParameters,
|
||||
) as NodeParameterValueType;
|
||||
} else {
|
||||
targetArray[index] = item;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
result[key] = source[key];
|
||||
}
|
||||
} else {
|
||||
// For primitive values, use source value
|
||||
result[key] = source[key];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const generateAgentRequest = (workflowId: string, nodeId: string): INodeParameters => {
|
||||
const nodeRequests = agentRequests.value[workflowId]?.[nodeId] || {};
|
||||
|
||||
return Object.entries(nodeRequests).reduce(
|
||||
(acc, [path, value]) => deepMerge(acc, buildRequestObject(parsePath(path), value)),
|
||||
{} as INodeParameters,
|
||||
);
|
||||
const getAgentRequest = (workflowId: string, nodeId: string): IAgentRequest | undefined => {
|
||||
if (agentRequests.value[workflowId]) return agentRequests.value[workflowId]?.[nodeId];
|
||||
return undefined;
|
||||
};
|
||||
|
||||
return {
|
||||
agentRequests,
|
||||
getAgentRequests,
|
||||
getAgentRequest,
|
||||
addAgentRequest,
|
||||
addAgentRequests,
|
||||
getQueryValue,
|
||||
setAgentRequestForNode,
|
||||
clearAgentRequests,
|
||||
clearAllAgentRequests,
|
||||
generateAgentRequest,
|
||||
getAgentRequest,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -124,8 +124,8 @@ describe('FromAiParametersModal', () => {
|
||||
workflowsStore.getCurrentWorkflow = vi.fn().mockReturnValue(mockWorkflow);
|
||||
agentRequestStore = useAgentRequestStore();
|
||||
agentRequestStore.clearAgentRequests = vi.fn();
|
||||
agentRequestStore.addAgentRequests = vi.fn();
|
||||
agentRequestStore.generateAgentRequest = vi.fn();
|
||||
agentRequestStore.setAgentRequestForNode = vi.fn();
|
||||
agentRequestStore.getAgentRequest = vi.fn();
|
||||
nodeTypesStore = useNodeTypesStore();
|
||||
nodeTypesStore.getNodeParameterOptions = vi.fn().mockResolvedValue(mockTools);
|
||||
});
|
||||
@@ -214,9 +214,11 @@ describe('FromAiParametersModal', () => {
|
||||
|
||||
await userEvent.click(getByTestId('execute-workflow-button'));
|
||||
|
||||
expect(agentRequestStore.addAgentRequests).toHaveBeenCalledWith('test-workflow', 'id1', {
|
||||
'query.testBoolean': true,
|
||||
'query.testParam': 'override',
|
||||
expect(agentRequestStore.setAgentRequestForNode).toHaveBeenCalledWith('test-workflow', 'id1', {
|
||||
query: {
|
||||
testBoolean: true,
|
||||
testParam: 'override',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -266,9 +268,11 @@ describe('FromAiParametersModal', () => {
|
||||
);
|
||||
await userEvent.click(getByTestId('execute-workflow-button'));
|
||||
|
||||
expect(agentRequestStore.addAgentRequests).toHaveBeenCalledWith('test-workflow', 'id1', {
|
||||
'query.testBoolean': false,
|
||||
'query.testParam': 'given value',
|
||||
expect(agentRequestStore.setAgentRequestForNode).toHaveBeenCalledWith('test-workflow', 'id1', {
|
||||
query: {
|
||||
testBoolean: false,
|
||||
testParam: 'given value',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { useRunWorkflow } from '@/composables/useRunWorkflow';
|
||||
import { FROM_AI_PARAMETERS_MODAL_KEY, AI_MCP_TOOL_NODE_TYPE } from '@/constants';
|
||||
import { useAgentRequestStore } from '@n8n/stores/useAgentRequestStore';
|
||||
import { useAgentRequestStore, type IAgentRequest } from '@n8n/stores/useAgentRequestStore';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { createEventBus } from '@n8n/utils/event-bus';
|
||||
import {
|
||||
@@ -166,11 +166,8 @@ watch(
|
||||
const inputQuery = inputOverrides?.query as IDataObject;
|
||||
const initialValue = inputQuery?.[value.key]
|
||||
? inputQuery[value.key]
|
||||
: (agentRequestStore.getAgentRequest(
|
||||
workflowsStore.workflowId,
|
||||
newNode.id,
|
||||
'query.' + value.key,
|
||||
) ?? mapTypes[type]?.defaultValue);
|
||||
: (agentRequestStore.getQueryValue(workflowsStore.workflowId, newNode.id, value.key) ??
|
||||
mapTypes[type]?.defaultValue);
|
||||
|
||||
result.push({
|
||||
name: 'query.' + value.key,
|
||||
@@ -189,7 +186,7 @@ watch(
|
||||
}
|
||||
const queryValue =
|
||||
inputQuery ??
|
||||
agentRequestStore.getAgentRequest(workflowsStore.workflowId, newNode.id, 'query') ??
|
||||
agentRequestStore.getQueryValue(workflowsStore.workflowId, newNode.id, 'query') ??
|
||||
'';
|
||||
|
||||
result.push({
|
||||
@@ -216,7 +213,24 @@ const onExecute = async () => {
|
||||
const inputValues = inputs.value?.getValues() ?? {};
|
||||
|
||||
agentRequestStore.clearAgentRequests(workflowsStore.workflowId, node.value.id);
|
||||
agentRequestStore.addAgentRequests(workflowsStore.workflowId, node.value.id, inputValues);
|
||||
|
||||
// Structure the input values as IAgentRequest
|
||||
const agentRequest: IAgentRequest = {
|
||||
query: {},
|
||||
toolName: inputValues.toolName as string,
|
||||
};
|
||||
|
||||
// Move all query.* fields to query object
|
||||
Object.entries(inputValues).forEach(([key, value]) => {
|
||||
if (key === 'query') {
|
||||
agentRequest.query = value as string;
|
||||
} else if (key.startsWith('query.') && 'string' !== typeof agentRequest.query) {
|
||||
const queryKey = key.replace('query.', '');
|
||||
agentRequest.query[queryKey] = value;
|
||||
}
|
||||
});
|
||||
|
||||
agentRequestStore.setAgentRequestForNode(workflowsStore.workflowId, node.value.id, agentRequest);
|
||||
|
||||
const telemetryPayload = {
|
||||
node_type: node.value.type,
|
||||
|
||||
@@ -68,7 +68,7 @@ vi.mock('@/stores/workflows.store', () => {
|
||||
vi.mock('@/stores/parameterOverrides.store', () => {
|
||||
const storeState: Partial<ReturnType<typeof useAgentRequestStore>> & {} = {
|
||||
agentRequests: {},
|
||||
generateAgentRequest: vi.fn(),
|
||||
getAgentRequest: vi.fn(),
|
||||
};
|
||||
return {
|
||||
useAgentRequestStore: vi.fn().mockReturnValue(storeState),
|
||||
@@ -692,12 +692,12 @@ describe('useRunWorkflow({ router })', () => {
|
||||
vi.mocked(workflowHelpers).getCurrentWorkflow.mockReturnValue(workflow);
|
||||
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue(workflowData);
|
||||
vi.mocked(workflowsStore).getWorkflowRunData = mockRunData;
|
||||
vi.mocked(agentRequestStore).generateAgentRequest.mockReturnValue(agentRequest);
|
||||
vi.mocked(agentRequestStore).getAgentRequest.mockReturnValue(agentRequest);
|
||||
// ACT
|
||||
const result = await runWorkflow({ destinationNode: 'Test node' });
|
||||
|
||||
// ASSERT
|
||||
expect(agentRequestStore.generateAgentRequest).toHaveBeenCalledWith('WorkflowId', 'Test id');
|
||||
expect(agentRequestStore.getAgentRequest).toHaveBeenCalledWith('WorkflowId', 'Test id');
|
||||
expect(workflowsStore.runWorkflow).toHaveBeenCalledWith({
|
||||
agentRequest: {
|
||||
query: 'query',
|
||||
|
||||
@@ -314,7 +314,7 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
|
||||
startRunData.destinationNode = options.destinationNode;
|
||||
const nodeId = workflowsStore.getNodeByName(options.destinationNode as string)?.id;
|
||||
if (workflow.id && nodeId && version === 2) {
|
||||
const agentRequest = agentRequestStore.generateAgentRequest(workflow.id, nodeId);
|
||||
const agentRequest = agentRequestStore.getAgentRequest(workflow.id, nodeId);
|
||||
|
||||
if (agentRequest) {
|
||||
startRunData.agentRequest = {
|
||||
|
||||
Reference in New Issue
Block a user