[Fix] Handle deprecated viewFilterOperand in workflows run & on interface (#14837)
In this PR https://github.com/twentyhq/twenty/pull/14785 we deprecated viewFilterOperand (from camelCase to capital snakeCase values), but we had not migrated the existing workflow steps values. Hence they cannot be executed! Let's fix that by dealing with both operands, new and deprecated, to mitigate the issue. Then we will add a command to migrate the values.
This commit is contained in:
+25
-10
@@ -1,19 +1,30 @@
|
||||
import { useRecoilComponentFamilyState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyState';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { type FilterSettings } from '@/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowEditActionFilter';
|
||||
import { currentStepFilterGroupsComponentState } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/currentStepFilterGroupsComponentState';
|
||||
import { currentStepFiltersComponentState } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/currentStepFiltersComponentState';
|
||||
import { hasInitializedCurrentStepFilterGroupsComponentFamilyState } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/hasInitializedCurrentStepFilterGroupsComponentFamilyState';
|
||||
import { hasInitializedCurrentStepFiltersComponentFamilyState } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/hasInitializedCurrentStepFiltersComponentFamilyState';
|
||||
import { useEffect } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import {
|
||||
type StepFilterGroup,
|
||||
type StepFilterWithPotentiallyDeprecatedOperand,
|
||||
} from 'twenty-shared/types';
|
||||
import {
|
||||
convertViewFilterOperandToCoreOperand,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
type FilterSettingsWithPotentiallyDeprecatedOperand = {
|
||||
stepFilterGroups?: StepFilterGroup[];
|
||||
stepFilters?: StepFilterWithPotentiallyDeprecatedOperand[];
|
||||
};
|
||||
|
||||
export const WorkflowEditActionFilterBodyEffect = ({
|
||||
stepId,
|
||||
defaultValue,
|
||||
}: {
|
||||
stepId: string;
|
||||
defaultValue?: FilterSettings;
|
||||
defaultValue?: FilterSettingsWithPotentiallyDeprecatedOperand;
|
||||
}) => {
|
||||
const [
|
||||
hasInitializedCurrentStepFilters,
|
||||
@@ -39,19 +50,23 @@ export const WorkflowEditActionFilterBodyEffect = ({
|
||||
currentStepFilterGroupsComponentState,
|
||||
);
|
||||
|
||||
const stepFiltersConverted = useMemo(() => {
|
||||
return defaultValue?.stepFilters?.map((filter) => ({
|
||||
...filter,
|
||||
operand: convertViewFilterOperandToCoreOperand(filter.operand),
|
||||
}));
|
||||
}, [defaultValue?.stepFilters]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!hasInitializedCurrentStepFilters &&
|
||||
isDefined(defaultValue?.stepFilters)
|
||||
) {
|
||||
setCurrentStepFilters(defaultValue.stepFilters ?? []);
|
||||
if (!hasInitializedCurrentStepFilters && isDefined(stepFiltersConverted)) {
|
||||
setCurrentStepFilters(stepFiltersConverted ?? []);
|
||||
setHasInitializedCurrentStepFilters(true);
|
||||
}
|
||||
}, [
|
||||
setCurrentStepFilters,
|
||||
hasInitializedCurrentStepFilters,
|
||||
setHasInitializedCurrentStepFilters,
|
||||
defaultValue?.stepFilters,
|
||||
stepFiltersConverted,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
+1
-1
@@ -2,6 +2,7 @@ import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Command } from 'nest-commander';
|
||||
import { type ViewFilterOperand as SharedViewFilterOperand } from 'twenty-shared/types';
|
||||
import { convertViewFilterOperandToCoreOperand } from 'twenty-shared/utils';
|
||||
import { DataSource, In, Repository, type QueryRunner } from 'typeorm';
|
||||
import { type QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
|
||||
|
||||
@@ -30,7 +31,6 @@ import { type ViewFilterWorkspaceEntity } from 'src/modules/view/standard-object
|
||||
import { type ViewGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-group.workspace-entity';
|
||||
import { type ViewSortWorkspaceEntity } from 'src/modules/view/standard-objects/view-sort.workspace-entity';
|
||||
import { type ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||
import { convertViewFilterOperandToCoreOperand } from 'src/modules/view/utils/convert-view-filter-operand-to-core-operand.util';
|
||||
import { convertViewFilterWorkspaceValueToCoreValue } from 'src/modules/view/utils/convert-view-filter-workspace-value-to-core-value';
|
||||
|
||||
@Command({
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
import { ViewFilterOperand } from 'twenty-shared/types';
|
||||
|
||||
const operandMapping: Record<string, ViewFilterOperand> = {
|
||||
Is: ViewFilterOperand.IS,
|
||||
IsNotNull: ViewFilterOperand.IS_NOT_NULL,
|
||||
IsNot: ViewFilterOperand.IS_NOT,
|
||||
LessThanOrEqual: ViewFilterOperand.LESS_THAN_OR_EQUAL,
|
||||
GreaterThanOrEqual: ViewFilterOperand.GREATER_THAN_OR_EQUAL,
|
||||
IsBefore: ViewFilterOperand.IS_BEFORE,
|
||||
IsAfter: ViewFilterOperand.IS_AFTER,
|
||||
Contains: ViewFilterOperand.CONTAINS,
|
||||
DoesNotContain: ViewFilterOperand.DOES_NOT_CONTAIN,
|
||||
IsEmpty: ViewFilterOperand.IS_EMPTY,
|
||||
IsNotEmpty: ViewFilterOperand.IS_NOT_EMPTY,
|
||||
IsRelative: ViewFilterOperand.IS_RELATIVE,
|
||||
IsInPast: ViewFilterOperand.IS_IN_PAST,
|
||||
IsInFuture: ViewFilterOperand.IS_IN_FUTURE,
|
||||
IsToday: ViewFilterOperand.IS_TODAY,
|
||||
VectorSearch: ViewFilterOperand.VECTOR_SEARCH,
|
||||
};
|
||||
|
||||
export const convertViewFilterOperandToCoreOperand = (
|
||||
sharedOperand: string,
|
||||
): ViewFilterOperand => {
|
||||
return operandMapping[sharedOperand];
|
||||
};
|
||||
+33
-11
@@ -8,23 +8,45 @@ import {
|
||||
type StepFilter,
|
||||
type StepFilterGroup,
|
||||
ViewFilterOperand,
|
||||
type ViewFilterOperandDeprecated,
|
||||
} from 'twenty-shared/types';
|
||||
import { convertViewFilterOperandToCoreOperand as convertViewFilterOperandDeprecated } from 'twenty-shared/utils';
|
||||
|
||||
import { parseAndEvaluateRelativeDateFilter } from 'src/modules/workflow/workflow-executor/workflow-actions/filter/utils/parse-and-evaluate-relative-date-filter.util';
|
||||
|
||||
type ResolvedFilter = Omit<StepFilter, 'value' | 'stepOutputKey'> & {
|
||||
type ResolvedFilterWithPotentiallyDeprecatedOperand = Omit<
|
||||
StepFilter,
|
||||
'value' | 'stepOutputKey' | 'operand'
|
||||
> & {
|
||||
rightOperand: unknown;
|
||||
leftOperand: unknown;
|
||||
operand: ViewFilterOperand | ViewFilterOperandDeprecated;
|
||||
};
|
||||
|
||||
function evaluateFilter(filter: ResolvedFilter): boolean {
|
||||
type ResolvedFilter = Omit<
|
||||
StepFilter,
|
||||
'value' | 'stepOutputKey' | 'operand'
|
||||
> & {
|
||||
rightOperand: unknown;
|
||||
leftOperand: unknown;
|
||||
operand: ViewFilterOperand;
|
||||
};
|
||||
|
||||
function evaluateFilter(
|
||||
filter: ResolvedFilterWithPotentiallyDeprecatedOperand,
|
||||
): boolean {
|
||||
const filterWithConvertedOperand = {
|
||||
...filter,
|
||||
operand: convertViewFilterOperandDeprecated(filter.operand),
|
||||
};
|
||||
|
||||
switch (filter.type) {
|
||||
case 'NUMBER':
|
||||
case 'NUMERIC':
|
||||
return evaluateNumberFilter(filter);
|
||||
return evaluateNumberFilter(filterWithConvertedOperand);
|
||||
case 'DATE':
|
||||
case 'DATE_TIME':
|
||||
return evaluateDateFilter(filter);
|
||||
return evaluateDateFilter(filterWithConvertedOperand);
|
||||
case 'TEXT':
|
||||
case 'MULTI_SELECT':
|
||||
case 'FULL_NAME':
|
||||
@@ -34,19 +56,19 @@ function evaluateFilter(filter: ResolvedFilter): boolean {
|
||||
case 'LINKS':
|
||||
case 'ARRAY':
|
||||
case 'RAW_JSON':
|
||||
return evaluateTextAndArrayFilter(filter);
|
||||
return evaluateTextAndArrayFilter(filterWithConvertedOperand);
|
||||
case 'SELECT':
|
||||
return evaluateSelectFilter(filter);
|
||||
return evaluateSelectFilter(filterWithConvertedOperand);
|
||||
case 'BOOLEAN':
|
||||
return evaluateBooleanFilter(filter);
|
||||
return evaluateBooleanFilter(filterWithConvertedOperand);
|
||||
case 'UUID':
|
||||
return evaluateUuidFilter(filter);
|
||||
return evaluateUuidFilter(filterWithConvertedOperand);
|
||||
case 'RELATION':
|
||||
return evaluateRelationFilter(filter);
|
||||
return evaluateRelationFilter(filterWithConvertedOperand);
|
||||
case 'CURRENCY':
|
||||
return evaluateCurrencyFilter(filter);
|
||||
return evaluateCurrencyFilter(filterWithConvertedOperand);
|
||||
default:
|
||||
return evaluateDefaultFilter(filter);
|
||||
return evaluateDefaultFilter(filterWithConvertedOperand);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type ViewFilterOperandDeprecated } from '@/types/ViewFilterOperandDeprecated';
|
||||
import { type ViewFilterOperand } from './ViewFilterOperand';
|
||||
|
||||
export enum StepLogicalOperator {
|
||||
@@ -24,3 +25,16 @@ export type StepFilter = {
|
||||
compositeFieldSubFieldName?: string;
|
||||
isFullRecord?: boolean;
|
||||
};
|
||||
|
||||
export type StepFilterWithPotentiallyDeprecatedOperand = {
|
||||
id: string;
|
||||
type: string;
|
||||
stepOutputKey: string;
|
||||
operand: ViewFilterOperand | ViewFilterOperandDeprecated;
|
||||
value: string;
|
||||
stepFilterGroupId: string;
|
||||
positionInStepFilterGroup?: number;
|
||||
fieldMetadataId?: string;
|
||||
compositeFieldSubFieldName?: string;
|
||||
isFullRecord?: boolean;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
export enum ViewFilterOperandDeprecated {
|
||||
Is = 'is',
|
||||
IsNotNull = 'isNotNull',
|
||||
IsNot = 'isNot',
|
||||
LessThanOrEqual = 'lessThanOrEqual',
|
||||
GreaterThanOrEqual = 'greaterThanOrEqual',
|
||||
IsBefore = 'isBefore',
|
||||
IsAfter = 'isAfter',
|
||||
Contains = 'contains',
|
||||
DoesNotContain = 'doesNotContain',
|
||||
IsEmpty = 'isEmpty',
|
||||
IsNotEmpty = 'isNotEmpty',
|
||||
IsRelative = 'isRelative',
|
||||
IsInPast = 'isInPast',
|
||||
IsInFuture = 'isInFuture',
|
||||
IsToday = 'isToday',
|
||||
VectorSearch = 'vectorSearch',
|
||||
}
|
||||
|
||||
@@ -82,7 +82,12 @@ export { DEFAULT_RELATIVE_DATE_VALUE } from './RelativeDateValue';
|
||||
export type { RestrictedFieldPermissions } from './RestrictedFieldPermissions';
|
||||
export type { RestrictedFieldsPermissions } from './RestrictedFieldsPermissions';
|
||||
export { SettingsPath } from './SettingsPath';
|
||||
export type { StepFilterGroup, StepFilter } from './StepFilters';
|
||||
export type {
|
||||
StepFilterGroup,
|
||||
StepFilter,
|
||||
StepFilterWithPotentiallyDeprecatedOperand,
|
||||
} from './StepFilters';
|
||||
export { StepLogicalOperator } from './StepFilters';
|
||||
export { TwoFactorAuthenticationStrategy } from './TwoFactorAuthenticationStrategy';
|
||||
export { ViewFilterOperand } from './ViewFilterOperand';
|
||||
export { ViewFilterOperandDeprecated } from './ViewFilterOperandDeprecated';
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import { ViewFilterOperand, ViewFilterOperandDeprecated } from '@/types';
|
||||
|
||||
const operandMapping: Record<string, ViewFilterOperand> = {
|
||||
Is: ViewFilterOperand.IS,
|
||||
IsNotNull: ViewFilterOperand.IS_NOT_NULL,
|
||||
IsNot: ViewFilterOperand.IS_NOT,
|
||||
LessThanOrEqual: ViewFilterOperand.LESS_THAN_OR_EQUAL,
|
||||
GreaterThanOrEqual: ViewFilterOperand.GREATER_THAN_OR_EQUAL,
|
||||
IsBefore: ViewFilterOperand.IS_BEFORE,
|
||||
IsAfter: ViewFilterOperand.IS_AFTER,
|
||||
Contains: ViewFilterOperand.CONTAINS,
|
||||
DoesNotContain: ViewFilterOperand.DOES_NOT_CONTAIN,
|
||||
IsEmpty: ViewFilterOperand.IS_EMPTY,
|
||||
IsNotEmpty: ViewFilterOperand.IS_NOT_EMPTY,
|
||||
IsRelative: ViewFilterOperand.IS_RELATIVE,
|
||||
IsInPast: ViewFilterOperand.IS_IN_PAST,
|
||||
IsInFuture: ViewFilterOperand.IS_IN_FUTURE,
|
||||
IsToday: ViewFilterOperand.IS_TODAY,
|
||||
VectorSearch: ViewFilterOperand.VECTOR_SEARCH,
|
||||
[ViewFilterOperandDeprecated.Is]: ViewFilterOperand.IS,
|
||||
[ViewFilterOperandDeprecated.IsNotNull]: ViewFilterOperand.IS_NOT_NULL,
|
||||
[ViewFilterOperandDeprecated.IsNot]: ViewFilterOperand.IS_NOT,
|
||||
[ViewFilterOperandDeprecated.LessThanOrEqual]: ViewFilterOperand.LESS_THAN_OR_EQUAL,
|
||||
[ViewFilterOperandDeprecated.GreaterThanOrEqual]: ViewFilterOperand.GREATER_THAN_OR_EQUAL,
|
||||
[ViewFilterOperandDeprecated.IsBefore]: ViewFilterOperand.IS_BEFORE,
|
||||
[ViewFilterOperandDeprecated.IsAfter]: ViewFilterOperand.IS_AFTER,
|
||||
[ViewFilterOperandDeprecated.Contains]: ViewFilterOperand.CONTAINS,
|
||||
[ViewFilterOperandDeprecated.DoesNotContain]: ViewFilterOperand.DOES_NOT_CONTAIN,
|
||||
[ViewFilterOperandDeprecated.IsEmpty]: ViewFilterOperand.IS_EMPTY,
|
||||
[ViewFilterOperandDeprecated.IsNotEmpty]: ViewFilterOperand.IS_NOT_EMPTY,
|
||||
[ViewFilterOperandDeprecated.IsRelative]: ViewFilterOperand.IS_RELATIVE,
|
||||
[ViewFilterOperandDeprecated.IsInPast]: ViewFilterOperand.IS_IN_PAST,
|
||||
[ViewFilterOperandDeprecated.IsInFuture]: ViewFilterOperand.IS_IN_FUTURE,
|
||||
[ViewFilterOperandDeprecated.IsToday]: ViewFilterOperand.IS_TODAY,
|
||||
[ViewFilterOperandDeprecated.VectorSearch]: ViewFilterOperand.VECTOR_SEARCH,
|
||||
[ViewFilterOperand.IS]: ViewFilterOperand.IS,
|
||||
[ViewFilterOperand.IS_NOT_NULL]: ViewFilterOperand.IS_NOT_NULL,
|
||||
[ViewFilterOperand.IS_NOT]: ViewFilterOperand.IS_NOT,
|
||||
[ViewFilterOperand.LESS_THAN_OR_EQUAL]: ViewFilterOperand.LESS_THAN_OR_EQUAL,
|
||||
[ViewFilterOperand.GREATER_THAN_OR_EQUAL]:
|
||||
ViewFilterOperand.GREATER_THAN_OR_EQUAL,
|
||||
[ViewFilterOperand.IS_BEFORE]: ViewFilterOperand.IS_BEFORE,
|
||||
[ViewFilterOperand.IS_AFTER]: ViewFilterOperand.IS_AFTER,
|
||||
[ViewFilterOperand.CONTAINS]: ViewFilterOperand.CONTAINS,
|
||||
[ViewFilterOperand.DOES_NOT_CONTAIN]: ViewFilterOperand.DOES_NOT_CONTAIN,
|
||||
[ViewFilterOperand.IS_EMPTY]: ViewFilterOperand.IS_EMPTY,
|
||||
[ViewFilterOperand.IS_NOT_EMPTY]: ViewFilterOperand.IS_NOT_EMPTY,
|
||||
[ViewFilterOperand.IS_RELATIVE]: ViewFilterOperand.IS_RELATIVE,
|
||||
[ViewFilterOperand.IS_IN_PAST]: ViewFilterOperand.IS_IN_PAST,
|
||||
[ViewFilterOperand.IS_IN_FUTURE]: ViewFilterOperand.IS_IN_FUTURE,
|
||||
[ViewFilterOperand.IS_TODAY]: ViewFilterOperand.IS_TODAY,
|
||||
[ViewFilterOperand.VECTOR_SEARCH]: ViewFilterOperand.VECTOR_SEARCH,
|
||||
};
|
||||
|
||||
export const convertViewFilterOperandToCoreOperand = (
|
||||
sharedOperand: string,
|
||||
): ViewFilterOperand => {
|
||||
return operandMapping[sharedOperand];
|
||||
};
|
||||
@@ -36,6 +36,7 @@ export type {
|
||||
export { turnRecordFilterGroupsIntoGqlOperationFilter } from './filter/turnRecordFilterGroupIntoGqlOperationFilter';
|
||||
export { turnRecordFilterIntoRecordGqlOperationFilter } from './filter/turnRecordFilterIntoGqlOperationFilter';
|
||||
export { combineFilters } from './filter/utils/combineFilters';
|
||||
export { convertViewFilterOperandToCoreOperand } from './filter/utils/convert-view-filter-operand-to-core-operand.util';
|
||||
export { convertViewFilterValueToString } from './filter/utils/convertViewFilterValueToString';
|
||||
export { createAnyFieldRecordFilterBaseProperties } from './filter/utils/createAnyFieldRecordFilterBaseProperties';
|
||||
export {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ViewFilterOperandDeprecated } from '@/types';
|
||||
import { z } from 'zod';
|
||||
import { StepLogicalOperator } from '../../types/StepFilters';
|
||||
import { ViewFilterOperand } from '../../types/ViewFilterOperand';
|
||||
@@ -19,7 +20,7 @@ export const workflowFilterActionSettingsSchema =
|
||||
id: z.string(),
|
||||
type: z.string(),
|
||||
stepOutputKey: z.string(),
|
||||
operand: z.enum(ViewFilterOperand),
|
||||
operand: z.enum(ViewFilterOperand).or(z.enum(ViewFilterOperandDeprecated)),
|
||||
value: z.string(),
|
||||
stepFilterGroupId: z.string(),
|
||||
positionInStepFilterGroup: z.number().optional(),
|
||||
|
||||
Reference in New Issue
Block a user