Add delete record action (#8908)
<img width="1272" alt="Capture d’écran 2024-12-05 à 18 04 19" src="https://github.com/user-attachments/assets/dcbcc761-2f6d-4b6d-9e10-0f5b25d12c39"> - Adding new action - Adding tests - Moving into action folder
This commit is contained in:
@ -1,4 +1,3 @@
|
|||||||
import { lazy, Suspense } from 'react';
|
|
||||||
import { WorkflowEditTriggerDatabaseEventForm } from '@/workflow/components/WorkflowEditTriggerDatabaseEventForm';
|
import { WorkflowEditTriggerDatabaseEventForm } from '@/workflow/components/WorkflowEditTriggerDatabaseEventForm';
|
||||||
import { WorkflowEditTriggerManualForm } from '@/workflow/components/WorkflowEditTriggerManualForm';
|
import { WorkflowEditTriggerManualForm } from '@/workflow/components/WorkflowEditTriggerManualForm';
|
||||||
import {
|
import {
|
||||||
@ -8,11 +7,14 @@ import {
|
|||||||
} from '@/workflow/types/Workflow';
|
} from '@/workflow/types/Workflow';
|
||||||
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
|
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
|
||||||
import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow';
|
import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow';
|
||||||
import { isWorkflowRecordCreateAction } from '@/workflow/utils/isWorkflowRecordCreateAction';
|
|
||||||
import { isWorkflowRecordUpdateAction } from '@/workflow/utils/isWorkflowRecordUpdateAction';
|
|
||||||
import { WorkflowEditActionFormRecordCreate } from '@/workflow/workflow-actions/components/WorkflowEditActionFormRecordCreate';
|
import { WorkflowEditActionFormRecordCreate } from '@/workflow/workflow-actions/components/WorkflowEditActionFormRecordCreate';
|
||||||
|
import { WorkflowEditActionFormRecordDelete } from '@/workflow/workflow-actions/components/WorkflowEditActionFormRecordDelete';
|
||||||
import { WorkflowEditActionFormRecordUpdate } from '@/workflow/workflow-actions/components/WorkflowEditActionFormRecordUpdate';
|
import { WorkflowEditActionFormRecordUpdate } from '@/workflow/workflow-actions/components/WorkflowEditActionFormRecordUpdate';
|
||||||
import { WorkflowEditActionFormSendEmail } from '@/workflow/workflow-actions/components/WorkflowEditActionFormSendEmail';
|
import { WorkflowEditActionFormSendEmail } from '@/workflow/workflow-actions/components/WorkflowEditActionFormSendEmail';
|
||||||
|
import { isWorkflowRecordCreateAction } from '@/workflow/workflow-actions/utils/isWorkflowRecordCreateAction';
|
||||||
|
import { isWorkflowRecordDeleteAction } from '@/workflow/workflow-actions/utils/isWorkflowRecordDeleteAction';
|
||||||
|
import { isWorkflowRecordUpdateAction } from '@/workflow/workflow-actions/utils/isWorkflowRecordUpdateAction';
|
||||||
|
import { lazy, Suspense } from 'react';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
import { RightDrawerSkeletonLoader } from '~/loading/components/RightDrawerSkeletonLoader';
|
import { RightDrawerSkeletonLoader } from '~/loading/components/RightDrawerSkeletonLoader';
|
||||||
|
|
||||||
@ -124,6 +126,15 @@ export const WorkflowStepDetail = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isWorkflowRecordDeleteAction(stepDefinition.definition)) {
|
||||||
|
return (
|
||||||
|
<WorkflowEditActionFormRecordDelete
|
||||||
|
action={stepDefinition.definition}
|
||||||
|
actionOptions={props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,4 +30,9 @@ export const ACTIONS: Array<{
|
|||||||
type: 'RECORD_CRUD.UPDATE',
|
type: 'RECORD_CRUD.UPDATE',
|
||||||
icon: IconAddressBook,
|
icon: IconAddressBook,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Delete Record',
|
||||||
|
type: 'RECORD_CRUD.DELETE',
|
||||||
|
icon: IconAddressBook,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,79 +0,0 @@
|
|||||||
import {
|
|
||||||
WorkflowCodeAction,
|
|
||||||
WorkflowRecordCRUDAction,
|
|
||||||
} from '@/workflow/types/Workflow';
|
|
||||||
import { isWorkflowRecordCreateAction } from '../isWorkflowRecordCreateAction';
|
|
||||||
|
|
||||||
it('returns false when providing an action that is not Record Create', () => {
|
|
||||||
const codeAction: WorkflowCodeAction = {
|
|
||||||
type: 'CODE',
|
|
||||||
id: '',
|
|
||||||
name: '',
|
|
||||||
settings: {
|
|
||||||
errorHandlingOptions: {
|
|
||||||
continueOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
retryOnFailure: {
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
input: {
|
|
||||||
serverlessFunctionId: '',
|
|
||||||
serverlessFunctionVersion: '',
|
|
||||||
serverlessFunctionInput: {},
|
|
||||||
},
|
|
||||||
outputSchema: {},
|
|
||||||
},
|
|
||||||
valid: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(isWorkflowRecordCreateAction(codeAction)).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false for Record Update', () => {
|
|
||||||
const codeAction: WorkflowRecordCRUDAction = {
|
|
||||||
type: 'RECORD_CRUD',
|
|
||||||
id: '',
|
|
||||||
name: '',
|
|
||||||
settings: {
|
|
||||||
errorHandlingOptions: {
|
|
||||||
continueOnFailure: { value: false },
|
|
||||||
retryOnFailure: { value: false },
|
|
||||||
},
|
|
||||||
input: {
|
|
||||||
type: 'UPDATE',
|
|
||||||
objectName: '',
|
|
||||||
objectRecord: {},
|
|
||||||
objectRecordId: '',
|
|
||||||
},
|
|
||||||
outputSchema: {},
|
|
||||||
},
|
|
||||||
valid: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(isWorkflowRecordCreateAction(codeAction)).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns true for Record Create', () => {
|
|
||||||
const codeAction: WorkflowRecordCRUDAction = {
|
|
||||||
type: 'RECORD_CRUD',
|
|
||||||
id: '',
|
|
||||||
name: '',
|
|
||||||
settings: {
|
|
||||||
errorHandlingOptions: {
|
|
||||||
continueOnFailure: { value: false },
|
|
||||||
retryOnFailure: { value: false },
|
|
||||||
},
|
|
||||||
input: {
|
|
||||||
type: 'CREATE',
|
|
||||||
objectName: '',
|
|
||||||
objectRecord: {},
|
|
||||||
},
|
|
||||||
outputSchema: {},
|
|
||||||
},
|
|
||||||
valid: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(isWorkflowRecordCreateAction(codeAction)).toBe(true);
|
|
||||||
});
|
|
||||||
@ -0,0 +1,170 @@
|
|||||||
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
|
import { Select, SelectOption } from '@/ui/input/components/Select';
|
||||||
|
import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase';
|
||||||
|
import { WorkflowSingleRecordPicker } from '@/workflow/components/WorkflowSingleRecordPicker';
|
||||||
|
import { WorkflowRecordDeleteAction } from '@/workflow/types/Workflow';
|
||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import {
|
||||||
|
HorizontalSeparator,
|
||||||
|
IconAddressBook,
|
||||||
|
isDefined,
|
||||||
|
useIcons,
|
||||||
|
} from 'twenty-ui';
|
||||||
|
|
||||||
|
import { JsonValue } from 'type-fest';
|
||||||
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
|
|
||||||
|
type WorkflowEditActionFormRecordDeleteProps = {
|
||||||
|
action: WorkflowRecordDeleteAction;
|
||||||
|
actionOptions:
|
||||||
|
| {
|
||||||
|
readonly: true;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
readonly?: false;
|
||||||
|
onActionUpdate: (action: WorkflowRecordDeleteAction) => void;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type DeleteRecordFormData = {
|
||||||
|
objectName: string;
|
||||||
|
objectRecordId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WorkflowEditActionFormRecordDelete = ({
|
||||||
|
action,
|
||||||
|
actionOptions,
|
||||||
|
}: WorkflowEditActionFormRecordDeleteProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const { getIcon } = useIcons();
|
||||||
|
|
||||||
|
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
||||||
|
|
||||||
|
const availableMetadata: Array<SelectOption<string>> =
|
||||||
|
activeObjectMetadataItems.map((item) => ({
|
||||||
|
Icon: getIcon(item.icon),
|
||||||
|
label: item.labelPlural,
|
||||||
|
value: item.nameSingular,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const [formData, setFormData] = useState<DeleteRecordFormData>({
|
||||||
|
objectName: action.settings.input.objectName,
|
||||||
|
objectRecordId: action.settings.input.objectRecordId,
|
||||||
|
});
|
||||||
|
const isFormDisabled = actionOptions.readonly;
|
||||||
|
|
||||||
|
const handleFieldChange = (
|
||||||
|
fieldName: keyof DeleteRecordFormData,
|
||||||
|
updatedValue: JsonValue,
|
||||||
|
) => {
|
||||||
|
const newFormData: DeleteRecordFormData = {
|
||||||
|
...formData,
|
||||||
|
[fieldName]: updatedValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
setFormData(newFormData);
|
||||||
|
|
||||||
|
saveAction(newFormData);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFormData({
|
||||||
|
objectName: action.settings.input.objectName,
|
||||||
|
objectRecordId: action.settings.input.objectRecordId,
|
||||||
|
});
|
||||||
|
}, [action.settings.input]);
|
||||||
|
|
||||||
|
const selectedObjectMetadataItemNameSingular = formData.objectName;
|
||||||
|
|
||||||
|
const selectedObjectMetadataItem = activeObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === selectedObjectMetadataItemNameSingular,
|
||||||
|
);
|
||||||
|
if (!isDefined(selectedObjectMetadataItem)) {
|
||||||
|
throw new Error('Should have found the metadata item');
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveAction = useDebouncedCallback(
|
||||||
|
async (formData: DeleteRecordFormData) => {
|
||||||
|
if (actionOptions.readonly === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
objectName: updatedObjectName,
|
||||||
|
objectRecordId: updatedObjectRecordId,
|
||||||
|
} = formData;
|
||||||
|
|
||||||
|
actionOptions.onActionUpdate({
|
||||||
|
...action,
|
||||||
|
settings: {
|
||||||
|
...action.settings,
|
||||||
|
input: {
|
||||||
|
type: 'DELETE',
|
||||||
|
objectName: updatedObjectName,
|
||||||
|
objectRecordId: updatedObjectRecordId ?? '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
1_000,
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
saveAction.flush();
|
||||||
|
};
|
||||||
|
}, [saveAction]);
|
||||||
|
|
||||||
|
const headerTitle = isDefined(action.name) ? action.name : `Delete Record`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WorkflowEditGenericFormBase
|
||||||
|
onTitleChange={(newName: string) => {
|
||||||
|
if (actionOptions.readonly === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionOptions.onActionUpdate({
|
||||||
|
...action,
|
||||||
|
name: newName,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
Icon={IconAddressBook}
|
||||||
|
iconColor={theme.font.color.tertiary}
|
||||||
|
initialTitle={headerTitle}
|
||||||
|
headerType="Action"
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
dropdownId="workflow-edit-action-record-delete-object-name"
|
||||||
|
label="Object"
|
||||||
|
fullWidth
|
||||||
|
disabled={isFormDisabled}
|
||||||
|
value={formData.objectName}
|
||||||
|
emptyOption={{ label: 'Select an option', value: '' }}
|
||||||
|
options={availableMetadata}
|
||||||
|
onChange={(objectName) => {
|
||||||
|
const newFormData: DeleteRecordFormData = {
|
||||||
|
objectName,
|
||||||
|
objectRecordId: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
setFormData(newFormData);
|
||||||
|
|
||||||
|
saveAction(newFormData);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<HorizontalSeparator noMargin />
|
||||||
|
|
||||||
|
<WorkflowSingleRecordPicker
|
||||||
|
label="Record"
|
||||||
|
onChange={(objectRecordId) =>
|
||||||
|
handleFieldChange('objectRecordId', objectRecordId)
|
||||||
|
}
|
||||||
|
objectNameSingular={formData.objectName}
|
||||||
|
defaultValue={formData.objectRecordId}
|
||||||
|
/>
|
||||||
|
</WorkflowEditGenericFormBase>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,11 +1,27 @@
|
|||||||
|
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
|
||||||
|
import { useGetAvailablePackages } from '@/settings/serverless-functions/hooks/useGetAvailablePackages';
|
||||||
|
import { useServerlessFunctionUpdateFormState } from '@/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState';
|
||||||
|
import { useUpdateOneServerlessFunction } from '@/settings/serverless-functions/hooks/useUpdateOneServerlessFunction';
|
||||||
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase';
|
import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase';
|
||||||
|
import { WorkflowVariablePicker } from '@/workflow/components/WorkflowVariablePicker';
|
||||||
|
import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||||
import { FunctionInput } from '@/workflow/types/FunctionInput';
|
import { FunctionInput } from '@/workflow/types/FunctionInput';
|
||||||
import { WorkflowCodeAction } from '@/workflow/types/Workflow';
|
import { WorkflowCodeAction } from '@/workflow/types/Workflow';
|
||||||
import { mergeDefaultFunctionInputAndFunctionInput } from '@/workflow/utils/mergeDefaultFunctionInputAndFunctionInput';
|
import { getDefaultFunctionInputFromInputSchema } from '@/workflow/utils/getDefaultFunctionInputFromInputSchema';
|
||||||
|
import { getFunctionInputSchema } from '@/workflow/utils/getFunctionInputSchema';
|
||||||
import { setNestedValue } from '@/workflow/utils/setNestedValue';
|
import { setNestedValue } from '@/workflow/utils/setNestedValue';
|
||||||
|
import { mergeDefaultFunctionInputAndFunctionInput } from '@/workflow/workflow-actions/utils/mergeDefaultFunctionInputAndFunctionInput';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
import { Monaco } from '@monaco-editor/react';
|
||||||
|
import { editor } from 'monaco-editor';
|
||||||
|
import { AutoTypings } from 'monaco-editor-auto-typings';
|
||||||
import { Fragment, ReactNode, useEffect, useState } from 'react';
|
import { Fragment, ReactNode, useEffect, useState } from 'react';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
import {
|
import {
|
||||||
CodeEditor,
|
CodeEditor,
|
||||||
HorizontalSeparator,
|
HorizontalSeparator,
|
||||||
@ -13,23 +29,7 @@ import {
|
|||||||
isDefined,
|
isDefined,
|
||||||
} from 'twenty-ui';
|
} from 'twenty-ui';
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
import { useServerlessFunctionUpdateFormState } from '@/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState';
|
|
||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
|
||||||
import { usePreventOverlapCallback } from '~/hooks/usePreventOverlapCallback';
|
import { usePreventOverlapCallback } from '~/hooks/usePreventOverlapCallback';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
|
||||||
import { useUpdateOneServerlessFunction } from '@/settings/serverless-functions/hooks/useUpdateOneServerlessFunction';
|
|
||||||
import { useGetAvailablePackages } from '@/settings/serverless-functions/hooks/useGetAvailablePackages';
|
|
||||||
import { AutoTypings } from 'monaco-editor-auto-typings';
|
|
||||||
import { editor } from 'monaco-editor';
|
|
||||||
import { Monaco } from '@monaco-editor/react';
|
|
||||||
import { WorkflowVariablePicker } from '@/workflow/components/WorkflowVariablePicker';
|
|
||||||
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
|
|
||||||
import { getFunctionInputSchema } from '@/workflow/utils/getFunctionInputSchema';
|
|
||||||
import { getDefaultFunctionInputFromInputSchema } from '@/workflow/utils/getDefaultFunctionInputFromInputSchema';
|
|
||||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
|
||||||
import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion';
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|||||||
@ -0,0 +1,81 @@
|
|||||||
|
import {
|
||||||
|
WorkflowCodeAction,
|
||||||
|
WorkflowRecordCRUDAction,
|
||||||
|
} from '@/workflow/types/Workflow';
|
||||||
|
import { isWorkflowRecordCreateAction } from '../isWorkflowRecordCreateAction';
|
||||||
|
|
||||||
|
describe('isWorkflowRecordCreateAction', () => {
|
||||||
|
it('returns false when providing an action that is not Record Create', () => {
|
||||||
|
const action: WorkflowCodeAction = {
|
||||||
|
type: 'CODE',
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
settings: {
|
||||||
|
errorHandlingOptions: {
|
||||||
|
continueOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
retryOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
serverlessFunctionId: '',
|
||||||
|
serverlessFunctionVersion: '',
|
||||||
|
serverlessFunctionInput: {},
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(isWorkflowRecordCreateAction(action)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false for Record Update', () => {
|
||||||
|
const action: WorkflowRecordCRUDAction = {
|
||||||
|
type: 'RECORD_CRUD',
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
settings: {
|
||||||
|
errorHandlingOptions: {
|
||||||
|
continueOnFailure: { value: false },
|
||||||
|
retryOnFailure: { value: false },
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
type: 'UPDATE',
|
||||||
|
objectName: '',
|
||||||
|
objectRecord: {},
|
||||||
|
objectRecordId: '',
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(isWorkflowRecordCreateAction(action)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true for Record Create', () => {
|
||||||
|
const action: WorkflowRecordCRUDAction = {
|
||||||
|
type: 'RECORD_CRUD',
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
settings: {
|
||||||
|
errorHandlingOptions: {
|
||||||
|
continueOnFailure: { value: false },
|
||||||
|
retryOnFailure: { value: false },
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
type: 'CREATE',
|
||||||
|
objectName: '',
|
||||||
|
objectRecord: {},
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(isWorkflowRecordCreateAction(action)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
import {
|
||||||
|
WorkflowCodeAction,
|
||||||
|
WorkflowRecordCRUDAction,
|
||||||
|
} from '@/workflow/types/Workflow';
|
||||||
|
import { isWorkflowRecordDeleteAction } from '../isWorkflowRecordDeleteAction';
|
||||||
|
|
||||||
|
describe('isWorkflowRecordDeleteAction', () => {
|
||||||
|
it('returns false when providing an action that is not Record Delete', () => {
|
||||||
|
const action: WorkflowCodeAction = {
|
||||||
|
type: 'CODE',
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
settings: {
|
||||||
|
errorHandlingOptions: {
|
||||||
|
continueOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
retryOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
serverlessFunctionId: '',
|
||||||
|
serverlessFunctionVersion: '',
|
||||||
|
serverlessFunctionInput: {},
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(isWorkflowRecordDeleteAction(action)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false for Record Update', () => {
|
||||||
|
const action: WorkflowRecordCRUDAction = {
|
||||||
|
type: 'RECORD_CRUD',
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
settings: {
|
||||||
|
errorHandlingOptions: {
|
||||||
|
continueOnFailure: { value: false },
|
||||||
|
retryOnFailure: { value: false },
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
type: 'UPDATE',
|
||||||
|
objectName: '',
|
||||||
|
objectRecord: {},
|
||||||
|
objectRecordId: '',
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(isWorkflowRecordDeleteAction(action)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true for Record Delete', () => {
|
||||||
|
const action: WorkflowRecordCRUDAction = {
|
||||||
|
type: 'RECORD_CRUD',
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
settings: {
|
||||||
|
errorHandlingOptions: {
|
||||||
|
continueOnFailure: { value: false },
|
||||||
|
retryOnFailure: { value: false },
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
type: 'DELETE',
|
||||||
|
objectName: '',
|
||||||
|
objectRecordId: '',
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(isWorkflowRecordDeleteAction(action)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
import {
|
||||||
|
WorkflowCodeAction,
|
||||||
|
WorkflowRecordCRUDAction,
|
||||||
|
} from '@/workflow/types/Workflow';
|
||||||
|
import { isWorkflowRecordUpdateAction } from '../isWorkflowRecordUpdateAction';
|
||||||
|
|
||||||
|
describe('isWorkflowRecordUpdateAction', () => {
|
||||||
|
it('returns false when providing an action that is not Record Create', () => {
|
||||||
|
const action: WorkflowCodeAction = {
|
||||||
|
type: 'CODE',
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
settings: {
|
||||||
|
errorHandlingOptions: {
|
||||||
|
continueOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
retryOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
serverlessFunctionId: '',
|
||||||
|
serverlessFunctionVersion: '',
|
||||||
|
serverlessFunctionInput: {},
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(isWorkflowRecordUpdateAction(action)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true for Record Update', () => {
|
||||||
|
const action: WorkflowRecordCRUDAction = {
|
||||||
|
type: 'RECORD_CRUD',
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
settings: {
|
||||||
|
errorHandlingOptions: {
|
||||||
|
continueOnFailure: { value: false },
|
||||||
|
retryOnFailure: { value: false },
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
type: 'UPDATE',
|
||||||
|
objectName: '',
|
||||||
|
objectRecord: {},
|
||||||
|
objectRecordId: '',
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(isWorkflowRecordUpdateAction(action)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false for Record Create', () => {
|
||||||
|
const action: WorkflowRecordCRUDAction = {
|
||||||
|
type: 'RECORD_CRUD',
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
settings: {
|
||||||
|
errorHandlingOptions: {
|
||||||
|
continueOnFailure: { value: false },
|
||||||
|
retryOnFailure: { value: false },
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
type: 'CREATE',
|
||||||
|
objectName: '',
|
||||||
|
objectRecord: {},
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(isWorkflowRecordUpdateAction(action)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { mergeDefaultFunctionInputAndFunctionInput } from '@/workflow/utils/mergeDefaultFunctionInputAndFunctionInput';
|
import { mergeDefaultFunctionInputAndFunctionInput } from '../mergeDefaultFunctionInputAndFunctionInput';
|
||||||
|
|
||||||
describe('mergeDefaultFunctionInputAndFunctionInput', () => {
|
describe('mergeDefaultFunctionInputAndFunctionInput', () => {
|
||||||
it('should merge properly', () => {
|
it('should merge properly', () => {
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import {
|
||||||
|
WorkflowAction,
|
||||||
|
WorkflowRecordDeleteAction,
|
||||||
|
} from '@/workflow/types/Workflow';
|
||||||
|
|
||||||
|
export const isWorkflowRecordDeleteAction = (
|
||||||
|
action: WorkflowAction,
|
||||||
|
): action is WorkflowRecordDeleteAction => {
|
||||||
|
return (
|
||||||
|
action.type === 'RECORD_CRUD' && action.settings.input.type === 'DELETE'
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -169,6 +169,27 @@ export class WorkflowVersionStepWorkspaceService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case `${WorkflowActionType.RECORD_CRUD}.${WorkflowRecordCRUDType.DELETE}`: {
|
||||||
|
const activeObjectMetadataItem =
|
||||||
|
await this.objectMetadataRepository.findOne({
|
||||||
|
where: { workspaceId, isActive: true, isSystem: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: newStepId,
|
||||||
|
name: 'Delete Record',
|
||||||
|
type: WorkflowActionType.RECORD_CRUD,
|
||||||
|
valid: false,
|
||||||
|
settings: {
|
||||||
|
...BASE_STEP_DEFINITION,
|
||||||
|
input: {
|
||||||
|
type: WorkflowRecordCRUDType.DELETE,
|
||||||
|
objectName: activeObjectMetadataItem?.nameSingular || '',
|
||||||
|
objectRecordId: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new WorkflowVersionStepException(
|
throw new WorkflowVersionStepException(
|
||||||
`WorkflowActionType '${type}' unknown`,
|
`WorkflowActionType '${type}' unknown`,
|
||||||
|
|||||||
Reference in New Issue
Block a user