diff --git a/packages/backend/src/controllers/api/v1/apps/get-apps.js b/packages/backend/src/controllers/api/v1/apps/get-apps.js index be6e112e..4cedc706 100644 --- a/packages/backend/src/controllers/api/v1/apps/get-apps.js +++ b/packages/backend/src/controllers/api/v1/apps/get-apps.js @@ -4,11 +4,11 @@ import { renderObject } from '../../../../helpers/renderer.js'; export default async (request, response) => { let apps = await App.findAll(request.query.name); - if (request.query.onlyWithTriggers) { + if (request.query.onlyWithTriggers === 'true') { apps = apps.filter((app) => app.triggers?.length); } - if (request.query.onlyWithActions) { + if (request.query.onlyWithActions === 'true') { apps = apps.filter((app) => app.actions?.length); } diff --git a/packages/web/src/components/ChooseAppAndEventSubstep/index.jsx b/packages/web/src/components/ChooseAppAndEventSubstep/index.jsx index aa5da519..d71d82c8 100644 --- a/packages/web/src/components/ChooseAppAndEventSubstep/index.jsx +++ b/packages/web/src/components/ChooseAppAndEventSubstep/index.jsx @@ -8,6 +8,7 @@ import ListItem from '@mui/material/ListItem'; import TextField from '@mui/material/TextField'; import Autocomplete from '@mui/material/Autocomplete'; import Chip from '@mui/material/Chip'; + import useFormatMessage from 'hooks/useFormatMessage'; import useApps from 'hooks/useApps'; import { EditorContext } from 'contexts/Editor'; @@ -18,13 +19,16 @@ const optionGenerator = (app) => ({ label: app.name, value: app.key, }); + const eventOptionGenerator = (app) => ({ label: app.name, value: app.key, type: app?.type, }); + const getOption = (options, selectedOptionValue) => options.find((option) => option.value === selectedOptionValue); + function ChooseAppAndEventSubstep(props) { const { substep, @@ -39,26 +43,36 @@ function ChooseAppAndEventSubstep(props) { const editorContext = React.useContext(EditorContext); const isTrigger = step.type === 'trigger'; const isAction = step.type === 'action'; - const { apps } = useApps({ + + const { data: apps } = useApps({ onlyWithTriggers: isTrigger, onlyWithActions: isAction, }); - const app = apps?.find((currentApp) => currentApp.key === step.appKey); + + const app = apps?.data?.find((currentApp) => currentApp.key === step.appKey); + const appOptions = React.useMemo( - () => apps?.map((app) => optionGenerator(app)) || [], - [apps], + () => apps?.data?.map((app) => optionGenerator(app)) || [], + [apps?.data], ); + const actionsOrTriggers = (isTrigger ? app?.triggers : app?.actions) || []; + const actionOrTriggerOptions = React.useMemo( () => actionsOrTriggers.map((trigger) => eventOptionGenerator(trigger)), [app?.key], ); + const selectedActionOrTrigger = actionsOrTriggers.find( (actionOrTrigger) => actionOrTrigger.key === step?.key, ); + const isWebhook = isTrigger && selectedActionOrTrigger?.type === 'webhook'; + const { name } = substep; + const valid = !!step.key && !!step.appKey; + // placeholders const onEventChange = React.useCallback( (event, selectedOption) => { @@ -79,6 +93,7 @@ function ChooseAppAndEventSubstep(props) { }, [step, onChange], ); + const onAppChange = React.useCallback( (event, selectedOption) => { if (typeof selectedOption === 'object') { @@ -100,7 +115,9 @@ function ChooseAppAndEventSubstep(props) { }, [step, onChange], ); + const onToggle = expanded ? onCollapse : onExpand; + return ( {props.id} ); + return ( @@ -48,6 +51,7 @@ function ExecutionStepDate(props) { const formatMessage = useFormatMessage(); const createdAt = DateTime.fromMillis(parseInt(props.createdAt, 10)); const relativeCreatedAt = createdAt.toRelative(); + return ( currentApp.key === step.appKey); - if (!apps) return null; + + const app = apps?.data?.find((currentApp) => currentApp.key === step.appKey); + + if (!apps?.data) return null; + const validationStatusIcon = executionStep.status === 'success' ? validIcon : errorIcon; + const hasError = !!executionStep.errorDetails; + return (
diff --git a/packages/web/src/components/FlowStep/index.jsx b/packages/web/src/components/FlowStep/index.jsx index 8d360498..b4c16fc0 100644 --- a/packages/web/src/components/FlowStep/index.jsx +++ b/packages/web/src/components/FlowStep/index.jsx @@ -14,6 +14,7 @@ import CircularProgress from '@mui/material/CircularProgress'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from 'yup'; + import { EditorContext } from 'contexts/Editor'; import { StepExecutionsProvider } from 'contexts/StepExecutions'; import TestSubstep from 'components/TestSubstep'; @@ -83,6 +84,7 @@ function generateValidationSchema(substeps) { } } } + return { ...allValidations, ...substepArgumentValidations, @@ -90,9 +92,11 @@ function generateValidationSchema(substeps) { }, {}, ); + const validationSchema = yup.object({ parameters: yup.object(fieldValidations), }); + return yupResolver(validationSchema); } @@ -106,16 +110,19 @@ function FlowStep(props) { const isAction = step.type === 'action'; const formatMessage = useFormatMessage(); const [currentSubstep, setCurrentSubstep] = React.useState(0); - const { apps } = useApps({ + + const { data: apps } = useApps({ onlyWithTriggers: isTrigger, onlyWithActions: isAction, }); + const [ getStepWithTestExecutions, { data: stepWithTestExecutionsData, called: stepWithTestExecutionsCalled }, ] = useLazyQuery(GET_STEP_WITH_TEST_EXECUTIONS, { fetchPolicy: 'network-only', }); + React.useEffect(() => { if (!stepWithTestExecutionsCalled && !collapsed && !isTrigger) { getStepWithTestExecutions({ @@ -131,26 +138,33 @@ function FlowStep(props) { step.id, isTrigger, ]); - const app = apps?.find((currentApp) => currentApp.key === step.appKey); + + const app = apps?.data?.find((currentApp) => currentApp.key === step.appKey); const actionsOrTriggers = (isTrigger ? app?.triggers : app?.actions) || []; const actionOrTrigger = actionsOrTriggers?.find( ({ key }) => key === step.key, ); + const substeps = actionOrTrigger?.substeps || []; + const handleChange = React.useCallback(({ step }) => { onChange(step); }, []); + const expandNextStep = React.useCallback(() => { setCurrentSubstep((currentSubstep) => (currentSubstep ?? 0) + 1); }, []); + const handleSubmit = (val) => { handleChange({ step: val }); }; + const stepValidationSchema = React.useMemo( () => generateValidationSchema(substeps), [substeps], ); - if (!apps) { + + if (!apps?.data) { return ( ); } + const onContextMenuClose = (event) => { event.stopPropagation(); setAnchorEl(null); }; + const onContextMenuClick = (event) => { event.stopPropagation(); setAnchorEl(contextButtonRef.current); }; + const onOpen = () => collapsed && props.onOpen?.(); + const onClose = () => props.onClose?.(); + const toggleSubstep = (substepIndex) => setCurrentSubstep((value) => value !== substepIndex ? substepIndex : null, ); + const validationStatusIcon = step.status === 'completed' ? validIcon : errorIcon; + return ( { + const { data } = await api.get('/v1/apps', { + params: variables, + signal, + }); + + return data; + }, }); - const apps = data?.getApps; - return { - apps, - loading, - }; + + return query; }