Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 28 additions & 27 deletions libs/designer-v2/src/lib/core/actions/bjsworkflow/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ export const serializeOperation = async (
}

let serializedOperation: LogicAppsV2.OperationDefinition;
const isManagedMcpClient = operation.type?.toLowerCase() === 'mcpclienttool' && operation.kind?.toLowerCase() === "managed";
const isManagedMcpClient = operation.type?.toLowerCase() === 'mcpclienttool' && operation.kind?.toLowerCase() === 'managed';

if (isManagedMcpClient) {
serializedOperation = await serializeManagedMcpOperation(rootState, operationId);
Expand Down Expand Up @@ -432,9 +432,10 @@ const serializeManifestBasedOperation = async (rootState: RootState, operationId
const hostInfo = serializeHost(operationId, manifest, rootState);
const inputs = hostInfo !== undefined ? mergeHostWithInputs(hostInfo, inputPathValue) : inputPathValue;
const operationFromWorkflow = getRecordEntry(rootState.workflow.operations, operationId) as LogicAppsV2.OperationDefinition;
const runAfter = isRootNode(operationId, rootState.workflow.nodesMetadata) || manifest.properties.runAfter?.type === RunAfterType.NotSupported
? undefined
: getRunAfter(operationFromWorkflow, idReplacements);
const runAfter =
isRootNode(operationId, rootState.workflow.nodesMetadata) || manifest.properties.runAfter?.type === RunAfterType.NotSupported
? undefined
: getRunAfter(operationFromWorkflow, idReplacements);
const recurrence =
isTrigger && manifest.properties.recurrence && manifest.properties.recurrence.type !== RecurrenceType.None
? constructInputValues('recurrence.$.recurrence', inputsToSerialize, false /* encodePathComponents */)
Expand Down Expand Up @@ -475,7 +476,7 @@ const serializeManagedMcpOperation = async (rootState: RootState, nodeId: string
const nativeMcpOperationInfo = { connectorId: 'connectionProviders/mcpclient', operationId: 'nativemcpclient' };
const manifest = await getOperationManifest(nativeMcpOperationInfo);
const inputParameters = serializeParametersFromManifest(inputsToSerialize, manifest);

const operationFromWorkflow = getRecordEntry(rootState.workflow.operations, nodeId) as LogicAppsV2.OperationDefinition;

const { parsedSwagger } = await getConnectorWithSwagger(connectorId);
Expand Down Expand Up @@ -859,7 +860,7 @@ interface AgentConnectionInfo {
interface McpConnectionInfo {
connectionReference: {
connectionName: string;
}
};
}

const serializeHost = (
Expand Down Expand Up @@ -945,8 +946,8 @@ const serializeHost = (
case ConnectionReferenceKeyFormat.McpConnection:
return {
connectionReference: {
connectionName: referenceKey
}
connectionName: referenceKey,
},
};
default:
throw new AssertionException(
Expand Down Expand Up @@ -1006,28 +1007,28 @@ const serializeNestedOperations = async (

if (subGraphDetail?.allowOperations) {
const operations = operationNodes.filter((graph) => graph.subGraphLocation === subGraphLocation);
const nestedOperationsPromises = operations.map((nestedOperation) =>
serializeOperation(rootState, nestedOperation.id)
) as Promise<LogicAppsV2.OperationDefinition>[];
const nestedOperations = await Promise.all(nestedOperationsPromises);
const idReplacements = rootState.workflow.idReplacements;

const newResult = {};
safeSetObjectPropertyValue(
newResult,
[subGraphLocation],
nestedOperations.reduce((actions: LogicAppsV2.Actions, action: LogicAppsV2.OperationDefinition, index: number) => {
if (!isNullOrEmpty(action)) {
const actionId = operations[index].id;
actions[getRecordEntry(idReplacements, actionId) ?? actionId] = action;
return actions;
}
const nestedOperationsPromises = operations.map((nestedOperation) =>
serializeOperation(rootState, nestedOperation.id)
) as Promise<LogicAppsV2.OperationDefinition>[];
const nestedOperations = await Promise.all(nestedOperationsPromises);
const idReplacements = rootState.workflow.idReplacements;

const newResult = {};
safeSetObjectPropertyValue(
newResult,
[subGraphLocation],
nestedOperations.reduce((actions: LogicAppsV2.Actions, action: LogicAppsV2.OperationDefinition, index: number) => {
if (!isNullOrEmpty(action)) {
const actionId = operations[index].id;
actions[getRecordEntry(idReplacements, actionId) ?? actionId] = action;
return actions;
}, {})
);
}

return actions;
}, {})
);

result = merge(result, newResult);
result = merge(result, newResult);
}

if (subGraphDetail?.isAdditive) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,62 +9,62 @@ export const agentMcpWorkflowDefinitionInput = {
contentVersion: '1.0.0.0',
parameters: {},
triggers: {
'manual': {
manual: {
type: 'Request',
kind: 'Http',
inputs: {
schema: {}
}
}
schema: {},
},
},
},
actions: {
'WorkflowAgent': {
WorkflowAgent: {
type: 'Agent',
inputs: {
parameters: {
deploymentId: 'gpt-4o',
messages: [
{
role: 'System',
content: 'You are a helpful assistant that can use tools to accomplish tasks.'
content: 'You are a helpful assistant that can use tools to accomplish tasks.',
},
{
role: 'User',
content: 'Help me manage files and data.'
}
]
}
content: 'Help me manage files and data.',
},
],
},
},
tools: {
'McpFileServer': {
type: 'McpClientTool',
kind: 'BuiltIn',
inputs: {
parameters: {
mcpServerPath: '/servers/filesystem',
toolName: 'list_files',
}
}
}
McpFileServer: {
type: 'McpClientTool',
kind: 'BuiltIn',
inputs: {
parameters: {
mcpServerPath: '/servers/filesystem',
toolName: 'list_files',
},
},
},
},
runAfter: {},
limit: {
timeout: 'PT2H',
count: 50
}
count: 50,
},
},
'ResponseAction': {
ResponseAction: {
type: 'Response',
inputs: {
statusCode: 200,
body: '@outputs("WorkflowAgent")'
body: '@outputs("WorkflowAgent")',
},
runAfter: {
'WorkflowAgent': ['SUCCEEDED']
}
}
WorkflowAgent: ['SUCCEEDED'],
},
},
},
outputs: {}
outputs: {},
};

export const expectedAgentMcpWorkflowDefinitionOutput = {
Expand Down Expand Up @@ -92,16 +92,16 @@ export const expectedAgentMcpWorkflowDefinitionOutput = {
children: [
{
height: 40,
id: "WorkflowAgent-addCase-#subgraph",
type: "SUBGRAPH_CARD_NODE",
id: 'WorkflowAgent-addCase-#subgraph',
type: 'SUBGRAPH_CARD_NODE',
width: 200,
},
],
edges: [],
id: "WorkflowAgent-addCase",
id: 'WorkflowAgent-addCase',
subGraphLocation: undefined,
type: "HIDDEN_NODE",
},
type: 'HIDDEN_NODE',
},
],
edges: [
{
Expand All @@ -124,15 +124,12 @@ export const expectedAgentMcpWorkflowDefinitionOutput = {
},
createWorkflowNode('ResponseAction'),
],
edges: [
createWorkflowEdge('manual', 'WorkflowAgent'),
createWorkflowEdge('WorkflowAgent', 'ResponseAction'),
],
edges: [createWorkflowEdge('manual', 'WorkflowAgent'), createWorkflowEdge('WorkflowAgent', 'ResponseAction')],
},
actionData: {
manual: {
inputs: {
schema: {}
schema: {},
},
kind: 'Http',
type: 'Request',
Expand All @@ -144,58 +141,58 @@ export const expectedAgentMcpWorkflowDefinitionOutput = {
messages: [
{
role: 'System',
content: 'You are a helpful assistant that can use tools to accomplish tasks.'
content: 'You are a helpful assistant that can use tools to accomplish tasks.',
},
{
role: 'User',
content: 'Help me manage files and data.'
}
]
}
content: 'Help me manage files and data.',
},
],
},
},
tools: {
'McpFileServer': {
type: 'McpClientTool',
kind: 'BuiltIn',
inputs: {
parameters: {
mcpServerPath: '/servers/filesystem',
toolName: 'list_files',
}
}
}
McpFileServer: {
type: 'McpClientTool',
kind: 'BuiltIn',
inputs: {
parameters: {
mcpServerPath: '/servers/filesystem',
toolName: 'list_files',
},
},
},
},
limit: {
timeout: 'PT2H',
count: 50
count: 50,
},
runAfter: {},
type: 'Agent',
},
ResponseAction: {
inputs: {
statusCode: 200,
body: '@outputs("WorkflowAgent")'
body: '@outputs("WorkflowAgent")',
},
runAfter: {
'WorkflowAgent': ['SUCCEEDED']
WorkflowAgent: ['SUCCEEDED'],
},
type: 'Response',
},
McpFileServer: {
type: 'McpClientTool',
kind: 'BuiltIn',
inputs: {
parameters: {
mcpServerPath: '/servers/filesystem',
toolName: 'list_files',
}
}
type: 'McpClientTool',
kind: 'BuiltIn',
inputs: {
parameters: {
mcpServerPath: '/servers/filesystem',
toolName: 'list_files',
},
},
},
},
nodesMetadata: {
manual: { graphId: 'root', isRoot: true, isTrigger: true },
WorkflowAgent: {
WorkflowAgent: {
graphId: 'root',
actionCount: 1,
parentNodeId: undefined,
Expand All @@ -204,13 +201,13 @@ export const expectedAgentMcpWorkflowDefinitionOutput = {
McpFileServer: {
graphId: 'WorkflowAgent',
parentNodeId: 'WorkflowAgent',
subgraphType: "MCP_CLIENT",
subgraphType: 'MCP_CLIENT',
},
'WorkflowAgent-addCase': {
actionCount: 0,
graphId: 'WorkflowAgent',
parentNodeId: 'WorkflowAgent',
subgraphType: 'AGENT_ADD_CONDITON',
actionCount: 0,
graphId: 'WorkflowAgent',
parentNodeId: 'WorkflowAgent',
subgraphType: 'AGENT_ADD_CONDITON',
},
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,46 @@ describe('CreateConnectionWrapper', () => {
expectedConnectionParameterSetValuesWithUndefined
);
});

it('filters out parameters not in validParameterKeys when provided', () => {
const outputValues = {
parameter1: 'value1',
parameter2: 'value2',
'token:tenantId': 'tenant-id-value', // This should be filtered out
extraParam: 'extra-value', // This should be filtered out
};

const validKeys = ['parameter1', 'parameter2'];

const result = getConnectionParameterSetValues(selectedParameterSetName, outputValues, validKeys);

expect(result).toEqual({
name: selectedParameterSetName,
values: {
parameter1: { value: 'value1' },
parameter2: { value: 'value2' },
},
});
// Ensure filtered parameters are not included
expect(result.values).not.toHaveProperty('token:tenantId');
expect(result.values).not.toHaveProperty('extraParam');
});

it('includes all parameters when validParameterKeys is not provided', () => {
const outputValues = {
parameter1: 'value1',
'token:tenantId': 'tenant-id-value',
};

const result = getConnectionParameterSetValues(selectedParameterSetName, outputValues);

expect(result).toEqual({
name: selectedParameterSetName,
values: {
parameter1: { value: 'value1' },
'token:tenantId': { value: 'tenant-id-value' },
},
});
});
});
});
Loading
Loading