Merge pull request #1696 from automatisch/AUT-685
refactor: implement rest API endpoint for get app and get apps
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
import App from '../../models/app.js';
|
||||
|
||||
const getApps = async (_parent, params) => {
|
||||
const apps = await App.findAll(params.name);
|
||||
|
||||
if (params.onlyWithTriggers) {
|
||||
return apps.filter((app) => app.triggers?.length);
|
||||
}
|
||||
|
||||
if (params.onlyWithActions) {
|
||||
return apps.filter((app) => app.actions?.length);
|
||||
}
|
||||
|
||||
return apps;
|
||||
};
|
||||
|
||||
export default getApps;
|
@@ -2,7 +2,6 @@ import getApp from './queries/get-app.js';
|
||||
import getAppAuthClient from './queries/get-app-auth-client.ee.js';
|
||||
import getAppAuthClients from './queries/get-app-auth-clients.ee.js';
|
||||
import getAppConfig from './queries/get-app-config.ee.js';
|
||||
import getApps from './queries/get-apps.js';
|
||||
import getBillingAndUsage from './queries/get-billing-and-usage.ee.js';
|
||||
import getConfig from './queries/get-config.ee.js';
|
||||
import getConnectedApps from './queries/get-connected-apps.js';
|
||||
@@ -37,7 +36,6 @@ const queryResolvers = {
|
||||
getAppAuthClient,
|
||||
getAppAuthClients,
|
||||
getAppConfig,
|
||||
getApps,
|
||||
getBillingAndUsage,
|
||||
getConfig,
|
||||
getConnectedApps,
|
||||
|
@@ -1,9 +1,4 @@
|
||||
type Query {
|
||||
getApps(
|
||||
name: String
|
||||
onlyWithTriggers: Boolean
|
||||
onlyWithActions: Boolean
|
||||
): [App]
|
||||
getApp(key: String!): App
|
||||
getAppConfig(key: String!): AppConfig
|
||||
getAppAuthClient(id: String!): AppAuthClient
|
||||
|
@@ -16,10 +16,12 @@ import useAuthenticateApp from 'hooks/useAuthenticateApp.ee';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import { generateExternalLink } from 'helpers/translationValues';
|
||||
import { Form } from './style';
|
||||
import useAppAuth from 'hooks/useAppAuth';
|
||||
|
||||
function AddAppConnection(props) {
|
||||
const { application, connectionId, onClose } = props;
|
||||
const { name, authDocUrl, key, auth } = application;
|
||||
const { name, authDocUrl, key } = application;
|
||||
const { data: auth } = useAppAuth(key);
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const formatMessage = useFormatMessage();
|
||||
@@ -34,31 +36,40 @@ function AddAppConnection(props) {
|
||||
appAuthClientId,
|
||||
useShared: !!appAuthClientId,
|
||||
});
|
||||
|
||||
React.useEffect(function relayProviderData() {
|
||||
if (window.opener) {
|
||||
window.opener.postMessage({
|
||||
source: 'automatisch',
|
||||
payload: { search: window.location.search, hash: window.location.hash },
|
||||
});
|
||||
|
||||
window.close();
|
||||
}
|
||||
}, []);
|
||||
|
||||
React.useEffect(
|
||||
function initiateSharedAuthenticationForGivenAuthClient() {
|
||||
if (!appAuthClientId) return;
|
||||
|
||||
if (!authenticate) return;
|
||||
|
||||
const asyncAuthenticate = async () => {
|
||||
await authenticate();
|
||||
navigate(URLS.APP_CONNECTIONS(key));
|
||||
};
|
||||
|
||||
asyncAuthenticate();
|
||||
},
|
||||
[appAuthClientId, authenticate],
|
||||
);
|
||||
|
||||
const handleClientClick = (appAuthClientId) =>
|
||||
navigate(URLS.APP_ADD_CONNECTION_WITH_AUTH_CLIENT_ID(key, appAuthClientId));
|
||||
|
||||
const handleAuthClientsDialogClose = () =>
|
||||
navigate(URLS.APP_CONNECTIONS(key));
|
||||
|
||||
const submitHandler = React.useCallback(
|
||||
async (data) => {
|
||||
if (!authenticate) return;
|
||||
@@ -78,6 +89,7 @@ function AddAppConnection(props) {
|
||||
},
|
||||
[authenticate],
|
||||
);
|
||||
|
||||
if (useShared)
|
||||
return (
|
||||
<AppAuthClientsDialog
|
||||
@@ -86,7 +98,9 @@ function AddAppConnection(props) {
|
||||
onClientClick={handleClientClick}
|
||||
/>
|
||||
);
|
||||
|
||||
if (appAuthClientId) return <React.Fragment />;
|
||||
|
||||
return (
|
||||
<Dialog open={true} onClose={onClose} data-test="add-app-connection-dialog">
|
||||
<DialogTitle>
|
||||
@@ -121,7 +135,7 @@ function AddAppConnection(props) {
|
||||
<DialogContent>
|
||||
<DialogContentText tabIndex={-1} component="div">
|
||||
<Form onSubmit={submitHandler}>
|
||||
{auth?.fields?.map((field) => (
|
||||
{auth?.data?.fields?.map((field) => (
|
||||
<InputCreator key={field.key} schema={field} />
|
||||
))}
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useLazyQuery } from '@apollo/client';
|
||||
import { Link } from 'react-router-dom';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
@@ -20,15 +19,17 @@ import InputLabel from '@mui/material/InputLabel';
|
||||
import OutlinedInput from '@mui/material/OutlinedInput';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
import * as URLS from 'config/urls';
|
||||
import AppIcon from 'components/AppIcon';
|
||||
import { GET_APPS } from 'graphql/queries/get-apps';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useLazyApps from 'hooks/useLazyApps';
|
||||
|
||||
function createConnectionOrFlow(appKey, supportsConnections = false) {
|
||||
if (!supportsConnections) {
|
||||
return URLS.CREATE_FLOW_WITH_APP(appKey);
|
||||
}
|
||||
|
||||
return URLS.APP_ADD_CONNECTION(appKey);
|
||||
}
|
||||
function AddNewAppConnection(props) {
|
||||
@@ -36,29 +37,28 @@ function AddNewAppConnection(props) {
|
||||
const theme = useTheme();
|
||||
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
const formatMessage = useFormatMessage();
|
||||
const [appName, setAppName] = React.useState(null);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [getApps, { data }] = useLazyQuery(GET_APPS, {
|
||||
onCompleted: () => {
|
||||
setLoading(false);
|
||||
const [appName, setAppName] = React.useState('');
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
|
||||
const { data: apps, mutate } = useLazyApps({
|
||||
appName,
|
||||
onSuccess: () => {
|
||||
setIsLoading(false);
|
||||
},
|
||||
});
|
||||
const fetchData = React.useMemo(
|
||||
() => debounce((name) => getApps({ variables: { name } }), 300),
|
||||
[getApps],
|
||||
);
|
||||
React.useEffect(
|
||||
function fetchAppsOnAppNameChange() {
|
||||
setLoading(true);
|
||||
fetchData(appName);
|
||||
},
|
||||
[fetchData, appName],
|
||||
);
|
||||
React.useEffect(function cancelDebounceOnUnmount() {
|
||||
|
||||
const fetchData = React.useMemo(() => debounce(mutate, 300), [mutate]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setIsLoading(true);
|
||||
|
||||
fetchData(appName);
|
||||
|
||||
return () => {
|
||||
fetchData.cancel();
|
||||
};
|
||||
}, []);
|
||||
}, [fetchData, appName]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={true}
|
||||
@@ -102,15 +102,15 @@ function AddNewAppConnection(props) {
|
||||
|
||||
<DialogContent>
|
||||
<List sx={{ pt: 2, width: '100%' }}>
|
||||
{loading && (
|
||||
{isLoading && (
|
||||
<CircularProgress
|
||||
data-test="search-for-app-loader"
|
||||
sx={{ display: 'block', margin: '20px auto' }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!loading &&
|
||||
data?.getApps?.map((app) => (
|
||||
{!isLoading &&
|
||||
apps?.data.map((app) => (
|
||||
<ListItem disablePadding key={app.name} data-test="app-list-item">
|
||||
<ListItemButton
|
||||
component={Link}
|
||||
|
@@ -8,12 +8,14 @@ import { CREATE_APP_AUTH_CLIENT } from 'graphql/mutations/create-app-auth-client
|
||||
import useAppConfig from 'hooks/useAppConfig.ee';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import AdminApplicationAuthClientDialog from 'components/AdminApplicationAuthClientDialog';
|
||||
import useAppAuth from 'hooks/useAppAuth';
|
||||
|
||||
function AdminApplicationCreateAuthClient(props) {
|
||||
const { appKey, application, onClose } = props;
|
||||
const { auth } = application;
|
||||
const { appKey, onClose } = props;
|
||||
const { data: auth } = useAppAuth(appKey);
|
||||
const formatMessage = useFormatMessage();
|
||||
const { appConfig, loading: loadingAppConfig } = useAppConfig(appKey);
|
||||
|
||||
const [
|
||||
createAppConfig,
|
||||
{ loading: loadingCreateAppConfig, error: createAppConfigError },
|
||||
@@ -21,6 +23,7 @@ function AdminApplicationCreateAuthClient(props) {
|
||||
refetchQueries: ['GetAppConfig'],
|
||||
context: { autoSnackbar: false },
|
||||
});
|
||||
|
||||
const [
|
||||
createAppAuthClient,
|
||||
{ loading: loadingCreateAppAuthClient, error: createAppAuthClientError },
|
||||
@@ -28,8 +31,10 @@ function AdminApplicationCreateAuthClient(props) {
|
||||
refetchQueries: ['GetAppAuthClients'],
|
||||
context: { autoSnackbar: false },
|
||||
});
|
||||
|
||||
const submitHandler = async (values) => {
|
||||
let appConfigId = appConfig?.id;
|
||||
|
||||
if (!appConfigId) {
|
||||
const { data: appConfigData } = await createAppConfig({
|
||||
variables: {
|
||||
@@ -41,8 +46,10 @@ function AdminApplicationCreateAuthClient(props) {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
appConfigId = appConfigData.createAppConfig.id;
|
||||
}
|
||||
|
||||
const { name, active, ...formattedAuthDefaults } = values;
|
||||
await createAppAuthClient({
|
||||
variables: {
|
||||
@@ -54,22 +61,28 @@ function AdminApplicationCreateAuthClient(props) {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
onClose();
|
||||
};
|
||||
|
||||
const getAuthFieldsDefaultValues = useCallback(() => {
|
||||
if (!auth?.fields) {
|
||||
if (!auth?.data?.fields) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const defaultValues = {};
|
||||
auth.fields.forEach((field) => {
|
||||
|
||||
auth.data.fields.forEach((field) => {
|
||||
if (field.value || field.type !== 'string') {
|
||||
defaultValues[field.key] = field.value;
|
||||
} else if (field.type === 'string') {
|
||||
defaultValues[field.key] = '';
|
||||
}
|
||||
});
|
||||
|
||||
return defaultValues;
|
||||
}, [auth?.fields]);
|
||||
}, [auth?.data?.fields]);
|
||||
|
||||
const defaultValues = useMemo(
|
||||
() => ({
|
||||
name: '',
|
||||
@@ -78,6 +91,7 @@ function AdminApplicationCreateAuthClient(props) {
|
||||
}),
|
||||
[getAuthFieldsDefaultValues],
|
||||
);
|
||||
|
||||
return (
|
||||
<AdminApplicationAuthClientDialog
|
||||
onClose={onClose}
|
||||
@@ -85,7 +99,7 @@ function AdminApplicationCreateAuthClient(props) {
|
||||
title={formatMessage('createAuthClient.title')}
|
||||
loading={loadingAppConfig}
|
||||
submitHandler={submitHandler}
|
||||
authFields={auth?.fields}
|
||||
authFields={auth?.data?.fields}
|
||||
submitting={loadingCreateAppConfig || loadingCreateAppAuthClient}
|
||||
defaultValues={defaultValues}
|
||||
/>
|
||||
|
@@ -8,23 +8,29 @@ 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';
|
||||
import FlowSubstepTitle from 'components/FlowSubstepTitle';
|
||||
import { StepPropType, SubstepPropType } from 'propTypes/propTypes';
|
||||
import useTriggers from 'hooks/useTriggers';
|
||||
import useActions from 'hooks/useActions';
|
||||
|
||||
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 +45,48 @@ function ChooseAppAndEventSubstep(props) {
|
||||
const editorContext = React.useContext(EditorContext);
|
||||
const isTrigger = step.type === 'trigger';
|
||||
const isAction = step.type === 'action';
|
||||
const { apps } = useApps({
|
||||
onlyWithTriggers: isTrigger,
|
||||
onlyWithActions: isAction,
|
||||
});
|
||||
const app = apps?.find((currentApp) => currentApp.key === step.appKey);
|
||||
const appOptions = React.useMemo(
|
||||
() => apps?.map((app) => optionGenerator(app)) || [],
|
||||
[apps],
|
||||
const useAppsOptions = {};
|
||||
|
||||
if (isTrigger) {
|
||||
useAppsOptions.onlyWithTriggers = true;
|
||||
}
|
||||
|
||||
if (isAction) {
|
||||
useAppsOptions.onlyWithActions = true;
|
||||
}
|
||||
|
||||
const { data: apps } = useApps(useAppsOptions);
|
||||
|
||||
const app = apps?.data?.find(
|
||||
(currentApp) => currentApp?.key === step?.appKey,
|
||||
);
|
||||
const actionsOrTriggers = (isTrigger ? app?.triggers : app?.actions) || [];
|
||||
|
||||
const { data: triggers } = useTriggers(app?.key);
|
||||
|
||||
const { data: actions } = useActions(app?.key);
|
||||
|
||||
const appOptions = React.useMemo(
|
||||
() => apps?.data?.map((app) => optionGenerator(app)) || [],
|
||||
[apps?.data],
|
||||
);
|
||||
|
||||
const actionsOrTriggers = (isTrigger ? triggers?.data : actions?.data) || [];
|
||||
|
||||
const actionOrTriggerOptions = React.useMemo(
|
||||
() => actionsOrTriggers.map((trigger) => eventOptionGenerator(trigger)),
|
||||
[app?.key],
|
||||
[actionsOrTriggers],
|
||||
);
|
||||
|
||||
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 +107,7 @@ function ChooseAppAndEventSubstep(props) {
|
||||
},
|
||||
[step, onChange],
|
||||
);
|
||||
|
||||
const onAppChange = React.useCallback(
|
||||
(event, selectedOption) => {
|
||||
if (typeof selectedOption === 'object') {
|
||||
@@ -100,7 +129,9 @@ function ChooseAppAndEventSubstep(props) {
|
||||
},
|
||||
[step, onChange],
|
||||
);
|
||||
|
||||
const onToggle = expanded ? onCollapse : onExpand;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FlowSubstepTitle
|
||||
|
@@ -176,6 +176,7 @@ function ChooseConnectionSubstep(props) {
|
||||
}
|
||||
}, [step.connection?.id, retestConnection]);
|
||||
const onToggle = expanded ? onCollapse : onExpand;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FlowSubstepTitle
|
||||
|
@@ -9,6 +9,7 @@ import Tab from '@mui/material/Tab';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
import TabPanel from 'components/TabPanel';
|
||||
import SearchableJSONViewer from 'components/SearchableJSONViewer';
|
||||
import AppIcon from 'components/AppIcon';
|
||||
@@ -26,11 +27,13 @@ import { ExecutionStepPropType, StepPropType } from 'propTypes/propTypes';
|
||||
|
||||
function ExecutionStepId(props) {
|
||||
const formatMessage = useFormatMessage();
|
||||
|
||||
const id = (
|
||||
<Typography variant="caption" component="span">
|
||||
{props.id}
|
||||
</Typography>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }} gridArea="id">
|
||||
<Typography variant="caption" fontWeight="bold">
|
||||
@@ -48,6 +51,7 @@ function ExecutionStepDate(props) {
|
||||
const formatMessage = useFormatMessage();
|
||||
const createdAt = DateTime.fromMillis(parseInt(props.createdAt, 10));
|
||||
const relativeCreatedAt = createdAt.toRelative();
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={createdAt.toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)}
|
||||
@@ -75,15 +79,27 @@ function ExecutionStep(props) {
|
||||
const isTrigger = step.type === 'trigger';
|
||||
const isAction = step.type === 'action';
|
||||
const formatMessage = useFormatMessage();
|
||||
const { apps } = useApps({
|
||||
onlyWithTriggers: isTrigger,
|
||||
onlyWithActions: isAction,
|
||||
});
|
||||
const app = apps?.find((currentApp) => currentApp.key === step.appKey);
|
||||
if (!apps) return null;
|
||||
const useAppsOptions = {};
|
||||
|
||||
if (isTrigger) {
|
||||
useAppsOptions.onlyWithTriggers = true;
|
||||
}
|
||||
|
||||
if (isAction) {
|
||||
useAppsOptions.onlyWithActions = true;
|
||||
}
|
||||
|
||||
const { data: apps } = useApps(useAppsOptions);
|
||||
|
||||
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 (
|
||||
<Wrapper elevation={1} data-test="execution-step">
|
||||
<Header>
|
||||
|
@@ -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';
|
||||
@@ -35,6 +36,10 @@ import {
|
||||
} from './style';
|
||||
import isEmpty from 'helpers/isEmpty';
|
||||
import { StepPropType } from 'propTypes/propTypes';
|
||||
import useTriggers from 'hooks/useTriggers';
|
||||
import useActions from 'hooks/useActions';
|
||||
import useTriggerSubsteps from 'hooks/useTriggerSubsteps';
|
||||
import useActionSubsteps from 'hooks/useActionSubsteps';
|
||||
|
||||
const validIcon = <CheckCircleIcon color="success" />;
|
||||
const errorIcon = <ErrorIcon color="error" />;
|
||||
@@ -83,6 +88,7 @@ function generateValidationSchema(substeps) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...allValidations,
|
||||
...substepArgumentValidations,
|
||||
@@ -90,9 +96,11 @@ function generateValidationSchema(substeps) {
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const validationSchema = yup.object({
|
||||
parameters: yup.object(fieldValidations),
|
||||
});
|
||||
|
||||
return yupResolver(validationSchema);
|
||||
}
|
||||
|
||||
@@ -106,16 +114,25 @@ function FlowStep(props) {
|
||||
const isAction = step.type === 'action';
|
||||
const formatMessage = useFormatMessage();
|
||||
const [currentSubstep, setCurrentSubstep] = React.useState(0);
|
||||
const { apps } = useApps({
|
||||
onlyWithTriggers: isTrigger,
|
||||
onlyWithActions: isAction,
|
||||
});
|
||||
const useAppsOptions = {};
|
||||
|
||||
if (isTrigger) {
|
||||
useAppsOptions.onlyWithTriggers = true;
|
||||
}
|
||||
|
||||
if (isAction) {
|
||||
useAppsOptions.onlyWithActions = true;
|
||||
}
|
||||
|
||||
const { data: apps } = useApps(useAppsOptions);
|
||||
|
||||
const [
|
||||
getStepWithTestExecutions,
|
||||
{ data: stepWithTestExecutionsData, called: stepWithTestExecutionsCalled },
|
||||
] = useLazyQuery(GET_STEP_WITH_TEST_EXECUTIONS, {
|
||||
fetchPolicy: 'network-only',
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!stepWithTestExecutionsCalled && !collapsed && !isTrigger) {
|
||||
getStepWithTestExecutions({
|
||||
@@ -131,26 +148,56 @@ function FlowStep(props) {
|
||||
step.id,
|
||||
isTrigger,
|
||||
]);
|
||||
const app = apps?.find((currentApp) => currentApp.key === step.appKey);
|
||||
const actionsOrTriggers = (isTrigger ? app?.triggers : app?.actions) || [];
|
||||
|
||||
const app = apps?.data?.find((currentApp) => currentApp.key === step.appKey);
|
||||
|
||||
const { data: triggers } = useTriggers(app?.key);
|
||||
|
||||
const { data: actions } = useActions(app?.key);
|
||||
|
||||
const actionsOrTriggers = (isTrigger ? triggers?.data : actions?.data) || [];
|
||||
|
||||
const actionOrTrigger = actionsOrTriggers?.find(
|
||||
({ key }) => key === step.key,
|
||||
);
|
||||
const substeps = actionOrTrigger?.substeps || [];
|
||||
|
||||
const { data: triggerSubsteps } = useTriggerSubsteps(
|
||||
app?.key,
|
||||
actionOrTrigger?.key,
|
||||
);
|
||||
|
||||
const triggerSubstepsData = triggerSubsteps?.data || [];
|
||||
|
||||
const { data: actionSubsteps } = useActionSubsteps(
|
||||
app?.key,
|
||||
actionOrTrigger?.key,
|
||||
);
|
||||
|
||||
const actionSubstepsData = actionSubsteps?.data || [];
|
||||
|
||||
const substeps =
|
||||
triggerSubstepsData.length > 0
|
||||
? triggerSubstepsData
|
||||
: actionSubstepsData || [];
|
||||
|
||||
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 (
|
||||
<CircularProgress
|
||||
data-test="step-circular-loader"
|
||||
@@ -158,22 +205,29 @@ function FlowStep(props) {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<Wrapper
|
||||
elevation={collapsed ? 1 : 4}
|
||||
|
@@ -1,105 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
export const GET_APP = gql`
|
||||
query GetApp($key: String!) {
|
||||
getApp(key: $key) {
|
||||
name
|
||||
key
|
||||
iconUrl
|
||||
docUrl
|
||||
authDocUrl
|
||||
primaryColor
|
||||
supportsConnections
|
||||
auth {
|
||||
fields {
|
||||
key
|
||||
label
|
||||
type
|
||||
required
|
||||
readOnly
|
||||
value
|
||||
description
|
||||
docUrl
|
||||
clickToCopy
|
||||
options {
|
||||
label
|
||||
value
|
||||
}
|
||||
}
|
||||
authenticationSteps {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
type
|
||||
properties {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
sharedAuthenticationSteps {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
type
|
||||
properties {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
reconnectionSteps {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
type
|
||||
properties {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
sharedReconnectionSteps {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
type
|
||||
properties {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
connections {
|
||||
id
|
||||
}
|
||||
triggers {
|
||||
name
|
||||
key
|
||||
type
|
||||
showWebhookUrl
|
||||
pollInterval
|
||||
description
|
||||
substeps {
|
||||
name
|
||||
}
|
||||
}
|
||||
actions {
|
||||
name
|
||||
key
|
||||
description
|
||||
substeps {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
@@ -1,234 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
export const GET_APPS = gql`
|
||||
query GetApps(
|
||||
$name: String
|
||||
$onlyWithTriggers: Boolean
|
||||
$onlyWithActions: Boolean
|
||||
) {
|
||||
getApps(
|
||||
name: $name
|
||||
onlyWithTriggers: $onlyWithTriggers
|
||||
onlyWithActions: $onlyWithActions
|
||||
) {
|
||||
name
|
||||
key
|
||||
iconUrl
|
||||
docUrl
|
||||
authDocUrl
|
||||
primaryColor
|
||||
connectionCount
|
||||
flowCount
|
||||
supportsConnections
|
||||
auth {
|
||||
fields {
|
||||
key
|
||||
label
|
||||
type
|
||||
required
|
||||
readOnly
|
||||
value
|
||||
placeholder
|
||||
description
|
||||
docUrl
|
||||
clickToCopy
|
||||
options {
|
||||
label
|
||||
value
|
||||
}
|
||||
}
|
||||
authenticationSteps {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
type
|
||||
properties {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
sharedAuthenticationSteps {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
type
|
||||
properties {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
reconnectionSteps {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
type
|
||||
properties {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
sharedReconnectionSteps {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
type
|
||||
properties {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
triggers {
|
||||
name
|
||||
key
|
||||
type
|
||||
showWebhookUrl
|
||||
pollInterval
|
||||
description
|
||||
substeps {
|
||||
key
|
||||
name
|
||||
arguments {
|
||||
label
|
||||
key
|
||||
type
|
||||
required
|
||||
description
|
||||
variables
|
||||
dependsOn
|
||||
options {
|
||||
label
|
||||
value
|
||||
}
|
||||
source {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
additionalFields {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
fields {
|
||||
label
|
||||
key
|
||||
type
|
||||
required
|
||||
description
|
||||
variables
|
||||
value
|
||||
dependsOn
|
||||
options {
|
||||
label
|
||||
value
|
||||
}
|
||||
source {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
additionalFields {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
actions {
|
||||
name
|
||||
key
|
||||
description
|
||||
substeps {
|
||||
key
|
||||
name
|
||||
arguments {
|
||||
label
|
||||
key
|
||||
type
|
||||
required
|
||||
description
|
||||
variables
|
||||
dependsOn
|
||||
value
|
||||
options {
|
||||
label
|
||||
value
|
||||
}
|
||||
source {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
additionalFields {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
fields {
|
||||
label
|
||||
key
|
||||
type
|
||||
required
|
||||
description
|
||||
variables
|
||||
value
|
||||
dependsOn
|
||||
options {
|
||||
label
|
||||
value
|
||||
}
|
||||
source {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
additionalFields {
|
||||
type
|
||||
name
|
||||
arguments {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
22
packages/web/src/hooks/useActionSubsteps.js
Normal file
22
packages/web/src/hooks/useActionSubsteps.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import api from 'helpers/api';
|
||||
|
||||
export default function useActionSubsteps(appKey, actionKey) {
|
||||
const query = useQuery({
|
||||
queryKey: ['actionSubsteps', appKey, actionKey],
|
||||
queryFn: async ({ payload, signal }) => {
|
||||
const { data } = await api.get(
|
||||
`/v1/apps/${appKey}/actions/${actionKey}/substeps`,
|
||||
{
|
||||
signal,
|
||||
},
|
||||
);
|
||||
|
||||
return data;
|
||||
},
|
||||
enabled: !!appKey,
|
||||
});
|
||||
|
||||
return query;
|
||||
}
|
19
packages/web/src/hooks/useActions.js
Normal file
19
packages/web/src/hooks/useActions.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import api from 'helpers/api';
|
||||
|
||||
export default function useActions(appKey) {
|
||||
const query = useQuery({
|
||||
queryKey: ['actions', appKey],
|
||||
queryFn: async ({ payload, signal }) => {
|
||||
const { data } = await api.get(`/v1/apps/${appKey}/actions`, {
|
||||
signal,
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
enabled: !!appKey,
|
||||
});
|
||||
|
||||
return query;
|
||||
}
|
@@ -6,7 +6,9 @@ export default function useAdminAppAuthClient(id) {
|
||||
const query = useQuery({
|
||||
queryKey: ['adminAppAuthClient', id],
|
||||
queryFn: async ({ payload, signal }) => {
|
||||
const { data } = await api.get(`/v1/admin/app-auth-clients/${id}`);
|
||||
const { data } = await api.get(`/v1/admin/app-auth-clients/${id}`, {
|
||||
signal,
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
|
@@ -1,12 +1,19 @@
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { GET_APP } from 'graphql/queries/get-app';
|
||||
export default function useApp(key) {
|
||||
const { data, loading } = useQuery(GET_APP, {
|
||||
variables: { key },
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import api from 'helpers/api';
|
||||
|
||||
export default function useApp(appKey) {
|
||||
const query = useQuery({
|
||||
queryKey: ['app', appKey],
|
||||
queryFn: async ({ payload, signal }) => {
|
||||
const { data } = await api.get(`/v1/apps/${appKey}`, {
|
||||
signal,
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
enabled: !!appKey,
|
||||
});
|
||||
const app = data?.getApp;
|
||||
return {
|
||||
app,
|
||||
loading,
|
||||
};
|
||||
|
||||
return query;
|
||||
}
|
||||
|
19
packages/web/src/hooks/useAppAuth.js
Normal file
19
packages/web/src/hooks/useAppAuth.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import api from 'helpers/api';
|
||||
|
||||
export default function useAppAuth(appKey) {
|
||||
const query = useQuery({
|
||||
queryKey: ['appAuth', appKey],
|
||||
queryFn: async ({ payload, signal }) => {
|
||||
const { data } = await api.get(`/v1/apps/${appKey}/auth`, {
|
||||
signal,
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
enabled: !!appKey,
|
||||
});
|
||||
|
||||
return query;
|
||||
}
|
@@ -1,12 +1,19 @@
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { GET_APPS } from 'graphql/queries/get-apps';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import api from 'helpers/api';
|
||||
|
||||
export default function useApps(variables) {
|
||||
const { data, loading } = useQuery(GET_APPS, {
|
||||
variables,
|
||||
const query = useQuery({
|
||||
queryKey: ['apps', variables],
|
||||
queryFn: async ({ payload, signal }) => {
|
||||
const { data } = await api.get('/v1/apps', {
|
||||
params: variables,
|
||||
signal,
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
});
|
||||
const apps = data?.getApps;
|
||||
return {
|
||||
apps,
|
||||
loading,
|
||||
};
|
||||
|
||||
return query;
|
||||
}
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { processStep } from 'helpers/authenticationSteps';
|
||||
import computeAuthStepVariables from 'helpers/computeAuthStepVariables';
|
||||
import useApp from './useApp';
|
||||
import useAppAuth from './useAppAuth';
|
||||
|
||||
function getSteps(auth, hasConnection, useShared) {
|
||||
if (hasConnection) {
|
||||
if (useShared) {
|
||||
@@ -9,19 +11,24 @@ function getSteps(auth, hasConnection, useShared) {
|
||||
}
|
||||
return auth?.reconnectionSteps;
|
||||
}
|
||||
|
||||
if (useShared) {
|
||||
return auth?.sharedAuthenticationSteps;
|
||||
}
|
||||
|
||||
return auth?.authenticationSteps;
|
||||
}
|
||||
|
||||
export default function useAuthenticateApp(payload) {
|
||||
const { appKey, appAuthClientId, connectionId, useShared = false } = payload;
|
||||
const { app } = useApp(appKey);
|
||||
const { data: auth } = useAppAuth(appKey);
|
||||
const [authenticationInProgress, setAuthenticationInProgress] =
|
||||
React.useState(false);
|
||||
const steps = getSteps(app?.auth, !!connectionId, useShared);
|
||||
const steps = getSteps(auth?.data, !!connectionId, useShared);
|
||||
|
||||
const authenticate = React.useMemo(() => {
|
||||
if (!steps?.length) return;
|
||||
|
||||
return async function authenticate(payload = {}) {
|
||||
const { fields } = payload;
|
||||
setAuthenticationInProgress(true);
|
||||
@@ -34,9 +41,11 @@ export default function useAuthenticateApp(payload) {
|
||||
fields,
|
||||
};
|
||||
let stepIndex = 0;
|
||||
|
||||
while (stepIndex < steps?.length) {
|
||||
const step = steps[stepIndex];
|
||||
const variables = computeAuthStepVariables(step.arguments, response);
|
||||
|
||||
try {
|
||||
const stepResponse = await processStep(step, variables);
|
||||
response[step.name] = stepResponse;
|
||||
@@ -46,6 +55,7 @@ export default function useAuthenticateApp(payload) {
|
||||
throw err;
|
||||
}
|
||||
stepIndex++;
|
||||
|
||||
if (stepIndex === steps.length) {
|
||||
return response;
|
||||
}
|
||||
@@ -53,6 +63,7 @@ export default function useAuthenticateApp(payload) {
|
||||
}
|
||||
};
|
||||
}, [steps, appKey, appAuthClientId, connectionId]);
|
||||
|
||||
return {
|
||||
authenticate,
|
||||
inProgress: authenticationInProgress,
|
||||
|
30
packages/web/src/hooks/useLazyApps.js
Normal file
30
packages/web/src/hooks/useLazyApps.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
|
||||
import api from 'helpers/api';
|
||||
import React from 'react';
|
||||
|
||||
export default function useLazyApps({ appName, onSuccess }) {
|
||||
const abortControllerRef = React.useRef(new AbortController());
|
||||
|
||||
React.useEffect(() => {
|
||||
abortControllerRef.current = new AbortController();
|
||||
|
||||
return () => {
|
||||
abortControllerRef.current?.abort();
|
||||
};
|
||||
}, [appName]);
|
||||
|
||||
const query = useMutation({
|
||||
mutationFn: async ({ payload }) => {
|
||||
const { data } = await api.get('/v1/apps', {
|
||||
params: { name: appName },
|
||||
signal: abortControllerRef.current.signal,
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
onSuccess,
|
||||
});
|
||||
|
||||
return query;
|
||||
}
|
22
packages/web/src/hooks/useTriggerSubsteps.js
Normal file
22
packages/web/src/hooks/useTriggerSubsteps.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import api from 'helpers/api';
|
||||
|
||||
export default function useTriggerSubsteps(appKey, triggerKey) {
|
||||
const query = useQuery({
|
||||
queryKey: ['triggerSubsteps', appKey, triggerKey],
|
||||
queryFn: async ({ payload, signal }) => {
|
||||
const { data } = await api.get(
|
||||
`/v1/apps/${appKey}/triggers/${triggerKey}/substeps`,
|
||||
{
|
||||
signal,
|
||||
},
|
||||
);
|
||||
|
||||
return data;
|
||||
},
|
||||
enabled: !!appKey,
|
||||
});
|
||||
|
||||
return query;
|
||||
}
|
19
packages/web/src/hooks/useTriggers.js
Normal file
19
packages/web/src/hooks/useTriggers.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import api from 'helpers/api';
|
||||
|
||||
export default function useTriggers(appKey) {
|
||||
const query = useQuery({
|
||||
queryKey: ['triggers', appKey],
|
||||
queryFn: async ({ payload, signal }) => {
|
||||
const { data } = await api.get(`/v1/apps/${appKey}/triggers`, {
|
||||
signal,
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
enabled: !!appKey,
|
||||
});
|
||||
|
||||
return query;
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import {
|
||||
Link,
|
||||
Route,
|
||||
@@ -15,8 +14,8 @@ import Box from '@mui/material/Box';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Tabs from '@mui/material/Tabs';
|
||||
import Tab from '@mui/material/Tab';
|
||||
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import { GET_APP } from 'graphql/queries/get-app';
|
||||
import * as URLS from 'config/urls';
|
||||
import AppIcon from 'components/AppIcon';
|
||||
import Container from 'components/Container';
|
||||
@@ -25,6 +24,8 @@ import AdminApplicationSettings from 'components/AdminApplicationSettings';
|
||||
import AdminApplicationAuthClients from 'components/AdminApplicationAuthClients';
|
||||
import AdminApplicationCreateAuthClient from 'components/AdminApplicationCreateAuthClient';
|
||||
import AdminApplicationUpdateAuthClient from 'components/AdminApplicationUpdateAuthClient';
|
||||
import useApp from 'hooks/useApp';
|
||||
|
||||
export default function AdminApplication() {
|
||||
const theme = useTheme();
|
||||
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md'));
|
||||
@@ -43,10 +44,15 @@ export default function AdminApplication() {
|
||||
end: false,
|
||||
});
|
||||
const { appKey } = useParams();
|
||||
const { data, loading } = useQuery(GET_APP, { variables: { key: appKey } });
|
||||
const app = data?.getApp || {};
|
||||
|
||||
const { data, loading } = useApp(appKey);
|
||||
|
||||
const app = data?.data || {};
|
||||
|
||||
const goToAuthClientsPage = () => navigate('auth-clients');
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
|
@@ -2,24 +2,25 @@ import * as React from 'react';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import Container from 'components/Container';
|
||||
import SearchInput from 'components/SearchInput';
|
||||
import AppRow from 'components/AppRow';
|
||||
import * as URLS from 'config/urls';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import { GET_APPS } from 'graphql/queries/get-apps';
|
||||
import useApps from 'hooks/useApps';
|
||||
|
||||
function AdminApplications() {
|
||||
const formatMessage = useFormatMessage();
|
||||
const [appName, setAppName] = React.useState(null);
|
||||
const { data, loading: appsLoading } = useQuery(GET_APPS, {
|
||||
variables: { name: appName },
|
||||
});
|
||||
const apps = data?.getApps;
|
||||
|
||||
const { data: apps, isLoading: isAppsLoading } = useApps(appName);
|
||||
|
||||
const onSearchChange = React.useCallback((event) => {
|
||||
setAppName(event.target.value);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={10} md={9}>
|
||||
@@ -36,15 +37,15 @@ function AdminApplications() {
|
||||
<Divider sx={{ mt: [2, 0], mb: 2 }} />
|
||||
</Grid>
|
||||
|
||||
{appsLoading && (
|
||||
{isAppsLoading && (
|
||||
<CircularProgress
|
||||
data-test="apps-loader"
|
||||
sx={{ display: 'block', margin: '20px auto' }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!appsLoading &&
|
||||
apps?.map((app) => (
|
||||
{!isAppsLoading &&
|
||||
apps?.data?.map((app) => (
|
||||
<Grid item xs={12} key={app.name}>
|
||||
<AppRow application={app} url={URLS.ADMIN_APP(app.key)} />
|
||||
</Grid>
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import {
|
||||
Link,
|
||||
Route,
|
||||
@@ -17,9 +16,9 @@ import Grid from '@mui/material/Grid';
|
||||
import Tabs from '@mui/material/Tabs';
|
||||
import Tab from '@mui/material/Tab';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useAppConfig from 'hooks/useAppConfig.ee';
|
||||
import { GET_APP } from 'graphql/queries/get-app';
|
||||
import * as URLS from 'config/urls';
|
||||
import SplitButton from 'components/SplitButton';
|
||||
import ConditionalIconButton from 'components/ConditionalIconButton';
|
||||
@@ -29,9 +28,12 @@ import AddAppConnection from 'components/AddAppConnection';
|
||||
import AppIcon from 'components/AppIcon';
|
||||
import Container from 'components/Container';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import useApp from 'hooks/useApp';
|
||||
|
||||
const ReconnectConnection = (props) => {
|
||||
const { application, onClose } = props;
|
||||
const { connectionId } = useParams();
|
||||
|
||||
return (
|
||||
<AddAppConnection
|
||||
onClose={onClose}
|
||||
@@ -40,6 +42,7 @@ const ReconnectConnection = (props) => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Application() {
|
||||
const theme = useTheme();
|
||||
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md'));
|
||||
@@ -52,11 +55,15 @@ export default function Application() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const { appKey } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const { data, loading } = useQuery(GET_APP, { variables: { key: appKey } });
|
||||
|
||||
const { data, loading } = useApp(appKey);
|
||||
const app = data?.data || {};
|
||||
|
||||
const { appConfig } = useAppConfig(appKey);
|
||||
const connectionId = searchParams.get('connectionId') || undefined;
|
||||
|
||||
const goToApplicationPage = () => navigate('connections');
|
||||
const app = data?.getApp || {};
|
||||
|
||||
const connectionOptions = React.useMemo(() => {
|
||||
const shouldHaveCustomConnection =
|
||||
appConfig?.canConnect && appConfig?.canCustomConnect;
|
||||
@@ -68,6 +75,7 @@ export default function Application() {
|
||||
to: URLS.APP_ADD_CONNECTION(appKey, appConfig?.canConnect),
|
||||
},
|
||||
];
|
||||
|
||||
if (shouldHaveCustomConnection) {
|
||||
options.push({
|
||||
label: formatMessage('app.addCustomConnection'),
|
||||
@@ -76,9 +84,12 @@ export default function Application() {
|
||||
to: URLS.APP_ADD_CONNECTION(appKey),
|
||||
});
|
||||
}
|
||||
|
||||
return options;
|
||||
}, [appKey, appConfig]);
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ py: 3 }}>
|
||||
|
Reference in New Issue
Block a user