From ec87c7f21cfc00b32981dabefb0b4d2a3ab9379d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Tue, 19 Mar 2024 16:56:53 +0300 Subject: [PATCH 1/2] feat: introduce useLazyFlows with RQ --- .../web/src/components/AppFlows/index.jsx | 7 ++ packages/web/src/hooks/useLazyFlows.js | 30 ++++++++ packages/web/src/pages/Flows/index.jsx | 76 ++++++++----------- 3 files changed, 70 insertions(+), 43 deletions(-) create mode 100644 packages/web/src/hooks/useLazyFlows.js diff --git a/packages/web/src/components/AppFlows/index.jsx b/packages/web/src/components/AppFlows/index.jsx index bf5f4d8e..7aa5a3ef 100644 --- a/packages/web/src/components/AppFlows/index.jsx +++ b/packages/web/src/components/AppFlows/index.jsx @@ -4,11 +4,14 @@ import { Link, useSearchParams } from 'react-router-dom'; import { GET_FLOWS } from 'graphql/queries/get-flows'; import Pagination from '@mui/material/Pagination'; import PaginationItem from '@mui/material/PaginationItem'; + import * as URLS from 'config/urls'; import AppFlowRow from 'components/FlowRow'; import NoResultFound from 'components/NoResultFound'; import useFormatMessage from 'hooks/useFormatMessage'; + const FLOW_PER_PAGE = 10; + const getLimitAndOffset = (page) => ({ limit: FLOW_PER_PAGE, offset: (page - 1) * FLOW_PER_PAGE, @@ -19,6 +22,7 @@ function AppFlows(props) { const [searchParams, setSearchParams] = useSearchParams(); const connectionId = searchParams.get('connectionId') || undefined; const page = parseInt(searchParams.get('page') || '', 10) || 1; + const { data } = useQuery(GET_FLOWS, { variables: { appKey, @@ -26,10 +30,12 @@ function AppFlows(props) { ...getLimitAndOffset(page), }, }); + const getFlows = data?.getFlows || {}; const { pageInfo, edges } = getFlows; const flows = edges?.map(({ node }) => node); const hasFlows = flows?.length; + if (!hasFlows) { return ( ); } + return ( <> {flows?.map((appFlow) => ( diff --git a/packages/web/src/hooks/useLazyFlows.js b/packages/web/src/hooks/useLazyFlows.js new file mode 100644 index 00000000..4769fb26 --- /dev/null +++ b/packages/web/src/hooks/useLazyFlows.js @@ -0,0 +1,30 @@ +import * as React from 'react'; + +import api from 'helpers/api'; +import { useMutation } from '@tanstack/react-query'; + +export default function useFlows({ flowName, page }, { onSuccess }) { + const abortControllerRef = React.useRef(new AbortController()); + + React.useEffect(() => { + abortControllerRef.current = new AbortController(); + + return () => { + abortControllerRef.current?.abort(); + }; + }, [flowName]); + + const query = useMutation({ + mutationFn: async () => { + const { data } = await api.get('/v1/flows', { + params: { name: flowName, page }, + signal: abortControllerRef.current.signal, + }); + + return data; + }, + onSuccess, + }); + + return query; +} diff --git a/packages/web/src/pages/Flows/index.jsx b/packages/web/src/pages/Flows/index.jsx index 462a89eb..c77f72a4 100644 --- a/packages/web/src/pages/Flows/index.jsx +++ b/packages/web/src/pages/Flows/index.jsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { Link, useSearchParams } from 'react-router-dom'; -import { useLazyQuery } from '@apollo/client'; import debounce from 'lodash/debounce'; import Box from '@mui/material/Box'; import Grid from '@mui/material/Grid'; @@ -9,6 +8,7 @@ import CircularProgress from '@mui/material/CircularProgress'; import Divider from '@mui/material/Divider'; import Pagination from '@mui/material/Pagination'; import PaginationItem from '@mui/material/PaginationItem'; + import Can from 'components/Can'; import FlowRow from 'components/FlowRow'; import NoResultFound from 'components/NoResultFound'; @@ -17,45 +17,37 @@ import Container from 'components/Container'; import PageTitle from 'components/PageTitle'; import SearchInput from 'components/SearchInput'; import useFormatMessage from 'hooks/useFormatMessage'; -import { GET_FLOWS } from 'graphql/queries/get-flows'; import * as URLS from 'config/urls'; -const FLOW_PER_PAGE = 10; -const getLimitAndOffset = (page) => ({ - limit: FLOW_PER_PAGE, - offset: (page - 1) * FLOW_PER_PAGE, -}); +import useLazyFlows from 'hooks/useLazyFlows'; + export default function Flows() { const formatMessage = useFormatMessage(); const [searchParams, setSearchParams] = useSearchParams(); const page = parseInt(searchParams.get('page') || '', 10) || 1; const [flowName, setFlowName] = React.useState(''); - const [loading, setLoading] = React.useState(false); - const [getFlows, { data }] = useLazyQuery(GET_FLOWS, { - onCompleted: () => { - setLoading(false); + const [isLoading, setIsLoading] = React.useState(false); + + const { data, mutate } = useLazyFlows( + { flowName, page }, + { + onSuccess: () => { + setIsLoading(false); + }, }, - }); - const fetchData = React.useMemo( - () => - debounce( - (name) => - getFlows({ - variables: { - ...getLimitAndOffset(page), - name, - }, - }), - 300, - ), - [page, getFlows], - ); - React.useEffect( - function fetchFlowsOnSearch() { - setLoading(true); - fetchData(flowName); - }, - [fetchData, flowName], ); + + const fetchData = React.useMemo(() => debounce(mutate, 300), [mutate]); + + React.useEffect(() => { + setIsLoading(true); + + fetchData({ flowName, page }); + + return () => { + fetchData.cancel(); + }; + }, [fetchData, flowName, page]); + React.useEffect( function resetPageOnSearch() { // reset search params which only consists of `page` @@ -63,17 +55,15 @@ export default function Flows() { }, [flowName], ); - React.useEffect(function cancelDebounceOnUnmount() { - return () => { - fetchData.cancel(); - }; - }, []); - const { pageInfo, edges } = data?.getFlows || {}; - const flows = edges?.map(({ node }) => node); + + const flows = data?.data || []; + const pageInfo = data?.meta; const hasFlows = flows?.length; + const onSearchChange = React.useCallback((event) => { setFlowName(event.target.value); }, []); + return ( @@ -116,18 +106,18 @@ export default function Flows() { - {loading && ( + {isLoading && ( )} - {!loading && + {!isLoading && flows?.map((flow) => )} - {!loading && !hasFlows && ( + {!isLoading && !hasFlows && ( )} - {!loading && pageInfo && pageInfo.totalPages > 1 && ( + {!isLoading && pageInfo && pageInfo.totalPages > 1 && ( Date: Tue, 19 Mar 2024 18:53:20 +0300 Subject: [PATCH 2/2] refactor: rewrite useFlows as useConnectionFlows and useAppFlows with RQ --- .../backend/src/graphql/queries/get-flows.js | 40 ------------------- .../backend/src/graphql/query-resolvers.js | 2 - packages/backend/src/graphql/schema.graphql | 16 -------- .../web/src/components/AppFlows/index.jsx | 34 ++++++++-------- packages/web/src/graphql/queries/get-flows.js | 36 ----------------- packages/web/src/hooks/useAppFlows.js | 22 ++++++++++ packages/web/src/hooks/useConnectionFlows.js | 25 ++++++++++++ packages/web/src/hooks/useLazyFlows.js | 2 +- 8 files changed, 64 insertions(+), 113 deletions(-) delete mode 100644 packages/backend/src/graphql/queries/get-flows.js delete mode 100644 packages/web/src/graphql/queries/get-flows.js create mode 100644 packages/web/src/hooks/useAppFlows.js create mode 100644 packages/web/src/hooks/useConnectionFlows.js diff --git a/packages/backend/src/graphql/queries/get-flows.js b/packages/backend/src/graphql/queries/get-flows.js deleted file mode 100644 index 6f3899e8..00000000 --- a/packages/backend/src/graphql/queries/get-flows.js +++ /dev/null @@ -1,40 +0,0 @@ -import Flow from '../../models/flow.js'; -import paginate from '../../helpers/pagination.js'; - -const getFlows = async (_parent, params, context) => { - const conditions = context.currentUser.can('read', 'Flow'); - const userFlows = context.currentUser.$relatedQuery('flows'); - const allFlows = Flow.query(); - const baseQuery = conditions.isCreator ? userFlows : allFlows; - - const flowsQuery = baseQuery - .clone() - .joinRelated({ - steps: true, - }) - .withGraphFetched({ - steps: { - connection: true, - }, - }) - .where((builder) => { - if (params.connectionId) { - builder.where('steps.connection_id', params.connectionId); - } - - if (params.name) { - builder.where('flows.name', 'ilike', `%${params.name}%`); - } - - if (params.appKey) { - builder.where('steps.app_key', params.appKey); - } - }) - .groupBy('flows.id') - .orderBy('active', 'desc') - .orderBy('updated_at', 'desc'); - - return paginate(flowsQuery, params.limit, params.offset); -}; - -export default getFlows; diff --git a/packages/backend/src/graphql/query-resolvers.js b/packages/backend/src/graphql/query-resolvers.js index 74ccedee..54fdc798 100644 --- a/packages/backend/src/graphql/query-resolvers.js +++ b/packages/backend/src/graphql/query-resolvers.js @@ -7,7 +7,6 @@ import getConnectedApps from './queries/get-connected-apps.js'; import getDynamicData from './queries/get-dynamic-data.js'; import getDynamicFields from './queries/get-dynamic-fields.js'; import getFlow from './queries/get-flow.js'; -import getFlows from './queries/get-flows.js'; import getNotifications from './queries/get-notifications.js'; import getStepWithTestExecutions from './queries/get-step-with-test-executions.js'; import getUsers from './queries/get-users.js'; @@ -23,7 +22,6 @@ const queryResolvers = { getDynamicData, getDynamicFields, getFlow, - getFlows, getNotifications, getStepWithTestExecutions, getUsers, diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index 3e1ab036..1e61d56b 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -5,13 +5,6 @@ type Query { getConnectedApps(name: String): [App] testConnection(id: String!): Connection getFlow(id: String!): Flow - getFlows( - limit: Int! - offset: Int! - appKey: String - connectionId: String - name: String - ): FlowConnection getStepWithTestExecutions(stepId: String!): [Step] getDynamicData( stepId: String! @@ -257,15 +250,6 @@ type Field { options: [SubstepArgumentOption] } -type FlowConnection { - edges: [FlowEdge] - pageInfo: PageInfo -} - -type FlowEdge { - node: Flow -} - enum FlowStatus { paused published diff --git a/packages/web/src/components/AppFlows/index.jsx b/packages/web/src/components/AppFlows/index.jsx index 7aa5a3ef..23ea2f14 100644 --- a/packages/web/src/components/AppFlows/index.jsx +++ b/packages/web/src/components/AppFlows/index.jsx @@ -1,7 +1,5 @@ import PropTypes from 'prop-types'; -import { useQuery } from '@apollo/client'; import { Link, useSearchParams } from 'react-router-dom'; -import { GET_FLOWS } from 'graphql/queries/get-flows'; import Pagination from '@mui/material/Pagination'; import PaginationItem from '@mui/material/PaginationItem'; @@ -9,31 +7,31 @@ import * as URLS from 'config/urls'; import AppFlowRow from 'components/FlowRow'; import NoResultFound from 'components/NoResultFound'; import useFormatMessage from 'hooks/useFormatMessage'; +import useConnectionFlows from 'hooks/useConnectionFlows'; +import useAppFlows from 'hooks/useAppFlows'; -const FLOW_PER_PAGE = 10; - -const getLimitAndOffset = (page) => ({ - limit: FLOW_PER_PAGE, - offset: (page - 1) * FLOW_PER_PAGE, -}); function AppFlows(props) { const { appKey } = props; const formatMessage = useFormatMessage(); const [searchParams, setSearchParams] = useSearchParams(); const connectionId = searchParams.get('connectionId') || undefined; const page = parseInt(searchParams.get('page') || '', 10) || 1; + const isConnectionFlowEnabled = !!connectionId; + const isAppFlowEnabled = !!appKey && !connectionId; - const { data } = useQuery(GET_FLOWS, { - variables: { - appKey, - connectionId, - ...getLimitAndOffset(page), - }, - }); + const connectionFlows = useConnectionFlows( + { connectionId, page }, + { enabled: isConnectionFlowEnabled }, + ); - const getFlows = data?.getFlows || {}; - const { pageInfo, edges } = getFlows; - const flows = edges?.map(({ node }) => node); + const appFlows = useAppFlows({ appKey, page }, { enabled: isAppFlowEnabled }); + + const flows = isConnectionFlowEnabled + ? connectionFlows?.data?.data || [] + : appFlows?.data?.data || []; + const pageInfo = isConnectionFlowEnabled + ? connectionFlows?.data?.meta || [] + : appFlows?.data?.meta || []; const hasFlows = flows?.length; if (!hasFlows) { diff --git a/packages/web/src/graphql/queries/get-flows.js b/packages/web/src/graphql/queries/get-flows.js deleted file mode 100644 index e4213d31..00000000 --- a/packages/web/src/graphql/queries/get-flows.js +++ /dev/null @@ -1,36 +0,0 @@ -import { gql } from '@apollo/client'; -export const GET_FLOWS = gql` - query GetFlows( - $limit: Int! - $offset: Int! - $appKey: String - $connectionId: String - $name: String - ) { - getFlows( - limit: $limit - offset: $offset - appKey: $appKey - connectionId: $connectionId - name: $name - ) { - pageInfo { - currentPage - totalPages - } - edges { - node { - id - name - createdAt - updatedAt - active - status - steps { - iconUrl - } - } - } - } - } -`; diff --git a/packages/web/src/hooks/useAppFlows.js b/packages/web/src/hooks/useAppFlows.js new file mode 100644 index 00000000..8ff98766 --- /dev/null +++ b/packages/web/src/hooks/useAppFlows.js @@ -0,0 +1,22 @@ +import { useQuery } from '@tanstack/react-query'; + +import api from 'helpers/api'; + +export default function useAppFlows({ appKey, page }, { enabled }) { + const query = useQuery({ + queryKey: ['appFlows', appKey, page], + queryFn: async ({ signal }) => { + const { data } = await api.get(`/v1/apps/${appKey}/flows`, { + params: { + page, + }, + signal, + }); + + return data; + }, + enabled, + }); + + return query; +} diff --git a/packages/web/src/hooks/useConnectionFlows.js b/packages/web/src/hooks/useConnectionFlows.js new file mode 100644 index 00000000..d8799567 --- /dev/null +++ b/packages/web/src/hooks/useConnectionFlows.js @@ -0,0 +1,25 @@ +import { useQuery } from '@tanstack/react-query'; + +import api from 'helpers/api'; + +export default function useConnectionFlows( + { connectionId, page }, + { enabled }, +) { + const query = useQuery({ + queryKey: ['connectionFlows', connectionId, page], + queryFn: async ({ signal }) => { + const { data } = await api.get(`/v1/connections/${connectionId}/flows`, { + params: { + page, + }, + signal, + }); + + return data; + }, + enabled, + }); + + return query; +} diff --git a/packages/web/src/hooks/useLazyFlows.js b/packages/web/src/hooks/useLazyFlows.js index 4769fb26..8de9ac82 100644 --- a/packages/web/src/hooks/useLazyFlows.js +++ b/packages/web/src/hooks/useLazyFlows.js @@ -3,7 +3,7 @@ import * as React from 'react'; import api from 'helpers/api'; import { useMutation } from '@tanstack/react-query'; -export default function useFlows({ flowName, page }, { onSuccess }) { +export default function useLazyFlows({ flowName, page }, { onSuccess }) { const abortControllerRef = React.useRef(new AbortController()); React.useEffect(() => {