Support define is tool logic function (#17926)

- supports isTool and timeout settings in defineLogicFunction in apps
and in setting tabs definition
- compute for all toolInputSchema for logic funciton, in settings and in
code steps

<img width="991" height="802" alt="image"
src="https://github.com/user-attachments/assets/05dc1221-cac9-45a3-87b0-3b13161446fd"
/>
This commit is contained in:
martmull
2026-02-16 10:43:29 +01:00
committed by GitHub
parent f694bb99b3
commit da064d5e88
138 changed files with 4839 additions and 367 deletions
@@ -0,0 +1,57 @@
import { canObjectBeManagedByWorkflow } from '@/workflow/utils/canObjectBeManagedByWorkflow';
describe('canObjectBeManagedByWorkflow', () => {
it('should return true for non-system, non-excluded objects', () => {
expect(
canObjectBeManagedByWorkflow({
nameSingular: 'company',
isSystem: false,
}),
).toBe(true);
});
it('should return false for system objects', () => {
expect(
canObjectBeManagedByWorkflow({
nameSingular: 'company',
isSystem: true,
}),
).toBe(false);
});
it('should return false for workflow object', () => {
expect(
canObjectBeManagedByWorkflow({
nameSingular: 'workflow',
isSystem: false,
}),
).toBe(false);
});
it('should return false for workflowVersion object', () => {
expect(
canObjectBeManagedByWorkflow({
nameSingular: 'workflowVersion',
isSystem: false,
}),
).toBe(false);
});
it('should return false for workflowRun object', () => {
expect(
canObjectBeManagedByWorkflow({
nameSingular: 'workflowRun',
isSystem: false,
}),
).toBe(false);
});
it('should return false for dashboard object', () => {
expect(
canObjectBeManagedByWorkflow({
nameSingular: 'dashboard',
isSystem: false,
}),
).toBe(false);
});
});
@@ -0,0 +1,30 @@
import { extractRawVariableNamePart } from '@/workflow/utils/extractRawVariableNameParts';
describe('extractRawVariableNamePart', () => {
it('should extract stepId from a variable name', () => {
const result = extractRawVariableNamePart({
rawVariableName: '{{step1.output.field}}',
part: 'stepId',
});
expect(result).toBe('step1');
});
it('should extract selectedField from a variable name', () => {
const result = extractRawVariableNamePart({
rawVariableName: '{{step1.output.field}}',
part: 'selectedField',
});
expect(result).toBe('field');
});
it('should handle deeply nested variable names', () => {
const result = extractRawVariableNamePart({
rawVariableName: '{{trigger.output.nested.deep}}',
part: 'selectedField',
});
expect(result).toBe('deep');
});
});
@@ -0,0 +1,23 @@
import { parseBooleanFromStringValue } from '@/workflow/utils/parseBooleanFromStringValue';
describe('parseBooleanFromStringValue', () => {
it('should return true for "true" string', () => {
expect(parseBooleanFromStringValue('true')).toBe(true);
});
it('should return false for "false" string', () => {
expect(parseBooleanFromStringValue('false')).toBe(false);
});
it('should return the original value for non-boolean strings', () => {
expect(parseBooleanFromStringValue('hello')).toBe('hello');
});
it('should return the original value for numbers', () => {
expect(parseBooleanFromStringValue(42)).toBe(42);
});
it('should return the original value for null', () => {
expect(parseBooleanFromStringValue(null)).toBeNull();
});
});
@@ -0,0 +1,116 @@
import { parseDataFromContentType } from '@/workflow/utils/parseDataFromContentType';
describe('parseDataFromContentType', () => {
describe('application/json', () => {
it('should stringify object data', () => {
const result = parseDataFromContentType(
{ key: 'value' },
'application/json',
);
expect(result).toBe('{"key":"value"}');
});
it('should return string data as-is', () => {
const result = parseDataFromContentType(
'{"key":"value"}',
'application/json',
);
expect(result).toBe('{"key":"value"}');
});
});
describe('text/plain', () => {
it('should return string data as-is', () => {
const result = parseDataFromContentType('hello', 'text/plain');
expect(result).toBe('hello');
});
it('should convert object to key=val format', () => {
const result = parseDataFromContentType(
{ name: 'Alice', age: '30' },
'text/plain',
);
expect(result).toContain('name=Alice');
expect(result).toContain('age=30');
});
});
describe('application/x-www-form-urlencoded', () => {
it('should convert object to URL-encoded format', () => {
const result = parseDataFromContentType(
{ key: 'value', foo: 'bar' },
'application/x-www-form-urlencoded',
);
expect(result).toContain('key=value');
expect(result).toContain('foo=bar');
});
it('should parse JSON string data', () => {
const result = parseDataFromContentType(
'{"key":"value"}',
'application/x-www-form-urlencoded',
);
expect(result).toContain('key=value');
});
it('should handle non-JSON string data', () => {
const result = parseDataFromContentType(
'raw-string',
'application/x-www-form-urlencoded',
);
expect(typeof result).toBe('string');
});
});
describe('multipart/form-data', () => {
it('should return FormData for object data', () => {
const result = parseDataFromContentType(
{ key: 'value' },
'multipart/form-data',
);
expect(result).toBeInstanceOf(FormData);
});
it('should parse JSON string into FormData', () => {
const result = parseDataFromContentType(
'{"key":"value"}',
'multipart/form-data',
);
expect(result).toBeInstanceOf(FormData);
});
it('should throw for invalid JSON string', () => {
expect(() =>
parseDataFromContentType('not-json', 'multipart/form-data'),
).toThrow('String data for FormData must be valid JSON');
});
});
describe('default (no content type)', () => {
it('should default to JSON parsing when content type is undefined', () => {
const result = parseDataFromContentType({ key: 'value' });
expect(result).toBe('{"key":"value"}');
});
});
describe('unknown content type', () => {
it('should default to JSON parsing', () => {
const result = parseDataFromContentType(
{ key: 'value' },
'application/xml',
);
expect(result).toBe('{"key":"value"}');
});
});
});