diff --git a/packages/backend/src/graphql/queries/get-dynamic-fields.js b/packages/backend/src/graphql/queries/get-dynamic-fields.js deleted file mode 100644 index 2d7d18db..00000000 --- a/packages/backend/src/graphql/queries/get-dynamic-fields.js +++ /dev/null @@ -1,40 +0,0 @@ -import App from '../../models/app.js'; -import Step from '../../models/step.js'; -import globalVariable from '../../helpers/global-variable.js'; - -const getDynamicFields = async (_parent, params, context) => { - const conditions = context.currentUser.can('update', 'Flow'); - const userSteps = context.currentUser.$relatedQuery('steps'); - const allSteps = Step.query(); - const stepBaseQuery = conditions.isCreator ? userSteps : allSteps; - - const step = await stepBaseQuery - .clone() - .withGraphFetched({ - connection: true, - flow: true, - }) - .findById(params.stepId); - - if (!step) return null; - - const connection = step.connection; - - if (!step.appKey) return null; - - const app = await App.findOneByKey(step.appKey); - const $ = await globalVariable({ connection, app, flow: step.flow, step }); - - const command = app.dynamicFields.find((data) => data.key === params.key); - - for (const parameterKey in params.parameters) { - const parameterValue = params.parameters[parameterKey]; - $.step.parameters[parameterKey] = parameterValue; - } - - const additionalFields = (await command.run($)) || []; - - return additionalFields; -}; - -export default getDynamicFields; diff --git a/packages/backend/src/graphql/query-resolvers.js b/packages/backend/src/graphql/query-resolvers.js index 55671f69..d7941d9c 100644 --- a/packages/backend/src/graphql/query-resolvers.js +++ b/packages/backend/src/graphql/query-resolvers.js @@ -4,7 +4,6 @@ import getAppAuthClients from './queries/get-app-auth-clients.ee.js'; import getBillingAndUsage from './queries/get-billing-and-usage.ee.js'; 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 getStepWithTestExecutions from './queries/get-step-with-test-executions.js'; import testConnection from './queries/test-connection.js'; @@ -16,7 +15,6 @@ const queryResolvers = { getBillingAndUsage, getConnectedApps, getDynamicData, - getDynamicFields, getFlow, getStepWithTestExecutions, testConnection, diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index 9f90abf5..356df968 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -11,11 +11,6 @@ type Query { key: String! parameters: JSONObject ): JSONObject - getDynamicFields( - stepId: String! - key: String! - parameters: JSONObject - ): [SubstepArgument] getBillingAndUsage: GetBillingAndUsage } diff --git a/packages/web/src/components/InputCreator/index.jsx b/packages/web/src/components/InputCreator/index.jsx index 158593f2..484b5ed5 100644 --- a/packages/web/src/components/InputCreator/index.jsx +++ b/packages/web/src/components/InputCreator/index.jsx @@ -1,6 +1,7 @@ import * as React from 'react'; import MuiTextField from '@mui/material/TextField'; import CircularProgress from '@mui/material/CircularProgress'; + import useDynamicFields from 'hooks/useDynamicFields'; import useDynamicData from 'hooks/useDynamicData'; import PowerInput from 'components/PowerInput'; @@ -8,8 +9,10 @@ import TextField from 'components/TextField'; import ControlledAutocomplete from 'components/ControlledAutocomplete'; import ControlledCustomAutocomplete from 'components/ControlledCustomAutocomplete'; import DynamicField from 'components/DynamicField'; + const optionGenerator = (options) => options?.map(({ name, value }) => ({ label: name, value: value })); + export default function InputCreator(props) { const { onChange, @@ -31,9 +34,12 @@ export default function InputCreator(props) { type, } = schema; const { data, loading } = useDynamicData(stepId, schema); - const { data: additionalFields, loading: additionalFieldsLoading } = + const { data: additionalFieldsData, isLoading: isDynamicFieldsLoading } = useDynamicFields(stepId, schema); + const additionalFields = additionalFieldsData?.data; + const computedName = namePrefix ? `${namePrefix}.${name}` : name; + if (type === 'dynamic') { return ( ); } + if (type === 'dropdown') { const preparedOptions = schema.options || optionGenerator(data); + return ( {!schema.variables && ( @@ -63,7 +71,9 @@ export default function InputCreator(props) { disablePortal disableClearable={required} options={preparedOptions} - renderInput={(params) => } + renderInput={(params) => ( + + )} defaultValue={value} description={description} loading={loading} @@ -93,7 +103,7 @@ export default function InputCreator(props) { /> )} - {additionalFieldsLoading && !additionalFields?.length && ( + {isDynamicFieldsLoading && !additionalFields?.length && (
@@ -113,6 +123,7 @@ export default function InputCreator(props) {
); } + if (type === 'string') { if (schema.variables) { return ( @@ -127,7 +138,7 @@ export default function InputCreator(props) { shouldUnregister={shouldUnregister} /> - {additionalFieldsLoading && !additionalFields?.length && ( + {isDynamicFieldsLoading && !additionalFields?.length && (
); } + return ( - {additionalFieldsLoading && !additionalFields?.length && ( + {isDynamicFieldsLoading && !additionalFields?.length && (
diff --git a/packages/web/src/hooks/useDynamicFields.js b/packages/web/src/hooks/useDynamicFields.js index dfe2de76..9027d937 100644 --- a/packages/web/src/hooks/useDynamicFields.js +++ b/packages/web/src/hooks/useDynamicFields.js @@ -1,27 +1,35 @@ import * as React from 'react'; -import { useLazyQuery } from '@apollo/client'; import { useFormContext } from 'react-hook-form'; import set from 'lodash/set'; import isEqual from 'lodash/isEqual'; -import { GET_DYNAMIC_FIELDS } from 'graphql/queries/get-dynamic-fields'; +import { useQuery } from '@tanstack/react-query'; + +import api from 'helpers/api'; + const variableRegExp = /({.*?})/; // TODO: extract this function to a separate file function computeArguments(args, getValues) { const initialValue = {}; + return args.reduce((result, { name, value }) => { const isVariable = variableRegExp.test(value); + if (isVariable) { const sanitizedFieldPath = value.replace(/{|}/g, ''); const computedValue = getValues(sanitizedFieldPath); + if (computedValue === undefined || computedValue === '') throw new Error(`The ${sanitizedFieldPath} field is required.`); + set(result, name, computedValue); return result; } + set(result, name, value); return result; }, initialValue); } + /** * Fetch the dynamic fields for the given step. * This hook must be within a react-hook-form context. @@ -31,10 +39,9 @@ function computeArguments(args, getValues) { */ function useDynamicFields(stepId, schema) { const lastComputedVariables = React.useRef({}); - const [getDynamicFields, { called, data, loading }] = - useLazyQuery(GET_DYNAMIC_FIELDS); const { getValues } = useFormContext(); const formValues = getValues(); + /** * Return `null` when even a field is missing value. * @@ -58,31 +65,28 @@ function useDynamicFields(stepId, schema) { return null; } } + return null; /** * `formValues` is to trigger recomputation when form is updated. * `getValues` is for convenience as it supports paths for fields like `getValues('foo.bar.baz')`. */ }, [schema, formValues, getValues]); - React.useEffect(() => { - if ( - schema.type === 'dropdown' && - stepId && - schema.additionalFields && - computedVariables - ) { - getDynamicFields({ - variables: { - stepId, - ...computedVariables, - }, + + const query = useQuery({ + queryKey: ['dynamicFields', stepId, computedVariables], + queryFn: async () => { + const { data } = await api.post(`/v1/steps/${stepId}/dynamic-fields`, { + dynamicFieldsKey: computedVariables.key, + parameters: computedVariables.parameters, }); - } - }, [getDynamicFields, stepId, schema, computedVariables]); - return { - called, - data: data?.getDynamicFields, - loading, - }; + + return data; + }, + enabled: !!stepId && !!computedVariables, + }); + + return query; } + export default useDynamicFields;