Skip to main content
Glama
flow-hooks.tsx8.2 kB
import { QueryClient, useMutation, useQuery } from '@tanstack/react-query'; import { t } from 'i18next'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; import { useApErrorDialogStore } from '@/components/custom/ap-error-dialog/ap-error-dialog-store'; import { useSocket } from '@/components/socket-provider'; import { internalErrorToast } from '@/components/ui/sonner'; import { flowRunsApi } from '@/features/flow-runs/lib/flow-runs-api'; import { pieceSelectorUtils } from '@/features/pieces/lib/piece-selector-utils'; import { piecesApi } from '@/features/pieces/lib/pieces-api'; import { stepUtils } from '@/features/pieces/lib/step-utils'; import { flagsHooks } from '@/hooks/flags-hooks'; import { authenticationSession } from '@/lib/authentication-session'; import { downloadFile } from '@/lib/utils'; import { ApFlagId, FlowOperationType, FlowRun, FlowStatus, FlowVersion, FlowVersionMetadata, ListFlowsRequest, PopulatedFlow, FlowTrigger, FlowTriggerType, WebsocketClientEvent, FlowStatusUpdatedResponse, isNil, } from '@activepieces/shared'; import { flowsApi } from './flows-api'; import { flowsUtils } from './flows-utils'; const createFlowsQueryKey = (projectId: string) => ['flows', projectId]; export const flowHooks = { invalidateFlowsQuery: (queryClient: QueryClient) => { queryClient.invalidateQueries({ queryKey: createFlowsQueryKey(authenticationSession.getProjectId()!), }); }, useFlows: (request: Omit<ListFlowsRequest, 'projectId'>) => { return useQuery({ queryKey: createFlowsQueryKey(authenticationSession.getProjectId()!), queryFn: async () => { return await flowsApi.list({ ...request, projectId: authenticationSession.getProjectId()!, }); }, staleTime: 5 * 1000, }); }, useChangeFlowStatus: ({ flowId, change, onSuccess, setIsPublishing, }: UseChangeFlowStatusParams) => { const { data: enableFlowOnPublish } = flagsHooks.useFlag<boolean>( ApFlagId.ENABLE_FLOW_ON_PUBLISH, ); const socket = useSocket(); const { openDialog } = useApErrorDialogStore(); return useMutation({ mutationFn: async () => { if (change === 'publish') { setIsPublishing?.(true); } return await new Promise<FlowStatusUpdatedResponse>( (resolve, reject) => { const onUpdateFinish = (response: FlowStatusUpdatedResponse) => { if (response.flow.id !== flowId) { return; } socket.off( WebsocketClientEvent.FLOW_STATUS_UPDATED, onUpdateFinish, ); resolve(response); }; socket.on(WebsocketClientEvent.FLOW_STATUS_UPDATED, onUpdateFinish); flowsApi .update(flowId, { type: change === 'publish' ? FlowOperationType.LOCK_AND_PUBLISH : FlowOperationType.CHANGE_STATUS, request: { status: change === 'publish' ? enableFlowOnPublish ? FlowStatus.ENABLED : FlowStatus.DISABLED : change, }, }) .then(() => {}) .catch((error) => { reject(error); }); }, ); }, onSuccess: (response: FlowStatusUpdatedResponse) => { if (change === 'publish') { setIsPublishing?.(false); } if (!isNil(response.error)) { openDialog({ title: change === 'publish' ? t('Publish failed') : t('Status update failed'), description: ( <p> {t( 'An error occurred while changing the flow status. This may be due to an issue in the trigger piece or its settings.', )} </p> ), error: { standardError: response.error.params.standardError, standardOutput: response.error.params.standardOutput || '', }, }); return; } onSuccess?.(response); }, onError: (_uncaughtError: unknown) => { internalErrorToast(); }, }); }, useExportFlows: () => { return useMutation({ mutationFn: async (flows: PopulatedFlow[]) => { if (flows.length === 0) { return flows; } if (flows.length === 1) { await flowsUtils.downloadFlow(flows[0].id); return flows; } await downloadFile({ obj: await flowsUtils.zipFlows(flows), fileName: 'flows', extension: 'zip', }); return flows; }, onSuccess: (res) => { if (res.length > 0) { toast.success( res.length === 1 ? t(`${res[0].version.displayName} has been exported.`) : t('Flows have been exported.'), { duration: 3000, }, ); } }, }); }, useFetchFlowVersion: ({ onSuccess, }: { onSuccess: (flowVersion: FlowVersion) => void; }) => { return useMutation<FlowVersion, Error, FlowVersionMetadata>({ mutationFn: async (flowVersion) => { const result = await flowsApi.get(flowVersion.flowId, { versionId: flowVersion.id, }); return result.version; }, onSuccess, }); }, useOverWriteDraftWithVersion: ({ onSuccess, }: { onSuccess: (flowVersion: PopulatedFlow) => void; }) => { return useMutation<PopulatedFlow, Error, FlowVersionMetadata>({ mutationFn: async (flowVersion) => { const result = await flowsApi.update(flowVersion.flowId, { type: FlowOperationType.USE_AS_DRAFT, request: { versionId: flowVersion.id, }, }); return result; }, onSuccess, }); }, useCreateMcpFlow: () => { const navigate = useNavigate(); return useMutation({ mutationFn: async () => { const flow = await flowsApi.create({ projectId: authenticationSession.getProjectId()!, displayName: t('Untitled'), }); const mcpPiece = await piecesApi.get({ name: '@activepieces/piece-mcp', }); const trigger = mcpPiece.triggers['mcp_tool']; if (!trigger) { throw new Error('MCP trigger not found'); } const stepData = pieceSelectorUtils.getDefaultStepValues({ stepName: 'trigger', pieceSelectorItem: { actionOrTrigger: trigger, type: FlowTriggerType.PIECE, pieceMetadata: stepUtils.mapPieceToMetadata({ piece: mcpPiece, type: 'trigger', }), }, }) as FlowTrigger; await flowsApi.update(flow.id, { type: FlowOperationType.UPDATE_TRIGGER, request: stepData, }); return flow; }, onSuccess: (flow) => { navigate(`/flows/${flow.id}/`); }, }); }, useGetFlow: (flowId: string) => { return useQuery({ queryKey: ['flow', flowId], queryFn: async () => { try { return await flowsApi.get(flowId); } catch (err) { console.error(err); return null; } }, staleTime: 0, }); }, useTestFlow: ({ flowVersionId, onUpdateRun, }: { flowVersionId: string; onUpdateRun: (run: FlowRun) => void; }) => { const socket = useSocket(); return useMutation<void>({ mutationFn: () => flowRunsApi.testFlow( socket, { flowVersionId, }, onUpdateRun, ), }); }, }; type UseChangeFlowStatusParams = { flowId: string; change: 'publish' | FlowStatus; onSuccess: (flow: FlowStatusUpdatedResponse) => void; setIsPublishing?: (isPublishing: boolean) => void; };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/activepieces/activepieces'

If you have feedback or need assistance with the MCP directory API, please join our Discord server