Add first filter step version (#13093)
I rebuilt the advanced filters used in views and workflow search for a specific filter step. Components structure remains the same, using `stepFilterGroups` and `stepFilters`. But those filters are directly sent to backend. Also re-using the same kind of states we use for advanced filters to share the current filters used. And a context to share what's coming from workflow props (function to update step settings and readonly) ⚠️ this PR only focusses on the content of the step. There is still a lot to do on the filter icon behavior in the workflow https://github.com/user-attachments/assets/8a6a76f0-11fa-444a-82b9-71fc96b18af4
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
import { WorkflowStep } from '@/workflow/types/Workflow';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { findStepPosition } from '../findStepPosition';
|
||||
|
||||
describe('findStepPosition', () => {
|
||||
const mockSteps: WorkflowStep[] = [
|
||||
{
|
||||
id: 'step-1',
|
||||
name: 'First Step',
|
||||
type: 'CREATE_RECORD',
|
||||
valid: true,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
input: {
|
||||
objectName: 'Company',
|
||||
objectRecord: {},
|
||||
},
|
||||
outputSchema: {},
|
||||
},
|
||||
} as WorkflowStep,
|
||||
{
|
||||
id: 'step-2',
|
||||
name: 'Second Step',
|
||||
type: 'UPDATE_RECORD',
|
||||
valid: true,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
input: {
|
||||
objectName: 'Company',
|
||||
objectRecord: {},
|
||||
objectRecordId: 'test-id',
|
||||
fieldsToUpdate: ['name'],
|
||||
},
|
||||
outputSchema: {},
|
||||
},
|
||||
} as WorkflowStep,
|
||||
{
|
||||
id: 'step-3',
|
||||
name: 'Third Step',
|
||||
type: 'DELETE_RECORD',
|
||||
valid: true,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
input: {
|
||||
objectName: 'Company',
|
||||
objectRecordId: 'test-id',
|
||||
},
|
||||
outputSchema: {},
|
||||
},
|
||||
} as WorkflowStep,
|
||||
];
|
||||
|
||||
it('should return index 0 when stepId is undefined', () => {
|
||||
const result = findStepPosition({
|
||||
steps: mockSteps,
|
||||
stepId: undefined,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
steps: mockSteps,
|
||||
index: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return index 0 when stepId is TRIGGER_STEP_ID', () => {
|
||||
const result = findStepPosition({
|
||||
steps: mockSteps,
|
||||
stepId: TRIGGER_STEP_ID,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
steps: mockSteps,
|
||||
index: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('should find the correct position for an existing step', () => {
|
||||
const result = findStepPosition({
|
||||
steps: mockSteps,
|
||||
stepId: 'step-2',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
steps: mockSteps,
|
||||
index: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('should find the first step position', () => {
|
||||
const result = findStepPosition({
|
||||
steps: mockSteps,
|
||||
stepId: 'step-1',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
steps: mockSteps,
|
||||
index: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('should find the last step position', () => {
|
||||
const result = findStepPosition({
|
||||
steps: mockSteps,
|
||||
stepId: 'step-3',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
steps: mockSteps,
|
||||
index: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return undefined for non-existent stepId', () => {
|
||||
const result = findStepPosition({
|
||||
steps: mockSteps,
|
||||
stepId: 'non-existent-step',
|
||||
});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should work with empty steps array', () => {
|
||||
const result = findStepPosition({
|
||||
steps: [],
|
||||
stepId: 'step-1',
|
||||
});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should work with single step array', () => {
|
||||
const singleStep = [mockSteps[0]];
|
||||
const result = findStepPosition({
|
||||
steps: singleStep,
|
||||
stepId: 'step-1',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
steps: singleStep,
|
||||
index: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
import { WorkflowAction, WorkflowTrigger } from '@/workflow/types/Workflow';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { getStepDefinitionOrThrow } from '../getStepDefinitionOrThrow';
|
||||
|
||||
describe('getStepDefinitionOrThrow', () => {
|
||||
const mockTrigger: WorkflowTrigger = {
|
||||
type: 'DATABASE_EVENT',
|
||||
settings: {
|
||||
eventName: 'company.created',
|
||||
outputSchema: {},
|
||||
},
|
||||
};
|
||||
|
||||
const mockSteps: WorkflowAction[] = [
|
||||
{
|
||||
id: 'step-1',
|
||||
name: 'Create Record',
|
||||
type: 'CREATE_RECORD',
|
||||
valid: true,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
input: {
|
||||
objectName: 'Company',
|
||||
objectRecord: {},
|
||||
},
|
||||
outputSchema: {},
|
||||
},
|
||||
} as WorkflowAction,
|
||||
{
|
||||
id: 'step-2',
|
||||
name: 'Update Record',
|
||||
type: 'UPDATE_RECORD',
|
||||
valid: true,
|
||||
settings: {
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
input: {
|
||||
objectName: 'Company',
|
||||
objectRecord: {},
|
||||
objectRecordId: 'test-id',
|
||||
fieldsToUpdate: ['name'],
|
||||
},
|
||||
outputSchema: {},
|
||||
},
|
||||
} as WorkflowAction,
|
||||
];
|
||||
|
||||
describe('when stepId is TRIGGER_STEP_ID', () => {
|
||||
it('should return trigger definition when trigger is provided', () => {
|
||||
const result = getStepDefinitionOrThrow({
|
||||
stepId: TRIGGER_STEP_ID,
|
||||
trigger: mockTrigger,
|
||||
steps: mockSteps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'trigger',
|
||||
definition: mockTrigger,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return undefined trigger definition when trigger is null', () => {
|
||||
const result = getStepDefinitionOrThrow({
|
||||
stepId: TRIGGER_STEP_ID,
|
||||
trigger: null,
|
||||
steps: mockSteps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'trigger',
|
||||
definition: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when stepId is not TRIGGER_STEP_ID', () => {
|
||||
it('should throw error when steps is null', () => {
|
||||
expect(() => {
|
||||
getStepDefinitionOrThrow({
|
||||
stepId: 'step-1',
|
||||
trigger: mockTrigger,
|
||||
steps: null,
|
||||
});
|
||||
}).toThrow(
|
||||
'Malformed workflow version: missing steps information; be sure to create at least one step before trying to edit one',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return action definition for existing step', () => {
|
||||
const result = getStepDefinitionOrThrow({
|
||||
stepId: 'step-1',
|
||||
trigger: mockTrigger,
|
||||
steps: mockSteps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'action',
|
||||
definition: mockSteps[0],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return action definition for second step', () => {
|
||||
const result = getStepDefinitionOrThrow({
|
||||
stepId: 'step-2',
|
||||
trigger: mockTrigger,
|
||||
steps: mockSteps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'action',
|
||||
definition: mockSteps[1],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return undefined for non-existent step', () => {
|
||||
const result = getStepDefinitionOrThrow({
|
||||
stepId: 'non-existent-step',
|
||||
trigger: mockTrigger,
|
||||
steps: mockSteps,
|
||||
});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should work with empty steps array', () => {
|
||||
const result = getStepDefinitionOrThrow({
|
||||
stepId: 'step-1',
|
||||
trigger: mockTrigger,
|
||||
steps: [],
|
||||
});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import { getStepOutputSchemaFamilyStateKey } from '../getStepOutputSchemaFamilyStateKey';
|
||||
|
||||
describe('getStepOutputSchemaFamilyStateKey', () => {
|
||||
it('should concatenate workflowVersionId and stepId with a dash', () => {
|
||||
const workflowVersionId = 'workflow-version-123';
|
||||
const stepId = 'step-456';
|
||||
|
||||
const result = getStepOutputSchemaFamilyStateKey(workflowVersionId, stepId);
|
||||
|
||||
expect(result).toBe('workflow-version-123-step-456');
|
||||
});
|
||||
|
||||
it('should handle empty strings', () => {
|
||||
const workflowVersionId = '';
|
||||
const stepId = '';
|
||||
|
||||
const result = getStepOutputSchemaFamilyStateKey(workflowVersionId, stepId);
|
||||
|
||||
expect(result).toBe('-');
|
||||
});
|
||||
|
||||
it('should handle UUID format IDs', () => {
|
||||
const workflowVersionId = '550e8400-e29b-41d4-a716-446655440000';
|
||||
const stepId = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
|
||||
|
||||
const result = getStepOutputSchemaFamilyStateKey(workflowVersionId, stepId);
|
||||
|
||||
expect(result).toBe(
|
||||
'550e8400-e29b-41d4-a716-446655440000-6ba7b810-9dad-11d1-80b4-00c04fd430c8',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle special characters in IDs', () => {
|
||||
const workflowVersionId = 'workflow_version.123';
|
||||
const stepId = 'step@456';
|
||||
|
||||
const result = getStepOutputSchemaFamilyStateKey(workflowVersionId, stepId);
|
||||
|
||||
expect(result).toBe('workflow_version.123-step@456');
|
||||
});
|
||||
|
||||
it('should handle numeric IDs', () => {
|
||||
const workflowVersionId = '123';
|
||||
const stepId = '456';
|
||||
|
||||
const result = getStepOutputSchemaFamilyStateKey(workflowVersionId, stepId);
|
||||
|
||||
expect(result).toBe('123-456');
|
||||
});
|
||||
});
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import { getWorkflowVisualizerComponentInstanceId } from '../getWorkflowVisualizerComponentInstanceId';
|
||||
|
||||
describe('getWorkflowVisualizerComponentInstanceId', () => {
|
||||
it('should return the same recordId that was passed in', () => {
|
||||
const recordId = 'test-record-id-123';
|
||||
|
||||
const result = getWorkflowVisualizerComponentInstanceId({ recordId });
|
||||
|
||||
expect(result).toBe(recordId);
|
||||
});
|
||||
|
||||
it('should return empty string when recordId is empty', () => {
|
||||
const recordId = '';
|
||||
|
||||
const result = getWorkflowVisualizerComponentInstanceId({ recordId });
|
||||
|
||||
expect(result).toBe('');
|
||||
});
|
||||
|
||||
it('should handle UUID format recordIds', () => {
|
||||
const recordId = '550e8400-e29b-41d4-a716-446655440000';
|
||||
|
||||
const result = getWorkflowVisualizerComponentInstanceId({ recordId });
|
||||
|
||||
expect(result).toBe(recordId);
|
||||
});
|
||||
|
||||
it('should handle special characters in recordId', () => {
|
||||
const recordId = 'test-record-id_with.special@chars';
|
||||
|
||||
const result = getWorkflowVisualizerComponentInstanceId({ recordId });
|
||||
|
||||
expect(result).toBe(recordId);
|
||||
});
|
||||
});
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
import { splitWorkflowTriggerEventName } from '../splitWorkflowTriggerEventName';
|
||||
|
||||
describe('splitWorkflowTriggerEventName', () => {
|
||||
it('should split a basic event name into objectType and event', () => {
|
||||
const eventName = 'company.created';
|
||||
|
||||
const result = splitWorkflowTriggerEventName(eventName);
|
||||
|
||||
expect(result).toEqual({
|
||||
objectType: 'company',
|
||||
event: 'created',
|
||||
});
|
||||
});
|
||||
|
||||
it('should split event name with updated event', () => {
|
||||
const eventName = 'person.updated';
|
||||
|
||||
const result = splitWorkflowTriggerEventName(eventName);
|
||||
|
||||
expect(result).toEqual({
|
||||
objectType: 'person',
|
||||
event: 'updated',
|
||||
});
|
||||
});
|
||||
|
||||
it('should split event name with deleted event', () => {
|
||||
const eventName = 'opportunity.deleted';
|
||||
|
||||
const result = splitWorkflowTriggerEventName(eventName);
|
||||
|
||||
expect(result).toEqual({
|
||||
objectType: 'opportunity',
|
||||
event: 'deleted',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle camelCase object types', () => {
|
||||
const eventName = 'activityTarget.created';
|
||||
|
||||
const result = splitWorkflowTriggerEventName(eventName);
|
||||
|
||||
expect(result).toEqual({
|
||||
objectType: 'activityTarget',
|
||||
event: 'created',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle event names with underscores', () => {
|
||||
const eventName = 'custom_object.field_updated';
|
||||
|
||||
const result = splitWorkflowTriggerEventName(eventName);
|
||||
|
||||
expect(result).toEqual({
|
||||
objectType: 'custom_object',
|
||||
event: 'field_updated',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle event names without dots', () => {
|
||||
const eventName = 'invalidEventName';
|
||||
|
||||
const result = splitWorkflowTriggerEventName(eventName);
|
||||
|
||||
expect(result).toEqual({
|
||||
objectType: 'invalidEventName',
|
||||
event: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle empty string', () => {
|
||||
const eventName = '';
|
||||
|
||||
const result = splitWorkflowTriggerEventName(eventName);
|
||||
|
||||
expect(result).toEqual({
|
||||
objectType: '',
|
||||
event: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle event name starting with dot', () => {
|
||||
const eventName = '.created';
|
||||
|
||||
const result = splitWorkflowTriggerEventName(eventName);
|
||||
|
||||
expect(result).toEqual({
|
||||
objectType: '',
|
||||
event: 'created',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle event name ending with dot', () => {
|
||||
const eventName = 'company.';
|
||||
|
||||
const result = splitWorkflowTriggerEventName(eventName);
|
||||
|
||||
expect(result).toEqual({
|
||||
objectType: 'company',
|
||||
event: '',
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user