Merge pull request #2089 from automatisch/aut-1266
feat: use REST API endpoint to update flow status
This commit is contained in:
@@ -7,7 +7,6 @@ import generateAuthUrl from './mutations/generate-auth-url.js';
|
|||||||
import createConnection from './mutations/create-connection.js';
|
import createConnection from './mutations/create-connection.js';
|
||||||
import resetConnection from './mutations/reset-connection.js';
|
import resetConnection from './mutations/reset-connection.js';
|
||||||
import updateConnection from './mutations/update-connection.js';
|
import updateConnection from './mutations/update-connection.js';
|
||||||
import updateFlowStatus from './mutations/update-flow-status.js';
|
|
||||||
|
|
||||||
const mutationResolvers = {
|
const mutationResolvers = {
|
||||||
createConnection,
|
createConnection,
|
||||||
@@ -16,7 +15,6 @@ const mutationResolvers = {
|
|||||||
resetConnection,
|
resetConnection,
|
||||||
updateConnection,
|
updateConnection,
|
||||||
updateCurrentUser,
|
updateCurrentUser,
|
||||||
updateFlowStatus,
|
|
||||||
updateUser,
|
updateUser,
|
||||||
verifyConnection,
|
verifyConnection,
|
||||||
};
|
};
|
||||||
|
@@ -1,91 +0,0 @@
|
|||||||
import Flow from '../../models/flow.js';
|
|
||||||
import flowQueue from '../../queues/flow.js';
|
|
||||||
import {
|
|
||||||
REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
|
||||||
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
|
||||||
} from '../../helpers/remove-job-configuration.js';
|
|
||||||
import globalVariable from '../../helpers/global-variable.js';
|
|
||||||
|
|
||||||
const JOB_NAME = 'flow';
|
|
||||||
const EVERY_15_MINUTES_CRON = '*/15 * * * *';
|
|
||||||
|
|
||||||
const updateFlowStatus = async (_parent, params, context) => {
|
|
||||||
const conditions = context.currentUser.can('publish', 'Flow');
|
|
||||||
const isCreator = conditions.isCreator;
|
|
||||||
const allFlows = Flow.query();
|
|
||||||
const userFlows = context.currentUser.$relatedQuery('flows');
|
|
||||||
const baseQuery = isCreator ? userFlows : allFlows;
|
|
||||||
|
|
||||||
let flow = await baseQuery
|
|
||||||
.clone()
|
|
||||||
.findOne({
|
|
||||||
id: params.input.id,
|
|
||||||
})
|
|
||||||
.throwIfNotFound();
|
|
||||||
|
|
||||||
const newActiveValue = params.input.active;
|
|
||||||
|
|
||||||
if (flow.active === newActiveValue) {
|
|
||||||
return flow;
|
|
||||||
}
|
|
||||||
|
|
||||||
const triggerStep = await flow.getTriggerStep();
|
|
||||||
|
|
||||||
if (triggerStep.status === 'incomplete') {
|
|
||||||
throw flow.IncompleteStepsError;
|
|
||||||
}
|
|
||||||
|
|
||||||
const trigger = await triggerStep.getTriggerCommand();
|
|
||||||
const interval = trigger.getInterval?.(triggerStep.parameters);
|
|
||||||
const repeatOptions = {
|
|
||||||
pattern: interval || EVERY_15_MINUTES_CRON,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (trigger.type === 'webhook') {
|
|
||||||
const $ = await globalVariable({
|
|
||||||
flow,
|
|
||||||
connection: await triggerStep.$relatedQuery('connection'),
|
|
||||||
app: await triggerStep.getApp(),
|
|
||||||
step: triggerStep,
|
|
||||||
testRun: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (newActiveValue && trigger.registerHook) {
|
|
||||||
await trigger.registerHook($);
|
|
||||||
} else if (!newActiveValue && trigger.unregisterHook) {
|
|
||||||
await trigger.unregisterHook($);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (newActiveValue) {
|
|
||||||
flow = await flow.$query().patchAndFetch({
|
|
||||||
publishedAt: new Date().toISOString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const jobName = `${JOB_NAME}-${flow.id}`;
|
|
||||||
|
|
||||||
await flowQueue.add(
|
|
||||||
jobName,
|
|
||||||
{ flowId: flow.id },
|
|
||||||
{
|
|
||||||
repeat: repeatOptions,
|
|
||||||
jobId: flow.id,
|
|
||||||
removeOnComplete: REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
|
||||||
removeOnFail: REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const repeatableJobs = await flowQueue.getRepeatableJobs();
|
|
||||||
const job = repeatableJobs.find((job) => job.id === flow.id);
|
|
||||||
|
|
||||||
await flowQueue.removeRepeatableByKey(job.key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flow = await flow.$query().withGraphFetched('steps').patchAndFetch({
|
|
||||||
active: newActiveValue,
|
|
||||||
});
|
|
||||||
|
|
||||||
return flow;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default updateFlowStatus;
|
|
@@ -8,7 +8,6 @@ type Mutation {
|
|||||||
resetConnection(input: ResetConnectionInput): Connection
|
resetConnection(input: ResetConnectionInput): Connection
|
||||||
updateConnection(input: UpdateConnectionInput): Connection
|
updateConnection(input: UpdateConnectionInput): Connection
|
||||||
updateCurrentUser(input: UpdateCurrentUserInput): User
|
updateCurrentUser(input: UpdateCurrentUserInput): User
|
||||||
updateFlowStatus(input: UpdateFlowStatusInput): Flow
|
|
||||||
updateUser(input: UpdateUserInput): User
|
updateUser(input: UpdateUserInput): User
|
||||||
verifyConnection(input: VerifyConnectionInput): Connection
|
verifyConnection(input: VerifyConnectionInput): Connection
|
||||||
}
|
}
|
||||||
@@ -234,11 +233,6 @@ input VerifyConnectionInput {
|
|||||||
id: String!
|
id: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateFlowStatusInput {
|
|
||||||
id: String!
|
|
||||||
active: Boolean!
|
|
||||||
}
|
|
||||||
|
|
||||||
input ExecuteFlowInput {
|
input ExecuteFlowInput {
|
||||||
stepId: String!
|
stepId: String!
|
||||||
}
|
}
|
||||||
|
@@ -125,7 +125,13 @@ class Flow extends Base {
|
|||||||
|
|
||||||
get IncompleteStepsError() {
|
get IncompleteStepsError() {
|
||||||
return new ValidationError({
|
return new ValidationError({
|
||||||
message: 'All steps should be completed before updating flow status!',
|
data: {
|
||||||
|
flow: [
|
||||||
|
{
|
||||||
|
message: 'All steps should be completed before updating flow status!'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
type: 'incompleteStepsError',
|
type: 'incompleteStepsError',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -366,8 +372,13 @@ class Flow extends Base {
|
|||||||
|
|
||||||
if (allSteps.length < 2) {
|
if (allSteps.length < 2) {
|
||||||
throw new ValidationError({
|
throw new ValidationError({
|
||||||
message:
|
data: {
|
||||||
'There should be at least one trigger and one action steps in the flow!',
|
flow: [
|
||||||
|
{
|
||||||
|
message: 'There should be at least one trigger and one action steps in the flow!'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
type: 'insufficientStepsError',
|
type: 'insufficientStepsError',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Link, useParams } from 'react-router-dom';
|
import { Link, useParams } from 'react-router-dom';
|
||||||
import { useMutation } from '@apollo/client';
|
|
||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
@@ -16,12 +15,11 @@ import Container from 'components/Container';
|
|||||||
import Editor from 'components/Editor';
|
import Editor from 'components/Editor';
|
||||||
import Can from 'components/Can';
|
import Can from 'components/Can';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import { UPDATE_FLOW_STATUS } from 'graphql/mutations/update-flow-status';
|
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import { TopBar } from './style';
|
import { TopBar } from './style';
|
||||||
import useFlow from 'hooks/useFlow';
|
import useFlow from 'hooks/useFlow';
|
||||||
import useUpdateFlow from 'hooks/useUpdateFlow';
|
import useUpdateFlow from 'hooks/useUpdateFlow';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import useUpdateFlowStatus from 'hooks/useUpdateFlowStatus';
|
||||||
import EditorNew from 'components/EditorNew/EditorNew';
|
import EditorNew from 'components/EditorNew/EditorNew';
|
||||||
|
|
||||||
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
|
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
|
||||||
@@ -30,10 +28,9 @@ export default function EditorLayout() {
|
|||||||
const { flowId } = useParams();
|
const { flowId } = useParams();
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const { mutateAsync: updateFlow } = useUpdateFlow(flowId);
|
const { mutateAsync: updateFlow } = useUpdateFlow(flowId);
|
||||||
const [updateFlowStatus] = useMutation(UPDATE_FLOW_STATUS);
|
const { mutateAsync: updateFlowStatus } = useUpdateFlowStatus(flowId);
|
||||||
const { data, isLoading: isFlowLoading } = useFlow(flowId);
|
const { data, isLoading: isFlowLoading } = useFlow(flowId);
|
||||||
const flow = data?.data;
|
const flow = data?.data;
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const onFlowNameUpdate = async (name) => {
|
const onFlowNameUpdate = async (name) => {
|
||||||
await updateFlow({
|
await updateFlow({
|
||||||
@@ -41,24 +38,6 @@ export default function EditorLayout() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFlowStatusUpdate = React.useCallback(
|
|
||||||
async (active) => {
|
|
||||||
try {
|
|
||||||
await updateFlowStatus({
|
|
||||||
variables: {
|
|
||||||
input: {
|
|
||||||
id: flowId,
|
|
||||||
active,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await queryClient.invalidateQueries({ queryKey: ['flows', flowId] });
|
|
||||||
} catch (err) {}
|
|
||||||
},
|
|
||||||
[flowId, queryClient],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBar
|
<TopBar
|
||||||
@@ -107,7 +86,7 @@ export default function EditorLayout() {
|
|||||||
disabled={!allowed || !flow}
|
disabled={!allowed || !flow}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => onFlowStatusUpdate(!flow.active)}
|
onClick={() => updateFlowStatus(!flow.active)}
|
||||||
data-test={
|
data-test={
|
||||||
flow?.active ? 'unpublish-flow-button' : 'publish-flow-button'
|
flow?.active ? 'unpublish-flow-button' : 'publish-flow-button'
|
||||||
}
|
}
|
||||||
@@ -153,7 +132,7 @@ export default function EditorLayout() {
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => onFlowStatusUpdate(!flow.active)}
|
onClick={() => updateFlowStatus(!flow.active)}
|
||||||
data-test="unpublish-flow-from-snackbar"
|
data-test="unpublish-flow-from-snackbar"
|
||||||
>
|
>
|
||||||
{formatMessage('flowEditor.unpublish')}
|
{formatMessage('flowEditor.unpublish')}
|
||||||
|
@@ -1,9 +0,0 @@
|
|||||||
import { gql } from '@apollo/client';
|
|
||||||
export const UPDATE_FLOW_STATUS = gql`
|
|
||||||
mutation UpdateFlowStatus($input: UpdateFlowStatusInput) {
|
|
||||||
updateFlowStatus(input: $input) {
|
|
||||||
id
|
|
||||||
active
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
39
packages/web/src/hooks/useUpdateFlowStatus.js
Normal file
39
packages/web/src/hooks/useUpdateFlowStatus.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import api from 'helpers/api';
|
||||||
|
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||||
|
|
||||||
|
export default function useUpdateFlowStatus(flowId) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const enqueueSnackbar = useEnqueueSnackbar();
|
||||||
|
|
||||||
|
const query = useMutation({
|
||||||
|
mutationFn: async (active) => {
|
||||||
|
const { data } = await api.patch(`/v1/flows/${flowId}/status`, {
|
||||||
|
active,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ['flows', flowId],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
const errors = Object.values(
|
||||||
|
error.response.data.errors || [['Failed while updating flow status!']],
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const [error] of errors) {
|
||||||
|
enqueueSnackbar(error, {
|
||||||
|
variant: 'error',
|
||||||
|
SnackbarProps: {
|
||||||
|
'data-test': 'snackbar-error',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
Reference in New Issue
Block a user