Introduce a TAB to test the HTTP node (#13622)
<img width="1020" height="757" alt="Screenshot 2025-08-05 at 11 05 12" src="https://github.com/user-attachments/assets/7cfcb216-d34d-4daa-b468-8153869c8f6a" />
This commit is contained in:
-113
@@ -1,113 +0,0 @@
|
||||
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||
|
||||
describe('resolveInput', () => {
|
||||
const context = {
|
||||
user: {
|
||||
name: 'John Doe',
|
||||
age: 30,
|
||||
},
|
||||
settings: {
|
||||
theme: 'dark',
|
||||
notifications: true,
|
||||
},
|
||||
};
|
||||
|
||||
it('should return null for null input', () => {
|
||||
expect(resolveInput(null, context)).toBeNull();
|
||||
});
|
||||
|
||||
it('should return undefined for undefined input', () => {
|
||||
expect(resolveInput(undefined, context)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should resolve a simple string variable', () => {
|
||||
expect(resolveInput('{{user.name}}', context)).toBe('John Doe');
|
||||
});
|
||||
|
||||
it('should resolve multiple variables in a string', () => {
|
||||
expect(
|
||||
resolveInput('Name: {{user.name}}, Age: {{user.age}}', context),
|
||||
).toBe('Name: John Doe, Age: 30');
|
||||
});
|
||||
|
||||
it('should handle non-existent variables', () => {
|
||||
expect(resolveInput('{{user.email}}', context)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should resolve variables in an array', () => {
|
||||
const input = ['{{user.name}}', '{{settings.theme}}', 'static'];
|
||||
const expected = ['John Doe', 'dark', 'static'];
|
||||
|
||||
expect(resolveInput(input, context)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should resolve variables in an object', () => {
|
||||
const input = {
|
||||
name: '{{user.name}}',
|
||||
theme: '{{settings.theme}}',
|
||||
static: 'value',
|
||||
};
|
||||
const expected = {
|
||||
name: 'John Doe',
|
||||
theme: 'dark',
|
||||
static: 'value',
|
||||
};
|
||||
|
||||
expect(resolveInput(input, context)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle nested objects and arrays', () => {
|
||||
const input = {
|
||||
user: {
|
||||
displayName: '{{user.name}}',
|
||||
preferences: ['{{settings.theme}}', '{{settings.notifications}}'],
|
||||
},
|
||||
staticData: [1, 2, 3],
|
||||
};
|
||||
const expected = {
|
||||
user: {
|
||||
displayName: 'John Doe',
|
||||
preferences: ['dark', true],
|
||||
},
|
||||
staticData: [1, 2, 3],
|
||||
};
|
||||
|
||||
expect(resolveInput(input, context)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('does not wrap string variables with double quotes', () => {
|
||||
expect(
|
||||
resolveInput('{ {{test}}: 2 }', {
|
||||
test: 'prop',
|
||||
}),
|
||||
).toBe('{ prop: 2 }');
|
||||
});
|
||||
|
||||
it('does not modify static JSON', () => {
|
||||
expect(resolveInput('{ "a": 2 }', {})).toBe('{ "a": 2 }');
|
||||
});
|
||||
|
||||
it('supports variable as JSON object property name', () => {
|
||||
expect(
|
||||
resolveInput('{ "{{test}}": 2 }', {
|
||||
test: 'prop',
|
||||
}),
|
||||
).toBe('{ "prop": 2 }');
|
||||
});
|
||||
|
||||
it('supports variable as JSON number value', () => {
|
||||
expect(
|
||||
resolveInput('{ "a": {{test}} }', {
|
||||
test: 2,
|
||||
}),
|
||||
).toBe('{ "a": 2 }');
|
||||
});
|
||||
|
||||
it('supports variable as JSON string value', () => {
|
||||
expect(
|
||||
resolveInput('{ "a": "{{test}}" }', {
|
||||
test: 'str',
|
||||
}),
|
||||
).toBe('{ "a": "str" }');
|
||||
});
|
||||
});
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
import { isNil, isString } from '@nestjs/common/utils/shared.utils';
|
||||
|
||||
import Handlebars from 'handlebars';
|
||||
|
||||
const VARIABLE_PATTERN = RegExp('\\{\\{(.*?)\\}\\}', 'g');
|
||||
|
||||
export const resolveInput = (
|
||||
unresolvedInput: unknown,
|
||||
context: Record<string, unknown>,
|
||||
): unknown => {
|
||||
if (isNil(unresolvedInput)) {
|
||||
return unresolvedInput;
|
||||
}
|
||||
|
||||
if (isString(unresolvedInput)) {
|
||||
return resolveString(unresolvedInput, context);
|
||||
}
|
||||
|
||||
if (Array.isArray(unresolvedInput)) {
|
||||
return resolveArray(unresolvedInput, context);
|
||||
}
|
||||
|
||||
if (typeof unresolvedInput === 'object' && unresolvedInput !== null) {
|
||||
return resolveObject(unresolvedInput, context);
|
||||
}
|
||||
|
||||
return unresolvedInput;
|
||||
};
|
||||
|
||||
const resolveArray = (
|
||||
input: unknown[],
|
||||
context: Record<string, unknown>,
|
||||
): unknown[] => {
|
||||
const resolvedArray = input;
|
||||
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
resolvedArray[i] = resolveInput(input[i], context);
|
||||
}
|
||||
|
||||
return resolvedArray;
|
||||
};
|
||||
|
||||
const resolveObject = (
|
||||
input: object,
|
||||
context: Record<string, unknown>,
|
||||
): object => {
|
||||
return Object.entries(input).reduce<Record<string, unknown>>(
|
||||
(resolvedObject, [key, value]) => {
|
||||
const resolvedKey = resolveInput(key, context);
|
||||
|
||||
resolvedObject[
|
||||
typeof resolvedKey === 'string' ? resolvedKey : String(resolvedKey)
|
||||
] = resolveInput(value, context);
|
||||
|
||||
return resolvedObject;
|
||||
},
|
||||
{},
|
||||
);
|
||||
};
|
||||
|
||||
const resolveString = (
|
||||
input: string,
|
||||
context: Record<string, unknown>,
|
||||
): string => {
|
||||
const matchedTokens = input.match(VARIABLE_PATTERN);
|
||||
|
||||
if (!matchedTokens || matchedTokens.length === 0) {
|
||||
return input;
|
||||
}
|
||||
|
||||
if (matchedTokens.length === 1 && matchedTokens[0] === input) {
|
||||
return evalFromContext(input, context);
|
||||
}
|
||||
|
||||
return input.replace(VARIABLE_PATTERN, (matchedToken, _) => {
|
||||
const processedToken = evalFromContext(matchedToken, context);
|
||||
|
||||
return processedToken;
|
||||
});
|
||||
};
|
||||
|
||||
const evalFromContext = (input: string, context: Record<string, unknown>) => {
|
||||
try {
|
||||
Handlebars.registerHelper('json', (input: string) => JSON.stringify(input));
|
||||
|
||||
const inputWithHelper = input
|
||||
.replace('{{', '{{{ json ')
|
||||
.replace('}}', ' }}}');
|
||||
|
||||
const inferredInput = Handlebars.compile(inputWithHelper)(context, {
|
||||
helpers: {
|
||||
json: (input: string) => JSON.stringify(input),
|
||||
},
|
||||
});
|
||||
|
||||
return JSON.parse(inferredInput) ?? '';
|
||||
} catch (exception) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
+1
-1
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
import { resolveInput } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
||||
|
||||
@@ -18,7 +19,6 @@ import {
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||
|
||||
import { isWorkflowAiAgentAction } from './guards/is-workflow-ai-agent-action.guard';
|
||||
|
||||
|
||||
+2
-1
@@ -1,5 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { resolveInput } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
||||
|
||||
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||
@@ -10,7 +12,6 @@ import {
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||
import { isWorkflowCodeAction } from 'src/modules/workflow/workflow-executor/workflow-actions/code/guards/is-workflow-code-action.guard';
|
||||
import { WorkflowCodeActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-input.type';
|
||||
|
||||
|
||||
+2
-1
@@ -1,5 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { resolveInput } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
||||
|
||||
import {
|
||||
@@ -8,7 +10,6 @@ import {
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||
import { isWorkflowFilterAction } from 'src/modules/workflow/workflow-executor/workflow-actions/filter/guards/is-workflow-filter-action.guard';
|
||||
import { evaluateFilterConditions } from 'src/modules/workflow/workflow-executor/workflow-actions/filter/utils/evaluate-filter-conditions.util';
|
||||
|
||||
|
||||
+1
-1
@@ -3,6 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isDefined } from 'class-validator';
|
||||
import { Repository } from 'typeorm';
|
||||
import { resolveInput } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
||||
|
||||
@@ -19,7 +20,6 @@ import {
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||
import {
|
||||
RecordCRUDActionException,
|
||||
RecordCRUDActionExceptionCode,
|
||||
|
||||
+1
-2
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'class-validator';
|
||||
import { isValidUuid } from 'twenty-shared/utils';
|
||||
import { isValidUuid, resolveInput } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||
import {
|
||||
RecordCRUDActionException,
|
||||
RecordCRUDActionExceptionCode,
|
||||
|
||||
+1
-1
@@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { Entity } from '@microsoft/microsoft-graph-types';
|
||||
import { QUERY_MAX_RECORDS } from 'twenty-shared/constants';
|
||||
import { ObjectLiteral } from 'typeorm';
|
||||
import { resolveInput } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
ObjectRecordFilter,
|
||||
@@ -24,7 +25,6 @@ import {
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||
import {
|
||||
RecordCRUDActionException,
|
||||
RecordCRUDActionExceptionCode,
|
||||
|
||||
+1
-2
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import deepEqual from 'deep-equal';
|
||||
import { isDefined, isValidUuid } from 'twenty-shared/utils';
|
||||
import { isDefined, isValidUuid, resolveInput } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||
import {
|
||||
RecordCRUDActionException,
|
||||
RecordCRUDActionExceptionCode,
|
||||
|
||||
+2
-1
@@ -1,5 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { resolveInput } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
||||
|
||||
import { ToolType } from 'src/engine/core-modules/tool/enums/tool-type.enum';
|
||||
@@ -7,7 +9,6 @@ import { ToolRegistryService } from 'src/engine/core-modules/tool/services/tool-
|
||||
import { ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type';
|
||||
import { WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
|
||||
@Injectable()
|
||||
|
||||
Reference in New Issue
Block a user