Merge branch 'main' into AUT-1372

This commit is contained in:
Jakub P.
2024-12-21 20:10:42 +01:00
173 changed files with 2933 additions and 2674 deletions

View File

@@ -83,6 +83,7 @@
"access": "public"
},
"devDependencies": {
"@simbathesailor/use-what-changed": "^2.0.0",
"@tanstack/eslint-plugin-query": "^5.20.1",
"@tanstack/react-query-devtools": "^5.24.1",
"eslint-config-prettier": "^9.1.0",

View File

@@ -9,7 +9,7 @@ import * as React from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { AppPropType } from 'propTypes/propTypes';
import AppAuthClientsDialog from 'components/AppAuthClientsDialog/index.ee';
import AppOAuthClientsDialog from 'components/OAuthClientsDialog/index.ee';
import InputCreator from 'components/InputCreator';
import * as URLS from 'config/urls';
import useAuthenticateApp from 'hooks/useAuthenticateApp.ee';
@@ -31,12 +31,12 @@ function AddAppConnection(props) {
const [inProgress, setInProgress] = React.useState(false);
const hasConnection = Boolean(connectionId);
const useShared = searchParams.get('shared') === 'true';
const appAuthClientId = searchParams.get('appAuthClientId') || undefined;
const oauthClientId = searchParams.get('oauthClientId') || undefined;
const { authenticate } = useAuthenticateApp({
appKey: key,
connectionId,
appAuthClientId,
useShared: !!appAuthClientId,
oauthClientId,
useShared: !!oauthClientId,
});
const queryClient = useQueryClient();
@@ -52,8 +52,8 @@ function AddAppConnection(props) {
}, []);
React.useEffect(
function initiateSharedAuthenticationForGivenAuthClient() {
if (!appAuthClientId) return;
function initiateSharedAuthenticationForGivenOAuthClient() {
if (!oauthClientId) return;
if (!authenticate) return;
@@ -64,13 +64,13 @@ function AddAppConnection(props) {
asyncAuthenticate();
},
[appAuthClientId, authenticate],
[oauthClientId, authenticate, key, navigate],
);
const handleClientClick = (appAuthClientId) =>
navigate(URLS.APP_ADD_CONNECTION_WITH_AUTH_CLIENT_ID(key, appAuthClientId));
const handleClientClick = (oauthClientId) =>
navigate(URLS.APP_ADD_CONNECTION_WITH_OAUTH_CLIENT_ID(key, oauthClientId));
const handleAuthClientsDialogClose = () =>
const handleOAuthClientsDialogClose = () =>
navigate(URLS.APP_CONNECTIONS(key));
const submitHandler = React.useCallback(
@@ -104,14 +104,14 @@ function AddAppConnection(props) {
if (useShared)
return (
<AppAuthClientsDialog
<AppOAuthClientsDialog
appKey={key}
onClose={handleAuthClientsDialogClose}
onClose={handleOAuthClientsDialogClose}
onClientClick={handleClientClick}
/>
);
if (appAuthClientId) return <React.Fragment />;
if (oauthClientId) return <React.Fragment />;
return (
<Dialog

View File

@@ -5,11 +5,11 @@ import { AppPropType } from 'propTypes/propTypes';
import useAdminCreateAppConfig from 'hooks/useAdminCreateAppConfig';
import useAppConfig from 'hooks/useAppConfig.ee';
import useFormatMessage from 'hooks/useFormatMessage';
import useAdminCreateAppAuthClient from 'hooks/useAdminCreateAppAuthClient.ee';
import AdminApplicationAuthClientDialog from 'components/AdminApplicationAuthClientDialog';
import useAdminCreateOAuthClient from 'hooks/useAdminCreateOAuthClient.ee';
import AdminApplicationOAuthClientDialog from 'components/AdminApplicationOAuthClientDialog';
import useAppAuth from 'hooks/useAppAuth';
function AdminApplicationCreateAuthClient(props) {
function AdminApplicationCreateOAuthClient(props) {
const { appKey, onClose } = props;
const { data: auth } = useAppAuth(appKey);
const formatMessage = useFormatMessage();
@@ -24,26 +24,26 @@ function AdminApplicationCreateAuthClient(props) {
} = useAdminCreateAppConfig(props.appKey);
const {
mutateAsync: createAppAuthClient,
isPending: isCreateAppAuthClientPending,
error: createAppAuthClientError,
} = useAdminCreateAppAuthClient(appKey);
mutateAsync: createOAuthClient,
isPending: isCreateOAuthClientPending,
error: createOAuthClientError,
} = useAdminCreateOAuthClient(appKey);
const submitHandler = async (values) => {
let appConfigKey = appConfig?.data?.key;
if (!appConfigKey) {
const { data: appConfigData } = await createAppConfig({
customConnectionAllowed: true,
shared: false,
useOnlyPredefinedAuthClients: false,
disabled: false,
});
appConfigKey = appConfigData.key;
}
const { name, active, ...formattedAuthDefaults } = values;
await createAppAuthClient({
await createOAuthClient({
appKey,
name,
active,
@@ -81,23 +81,23 @@ function AdminApplicationCreateAuthClient(props) {
);
return (
<AdminApplicationAuthClientDialog
<AdminApplicationOAuthClientDialog
onClose={onClose}
error={createAppConfigError || createAppAuthClientError}
title={formatMessage('createAuthClient.title')}
error={createAppConfigError || createOAuthClientError}
title={formatMessage('createOAuthClient.title')}
loading={isAppConfigLoading}
submitHandler={submitHandler}
authFields={auth?.data?.fields}
submitting={isCreateAppConfigPending || isCreateAppAuthClientPending}
submitting={isCreateAppConfigPending || isCreateOAuthClientPending}
defaultValues={defaultValues}
/>
);
}
AdminApplicationCreateAuthClient.propTypes = {
AdminApplicationCreateOAuthClient.propTypes = {
appKey: PropTypes.string.isRequired,
application: AppPropType.isRequired,
onClose: PropTypes.func.isRequired,
};
export default AdminApplicationCreateAuthClient;
export default AdminApplicationCreateOAuthClient;

View File

@@ -15,7 +15,7 @@ import Switch from 'components/Switch';
import TextField from 'components/TextField';
import { Form } from './style';
function AdminApplicationAuthClientDialog(props) {
function AdminApplicationOAuthClientDialog(props) {
const {
error,
onClose,
@@ -52,12 +52,12 @@ function AdminApplicationAuthClientDialog(props) {
<>
<Switch
name="active"
label={formatMessage('authClient.inputActive')}
label={formatMessage('oauthClient.inputActive')}
/>
<TextField
required={true}
name="name"
label={formatMessage('authClient.inputName')}
label={formatMessage('oauthClient.inputName')}
fullWidth
/>
{authFields?.map((field) => (
@@ -72,7 +72,7 @@ function AdminApplicationAuthClientDialog(props) {
loading={submitting}
disabled={disabled || !isDirty}
>
{formatMessage('authClient.buttonSubmit')}
{formatMessage('oauthClient.buttonSubmit')}
</LoadingButton>
</>
)}
@@ -84,7 +84,7 @@ function AdminApplicationAuthClientDialog(props) {
);
}
AdminApplicationAuthClientDialog.propTypes = {
AdminApplicationOAuthClientDialog.propTypes = {
error: PropTypes.shape({
message: PropTypes.string,
}),
@@ -98,4 +98,4 @@ AdminApplicationAuthClientDialog.propTypes = {
disabled: PropTypes.bool,
};
export default AdminApplicationAuthClientDialog;
export default AdminApplicationOAuthClientDialog;

View File

@@ -8,29 +8,30 @@ import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography';
import Chip from '@mui/material/Chip';
import Button from '@mui/material/Button';
import NoResultFound from 'components/NoResultFound';
import * as URLS from 'config/urls';
import useFormatMessage from 'hooks/useFormatMessage';
import useAdminAppAuthClients from 'hooks/useAdminAppAuthClients';
import NoResultFound from 'components/NoResultFound';
import useAdminOAuthClients from 'hooks/useAdminOAuthClients';
function AdminApplicationAuthClients(props) {
function AdminApplicationOAuthClients(props) {
const { appKey } = props;
const formatMessage = useFormatMessage();
const { data: appAuthClients, isLoading } = useAdminAppAuthClients(appKey);
const { data: appOAuthClients, isLoading } = useAdminOAuthClients(appKey);
if (isLoading)
return <CircularProgress sx={{ display: 'block', margin: '20px auto' }} />;
if (!appAuthClients?.data.length) {
if (!appOAuthClients?.data.length) {
return (
<NoResultFound
to={URLS.ADMIN_APP_AUTH_CLIENTS_CREATE(appKey)}
text={formatMessage('adminAppsAuthClients.noAuthClients')}
text={formatMessage('adminAppsOAuthClients.noOauthClients')}
/>
);
}
const sortedAuthClients = appAuthClients.data.slice().sort((a, b) => {
const sortedOAuthClients = appOAuthClients.data.slice().sort((a, b) => {
if (a.id < b.id) {
return -1;
}
@@ -42,7 +43,7 @@ function AdminApplicationAuthClients(props) {
return (
<div>
{sortedAuthClients.map((client) => (
{sortedOAuthClients.map((client) => (
<Card sx={{ mb: 1 }} key={client.id} data-test="auth-client">
<CardActionArea
component={Link}
@@ -59,8 +60,8 @@ function AdminApplicationAuthClients(props) {
variant={client?.active ? 'filled' : 'outlined'}
label={formatMessage(
client?.active
? 'adminAppsAuthClients.statusActive'
: 'adminAppsAuthClients.statusInactive',
? 'adminAppsOAuthClients.statusActive'
: 'adminAppsOAuthClients.statusInactive',
)}
/>
</Stack>
@@ -70,8 +71,13 @@ function AdminApplicationAuthClients(props) {
))}
<Stack justifyContent="flex-end" direction="row">
<Link to={URLS.ADMIN_APP_AUTH_CLIENTS_CREATE(appKey)}>
<Button variant="contained" sx={{ mt: 2 }} component="div" data-test="create-auth-client-button">
{formatMessage('createAuthClient.button')}
<Button
variant="contained"
sx={{ mt: 2 }}
component="div"
data-test="create-auth-client-button"
>
{formatMessage('createOAuthClient.button')}
</Button>
</Link>
</Stack>
@@ -79,8 +85,8 @@ function AdminApplicationAuthClients(props) {
);
}
AdminApplicationAuthClients.propTypes = {
AdminApplicationOAuthClients.propTypes = {
appKey: PropTypes.string.isRequired,
};
export default AdminApplicationAuthClients;
export default AdminApplicationOAuthClients;

View File

@@ -46,9 +46,8 @@ function AdminApplicationSettings(props) {
const defaultValues = useMemo(
() => ({
customConnectionAllowed:
appConfig?.data?.customConnectionAllowed || false,
shared: appConfig?.data?.shared || false,
useOnlyPredefinedAuthClients:
appConfig?.data?.useOnlyPredefinedAuthClients || false,
disabled: appConfig?.data?.disabled || false,
}),
[appConfig?.data],
@@ -62,21 +61,17 @@ function AdminApplicationSettings(props) {
<Paper sx={{ p: 2, mt: 4 }}>
<Stack spacing={2} direction="column">
<Switch
name="customConnectionAllowed"
label={formatMessage('adminAppsSettings.customConnectionAllowed')}
FormControlLabelProps={{
labelPlacement: 'start',
}}
/>
<Divider />
<Switch
name="shared"
label={formatMessage('adminAppsSettings.shared')}
name="useOnlyPredefinedAuthClients"
label={formatMessage(
'adminAppsSettings.useOnlyPredefinedAuthClients',
)}
FormControlLabelProps={{
labelPlacement: 'start',
}}
/>
<Divider />
<Switch
name="disabled"
label={formatMessage('adminAppsSettings.disabled')}
@@ -86,6 +81,7 @@ function AdminApplicationSettings(props) {
/>
<Divider />
</Stack>
<Stack>
<LoadingButton
data-test="submit-button"

View File

@@ -4,26 +4,26 @@ import { useParams } from 'react-router-dom';
import { AppPropType } from 'propTypes/propTypes';
import useFormatMessage from 'hooks/useFormatMessage';
import AdminApplicationAuthClientDialog from 'components/AdminApplicationAuthClientDialog';
import useAdminAppAuthClient from 'hooks/useAdminAppAuthClient.ee';
import useAdminUpdateAppAuthClient from 'hooks/useAdminUpdateAppAuthClient.ee';
import AdminApplicationOAuthClientDialog from 'components/AdminApplicationOAuthClientDialog';
import useAdminOAuthClient from 'hooks/useAdminOAuthClient.ee';
import useAdminUpdateOAuthClient from 'hooks/useAdminUpdateOAuthClient.ee';
import useAppAuth from 'hooks/useAppAuth';
function AdminApplicationUpdateAuthClient(props) {
function AdminApplicationUpdateOAuthClient(props) {
const { application, onClose } = props;
const formatMessage = useFormatMessage();
const { clientId } = useParams();
const { data: adminAppAuthClient, isLoading: isAdminAuthClientLoading } =
useAdminAppAuthClient(application.key, clientId);
const { data: adminOAuthClient, isLoading: isAdminOAuthClientLoading } =
useAdminOAuthClient(application.key, clientId);
const { data: auth } = useAppAuth(application.key);
const {
mutateAsync: updateAppAuthClient,
isPending: isUpdateAppAuthClientPending,
error: updateAppAuthClientError,
} = useAdminUpdateAppAuthClient(application.key, clientId);
mutateAsync: updateOAuthClient,
isPending: isUpdateOAuthClientPending,
error: updateOAuthClientError,
} = useAdminUpdateOAuthClient(application.key, clientId);
const authFields = auth?.data?.fields?.map((field) => ({
...field,
@@ -31,13 +31,13 @@ function AdminApplicationUpdateAuthClient(props) {
}));
const submitHandler = async (values) => {
if (!adminAppAuthClient) {
if (!adminOAuthClient) {
return;
}
const { name, active, ...formattedAuthDefaults } = values;
await updateAppAuthClient({
await updateOAuthClient({
name,
active,
formattedAuthDefaults,
@@ -64,31 +64,31 @@ function AdminApplicationUpdateAuthClient(props) {
const defaultValues = useMemo(
() => ({
name: adminAppAuthClient?.data?.name || '',
active: adminAppAuthClient?.data?.active || false,
name: adminOAuthClient?.data?.name || '',
active: adminOAuthClient?.data?.active || false,
...getAuthFieldsDefaultValues(),
}),
[adminAppAuthClient, getAuthFieldsDefaultValues],
[adminOAuthClient, getAuthFieldsDefaultValues],
);
return (
<AdminApplicationAuthClientDialog
<AdminApplicationOAuthClientDialog
onClose={onClose}
error={updateAppAuthClientError}
title={formatMessage('updateAuthClient.title')}
loading={isAdminAuthClientLoading}
error={updateOAuthClientError}
title={formatMessage('updateOAuthClient.title')}
loading={isAdminOAuthClientLoading}
submitHandler={submitHandler}
authFields={authFields}
submitting={isUpdateAppAuthClientPending}
submitting={isUpdateOAuthClientPending}
defaultValues={defaultValues}
disabled={!adminAppAuthClient}
disabled={!adminOAuthClient}
/>
);
}
AdminApplicationUpdateAuthClient.propTypes = {
AdminApplicationUpdateOAuthClient.propTypes = {
application: AppPropType.isRequired,
onClose: PropTypes.func.isRequired,
};
export default AdminApplicationUpdateAuthClient;
export default AdminApplicationUpdateOAuthClient;

View File

@@ -1,53 +0,0 @@
import PropTypes from 'prop-types';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import * as React from 'react';
import useAppAuthClients from 'hooks/useAppAuthClients';
import useFormatMessage from 'hooks/useFormatMessage';
function AppAuthClientsDialog(props) {
const { appKey, onClientClick, onClose } = props;
const { data: appAuthClients } = useAppAuthClients(appKey);
const formatMessage = useFormatMessage();
React.useEffect(
function autoAuthenticateSingleClient() {
if (appAuthClients?.data.length === 1) {
onClientClick(appAuthClients.data[0].id);
}
},
[appAuthClients?.data],
);
if (!appAuthClients?.data.length || appAuthClients?.data.length === 1)
return <React.Fragment />;
return (
<Dialog onClose={onClose} open={true}>
<DialogTitle>{formatMessage('appAuthClientsDialog.title')}</DialogTitle>
<List sx={{ pt: 0 }}>
{appAuthClients.data.map((appAuthClient) => (
<ListItem disableGutters key={appAuthClient.id}>
<ListItemButton onClick={() => onClientClick(appAuthClient.id)}>
<ListItemText primary={appAuthClient.name} />
</ListItemButton>
</ListItem>
))}
</List>
</Dialog>
);
}
AppAuthClientsDialog.propTypes = {
appKey: PropTypes.string.isRequired,
onClientClick: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
};
export default AppAuthClientsDialog;

View File

@@ -11,14 +11,7 @@ import { useQueryClient } from '@tanstack/react-query';
import Can from 'components/Can';
function ContextMenu(props) {
const {
appKey,
connection,
onClose,
onMenuItemClick,
anchorEl,
disableReconnection,
} = props;
const { appKey, connection, onClose, onMenuItemClick, anchorEl } = props;
const formatMessage = useFormatMessage();
const queryClient = useQueryClient();
@@ -73,11 +66,11 @@ function ContextMenu(props) {
{(allowed) => (
<MenuItem
component={Link}
disabled={!allowed || disableReconnection}
disabled={!allowed}
to={URLS.APP_RECONNECT_CONNECTION(
appKey,
connection.id,
connection.appAuthClientId,
connection.oauthClientId,
)}
onClick={createActionHandler({ type: 'reconnect' })}
>
@@ -109,7 +102,6 @@ ContextMenu.propTypes = {
PropTypes.func,
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
]),
disableReconnection: PropTypes.bool.isRequired,
};
export default ContextMenu;

View File

@@ -30,8 +30,7 @@ const countTranslation = (value) => (
function AppConnectionRow(props) {
const formatMessage = useFormatMessage();
const enqueueSnackbar = useEnqueueSnackbar();
const { id, key, formattedData, verified, createdAt, reconnectable } =
props.connection;
const { id, key, formattedData, verified, createdAt } = props.connection;
const [verificationVisible, setVerificationVisible] = React.useState(false);
const contextButtonRef = React.useRef(null);
const [anchorEl, setAnchorEl] = React.useState(null);
@@ -174,7 +173,6 @@ function AppConnectionRow(props) {
<ConnectionContextMenu
appKey={key}
connection={props.connection}
disableReconnection={!reconnectable}
onClose={handleClose}
onMenuItemClick={onContextMenuAction}
anchorEl={anchorEl}

View File

@@ -7,7 +7,7 @@ import TextField from '@mui/material/TextField';
import * as React from 'react';
import AddAppConnection from 'components/AddAppConnection';
import AppAuthClientsDialog from 'components/AppAuthClientsDialog/index.ee';
import AppOAuthClientsDialog from 'components/OAuthClientsDialog/index.ee';
import FlowSubstepTitle from 'components/FlowSubstepTitle';
import useAppConfig from 'hooks/useAppConfig.ee';
import { EditorContext } from 'contexts/Editor';
@@ -22,6 +22,7 @@ import useStepConnection from 'hooks/useStepConnection';
import { useQueryClient } from '@tanstack/react-query';
import useAppConnections from 'hooks/useAppConnections';
import useTestConnection from 'hooks/useTestConnection';
import useOAuthClients from 'hooks/useOAuthClients';
const ADD_CONNECTION_VALUE = 'ADD_CONNECTION';
const ADD_SHARED_CONNECTION_VALUE = 'ADD_SHARED_CONNECTION';
@@ -53,6 +54,7 @@ function ChooseConnectionSubstep(props) {
const [showAddSharedConnectionDialog, setShowAddSharedConnectionDialog] =
React.useState(false);
const queryClient = useQueryClient();
const { data: appOAuthClients } = useOAuthClients(application.key);
const { authenticate } = useAuthenticateApp({
appKey: application.key,
@@ -93,30 +95,53 @@ function ChooseConnectionSubstep(props) {
appWithConnections?.map((connection) => optionGenerator(connection)) ||
[];
const addCustomConnection = {
label: formatMessage('chooseConnectionSubstep.addNewConnection'),
value: ADD_CONNECTION_VALUE,
};
const addConnectionWithOAuthClient = {
label: formatMessage(
'chooseConnectionSubstep.addConnectionWithOAuthClient',
),
value: ADD_SHARED_CONNECTION_VALUE,
};
// means there is no app config. defaulting to custom connections only
if (!appConfig?.data) {
return options.concat([addCustomConnection]);
}
// app is disabled.
if (appConfig.data.disabled) return options;
// means only OAuth clients are allowed for connection creation and there is OAuth client
if (
!appConfig?.data ||
(!appConfig.data?.disabled && appConfig.data?.customConnectionAllowed)
appConfig.data.useOnlyPredefinedAuthClients === true &&
appOAuthClients.data.length > 0
) {
options.push({
label: formatMessage('chooseConnectionSubstep.addNewConnection'),
value: ADD_CONNECTION_VALUE,
});
return options.concat([addConnectionWithOAuthClient]);
}
if (appConfig?.data?.connectionAllowed) {
options.push({
label: formatMessage('chooseConnectionSubstep.addNewSharedConnection'),
value: ADD_SHARED_CONNECTION_VALUE,
});
// means there is no OAuth client. so we don't show the `addConnectionWithOAuthClient`
if (
appConfig.data.useOnlyPredefinedAuthClients === true &&
appOAuthClients.data.length === 0
) {
return options;
}
return options;
}, [data, formatMessage, appConfig?.data]);
if (appOAuthClients.data.length === 0) {
return options.concat([addCustomConnection]);
}
const handleClientClick = async (appAuthClientId) => {
return options.concat([addCustomConnection, addConnectionWithOAuthClient]);
}, [data, formatMessage, appConfig, appOAuthClients]);
const handleClientClick = async (oauthClientId) => {
try {
const response = await authenticate?.({
appAuthClientId,
oauthClientId,
});
const connectionId = response?.createConnection.id;
@@ -162,10 +187,7 @@ function ChooseConnectionSubstep(props) {
const handleChange = React.useCallback(
async (event, selectedOption) => {
if (typeof selectedOption === 'object') {
// TODO: try to simplify type casting below.
const typedSelectedOption = selectedOption;
const option = typedSelectedOption;
const connectionId = option?.value;
const connectionId = selectedOption?.value;
if (connectionId === ADD_CONNECTION_VALUE) {
setShowAddConnectionDialog(true);
@@ -270,7 +292,7 @@ function ChooseConnectionSubstep(props) {
)}
{application && showAddSharedConnectionDialog && (
<AppAuthClientsDialog
<AppOAuthClientsDialog
appKey={application.key}
onClose={() => setShowAddSharedConnectionDialog(false)}
onClientClick={handleClientClick}

View File

@@ -0,0 +1,43 @@
import PropTypes from 'prop-types';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import * as React from 'react';
import useOAuthClients from 'hooks/useOAuthClients';
import useFormatMessage from 'hooks/useFormatMessage';
function AppOAuthClientsDialog(props) {
const { appKey, onClientClick, onClose } = props;
const { data: appOAuthClients } = useOAuthClients(appKey);
const formatMessage = useFormatMessage();
if (!appOAuthClients?.data.length) return <React.Fragment />;
return (
<Dialog onClose={onClose} open={true}>
<DialogTitle>{formatMessage('appOAuthClientsDialog.title')}</DialogTitle>
<List sx={{ pt: 0 }}>
{appOAuthClients.data.map((oauthClient) => (
<ListItem disableGutters key={oauthClient.id}>
<ListItemButton onClick={() => onClientClick(oauthClient.id)}>
<ListItemText primary={oauthClient.name} />
</ListItemButton>
</ListItem>
))}
</List>
</Dialog>
);
}
AppOAuthClientsDialog.propTypes = {
appKey: PropTypes.string.isRequired,
onClientClick: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
};
export default AppOAuthClientsDialog;

View File

@@ -39,14 +39,14 @@ const PermissionCatalogFieldLoader = () => {
{[...Array(5)].map((action, index) => (
<TableCell key={index} align="center">
<Typography variant="subtitle2">
<ControlledCheckbox name="value" />
<ControlledCheckbox name="value" disabled />
</Typography>
</TableCell>
))}
<TableCell>
<Stack direction="row" gap={1} justifyContent="right">
<IconButton color="info" size="small">
<IconButton color="info" size="small" disabled>
<SettingsIcon />
</IconButton>
</Stack>

View File

@@ -21,13 +21,15 @@ const PermissionCatalogField = ({
name = 'permissions',
disabled = false,
syncIsCreator = false,
loading = false,
}) => {
const { data, isLoading: isPermissionCatalogLoading } =
usePermissionCatalog();
const permissionCatalog = data?.data;
const [dialogName, setDialogName] = React.useState();
if (isPermissionCatalogLoading) return <PermissionCatalogFieldLoader />;
if (isPermissionCatalogLoading || loading)
return <PermissionCatalogFieldLoader />;
return (
<TableContainer data-test="permissions-catalog" component={Paper}>
@@ -118,6 +120,7 @@ PermissionCatalogField.propTypes = {
name: PropTypes.string,
disabled: PropTypes.bool,
syncIsCreator: PropTypes.bool,
loading: PropTypes.bool,
};
export default PermissionCatalogField;

View File

@@ -67,17 +67,12 @@ export default function SplitButton(props) {
}}
open={open}
anchorEl={anchorRef.current}
placement="bottom-end"
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
{({ TransitionProps }) => (
<Grow {...TransitionProps}>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList autoFocusItem>

View File

@@ -17,19 +17,19 @@ export const APP_CONNECTIONS = (appKey) => `/app/${appKey}/connections`;
export const APP_CONNECTIONS_PATTERN = '/app/:appKey/connections';
export const APP_ADD_CONNECTION = (appKey, shared = false) =>
`/app/${appKey}/connections/add?shared=${shared}`;
export const APP_ADD_CONNECTION_WITH_AUTH_CLIENT_ID = (
export const APP_ADD_CONNECTION_WITH_OAUTH_CLIENT_ID = (
appKey,
appAuthClientId,
) => `/app/${appKey}/connections/add?appAuthClientId=${appAuthClientId}`;
oauthClientId,
) => `/app/${appKey}/connections/add?oauthClientId=${oauthClientId}`;
export const APP_ADD_CONNECTION_PATTERN = '/app/:appKey/connections/add';
export const APP_RECONNECT_CONNECTION = (
appKey,
connectionId,
appAuthClientId,
oauthClientId,
) => {
const path = `/app/${appKey}/connections/${connectionId}/reconnect`;
if (appAuthClientId) {
return `${path}?appAuthClientId=${appAuthClientId}`;
if (oauthClientId) {
return `${path}?oauthClientId=${oauthClientId}`;
}
return path;
};
@@ -71,18 +71,18 @@ export const ADMIN_APPS = `${ADMIN_SETTINGS}/apps`;
export const ADMIN_APP = (appKey) => `${ADMIN_SETTINGS}/apps/${appKey}`;
export const ADMIN_APP_PATTERN = `${ADMIN_SETTINGS}/apps/:appKey`;
export const ADMIN_APP_SETTINGS_PATTERN = `${ADMIN_SETTINGS}/apps/:appKey/settings`;
export const ADMIN_APP_AUTH_CLIENTS_PATTERN = `${ADMIN_SETTINGS}/apps/:appKey/auth-clients`;
export const ADMIN_APP_AUTH_CLIENTS_PATTERN = `${ADMIN_SETTINGS}/apps/:appKey/oauth-clients`;
export const ADMIN_APP_CONNECTIONS_PATTERN = `${ADMIN_SETTINGS}/apps/:appKey/connections`;
export const ADMIN_APP_CONNECTIONS = (appKey) =>
`${ADMIN_SETTINGS}/apps/${appKey}/connections`;
export const ADMIN_APP_SETTINGS = (appKey) =>
`${ADMIN_SETTINGS}/apps/${appKey}/settings`;
export const ADMIN_APP_AUTH_CLIENTS = (appKey) =>
`${ADMIN_SETTINGS}/apps/${appKey}/auth-clients`;
`${ADMIN_SETTINGS}/apps/${appKey}/oauth-clients`;
export const ADMIN_APP_AUTH_CLIENT = (appKey, id) =>
`${ADMIN_SETTINGS}/apps/${appKey}/auth-clients/${id}`;
`${ADMIN_SETTINGS}/apps/${appKey}/oauth-clients/${id}`;
export const ADMIN_APP_AUTH_CLIENTS_CREATE = (appKey) =>
`${ADMIN_SETTINGS}/apps/${appKey}/auth-clients/create`;
`${ADMIN_SETTINGS}/apps/${appKey}/oauth-clients/create`;
export const DASHBOARD = FLOWS;
// External links and paths

View File

@@ -1,19 +0,0 @@
import { useQuery } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useAdminAppAuthClient(appKey, id) {
const query = useQuery({
queryKey: ['admin', 'apps', appKey, 'authClients', id],
queryFn: async ({ signal }) => {
const { data } = await api.get(`/v1/admin/apps/${appKey}/auth-clients/${id}`, {
signal,
});
return data;
},
enabled: !!appKey && !!id,
});
return query;
}

View File

@@ -1,20 +1,23 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useAdminCreateAppAuthClient(appKey) {
export default function useAdminCreateOAuthClient(appKey) {
const queryClient = useQueryClient();
const query = useMutation({
mutationFn: async (payload) => {
const { data } = await api.post(`/v1/admin/apps/${appKey}/auth-clients`, payload);
const { data } = await api.post(
`/v1/admin/apps/${appKey}/oauth-clients`,
payload,
);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['admin', 'apps', appKey, 'authClients'],
queryKey: ['admin', 'apps', appKey, 'oauthClients'],
});
}
},
});
return query;

View File

@@ -0,0 +1,22 @@
import { useQuery } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useAdminOAuthClient(appKey, id) {
const query = useQuery({
queryKey: ['admin', 'apps', appKey, 'oauthClients', id],
queryFn: async ({ signal }) => {
const { data } = await api.get(
`/v1/admin/apps/${appKey}/oauth-clients/${id}`,
{
signal,
},
);
return data;
},
enabled: !!appKey && !!id,
});
return query;
}

View File

@@ -1,11 +1,11 @@
import { useQuery } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useAdminAppAuthClients(appKey) {
export default function useAdminOAuthClients(appKey) {
const query = useQuery({
queryKey: ['admin', 'apps', appKey, 'authClients'],
queryKey: ['admin', 'apps', appKey, 'oauthClients'],
queryFn: async ({ signal }) => {
const { data } = await api.get(`/v1/admin/apps/${appKey}/auth-clients`, {
const { data } = await api.get(`/v1/admin/apps/${appKey}/oauth-clients`, {
signal,
});
return data;

View File

@@ -1,13 +1,13 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useAdminUpdateAppAuthClient(appKey, id) {
export default function useAdminUpdateOAuthClient(appKey, id) {
const queryClient = useQueryClient();
const query = useMutation({
const mutation = useMutation({
mutationFn: async (payload) => {
const { data } = await api.patch(
`/v1/admin/apps/${appKey}/auth-clients/${id}`,
`/v1/admin/apps/${appKey}/oauth-clients/${id}`,
payload,
);
@@ -15,14 +15,14 @@ export default function useAdminUpdateAppAuthClient(appKey, id) {
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['admin', 'apps', appKey, 'authClients', id],
queryKey: ['admin', 'apps', appKey, 'oauthClients', id],
});
queryClient.invalidateQueries({
queryKey: ['admin', 'apps', appKey, 'authClients'],
queryKey: ['admin', 'apps', appKey, 'oauthClients'],
});
},
});
return query;
return mutation;
}

View File

@@ -13,6 +13,7 @@ import useCreateConnectionAuthUrl from './useCreateConnectionAuthUrl';
import useUpdateConnection from './useUpdateConnection';
import useResetConnection from './useResetConnection';
import useVerifyConnection from './useVerifyConnection';
import { useWhatChanged } from '@simbathesailor/use-what-changed';
function getSteps(auth, hasConnection, useShared) {
if (hasConnection) {
@@ -30,18 +31,20 @@ function getSteps(auth, hasConnection, useShared) {
}
export default function useAuthenticateApp(payload) {
const { appKey, appAuthClientId, connectionId, useShared = false } = payload;
const { appKey, oauthClientId, connectionId, useShared = false } = payload;
const { data: auth } = useAppAuth(appKey);
const queryClient = useQueryClient();
const { mutateAsync: createConnection } = useCreateConnection(appKey);
const { mutateAsync: createConnectionAuthUrl } = useCreateConnectionAuthUrl();
const { mutateAsync: updateConnection } = useUpdateConnection();
const { mutateAsync: resetConnection } = useResetConnection();
const { mutateAsync: verifyConnection } = useVerifyConnection();
const [authenticationInProgress, setAuthenticationInProgress] =
React.useState(false);
const formatMessage = useFormatMessage();
const steps = getSteps(auth?.data, !!connectionId, useShared);
const { mutateAsync: verifyConnection } = useVerifyConnection();
const steps = React.useMemo(() => {
return getSteps(auth?.data, !!connectionId, useShared);
}, [auth, connectionId, useShared]);
const authenticate = React.useMemo(() => {
if (!steps?.length) return;
@@ -52,12 +55,11 @@ export default function useAuthenticateApp(payload) {
const response = {
key: appKey,
appAuthClientId: appAuthClientId || payload.appAuthClientId,
oauthClientId: oauthClientId || payload.oauthClientId,
connectionId,
fields,
};
let stepIndex = 0;
while (stepIndex < steps?.length) {
const step = steps[stepIndex];
const variables = computeAuthStepVariables(step.arguments, response);
@@ -105,10 +107,10 @@ export default function useAuthenticateApp(payload) {
response[step.name] = stepResponse;
}
} catch (err) {
console.log(err);
console.error(err);
setAuthenticationInProgress(false);
queryClient.invalidateQueries({
await queryClient.invalidateQueries({
queryKey: ['apps', appKey, 'connections'],
});
@@ -126,13 +128,14 @@ export default function useAuthenticateApp(payload) {
return response;
};
// keep formatMessage out of it as it causes infinite loop.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
steps,
appKey,
appAuthClientId,
oauthClientId,
connectionId,
queryClient,
formatMessage,
createConnection,
createConnectionAuthUrl,
updateConnection,
@@ -140,6 +143,24 @@ export default function useAuthenticateApp(payload) {
verifyConnection,
]);
useWhatChanged(
[
steps,
appKey,
oauthClientId,
connectionId,
queryClient,
createConnection,
createConnectionAuthUrl,
updateConnection,
resetConnection,
verifyConnection,
],
'steps, appKey, oauthClientId, connectionId, queryClient, createConnection, createConnectionAuthUrl, updateConnection, resetConnection, verifyConnection',
'',
'useAuthenticate',
);
return {
authenticate,
inProgress: authenticationInProgress,

View File

@@ -9,7 +9,7 @@ export default function useAutomatischInfo() {
**/
staleTime: Infinity,
queryKey: ['automatisch', 'info'],
queryFn: async (payload, signal) => {
queryFn: async ({ signal }) => {
const { data } = await api.get('/v1/automatisch/info', { signal });
return data;

View File

@@ -3,10 +3,10 @@ import { useMutation } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useCreateConnection(appKey) {
const query = useMutation({
mutationFn: async ({ appAuthClientId, formattedData }) => {
const mutation = useMutation({
mutationFn: async ({ oauthClientId, formattedData }) => {
const { data } = await api.post(`/v1/apps/${appKey}/connections`, {
appAuthClientId,
oauthClientId,
formattedData,
});
@@ -14,5 +14,5 @@ export default function useCreateConnection(appKey) {
},
});
return query;
return mutation;
}

View File

@@ -0,0 +1,15 @@
import { useQuery } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useLicense() {
const query = useQuery({
queryKey: ['automatisch', 'license'],
queryFn: async ({ signal }) => {
const { data } = await api.get('/v1/automatisch/license', { signal });
return data;
},
});
return query;
}

View File

@@ -1,11 +1,11 @@
import { useQuery } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useAppAuthClients(appKey) {
export default function useOAuthClients(appKey) {
const query = useQuery({
queryKey: ['apps', appKey, 'auth-clients'],
queryKey: ['apps', appKey, 'oauth-clients'],
queryFn: async ({ signal }) => {
const { data } = await api.get(`/v1/apps/${appKey}/auth-clients`, {
const { data } = await api.get(`/v1/apps/${appKey}/oauth-clients`, {
signal,
});
return data;

View File

@@ -4,10 +4,10 @@ import api from 'helpers/api';
export default function useUpdateConnection() {
const query = useMutation({
mutationFn: async ({ connectionId, formattedData, appAuthClientId }) => {
mutationFn: async ({ connectionId, formattedData, oauthClientId }) => {
const { data } = await api.patch(`/v1/connections/${connectionId}`, {
formattedData,
appAuthClientId,
oauthClientId,
});
return data;

View File

@@ -22,7 +22,7 @@
"app.connectionCount": "{count} connections",
"app.flowCount": "{count} flows",
"app.addConnection": "Add connection",
"app.addCustomConnection": "Add custom connection",
"app.addConnectionWithOAuthClient": "Add connection with OAuth client",
"app.reconnectConnection": "Reconnect connection",
"app.createFlow": "Create flow",
"app.settings": "Settings",
@@ -74,7 +74,7 @@
"filterConditions.orContinueIf": "OR continue if…",
"chooseConnectionSubstep.continue": "Continue",
"chooseConnectionSubstep.addNewConnection": "Add new connection",
"chooseConnectionSubstep.addNewSharedConnection": "Add new shared connection",
"chooseConnectionSubstep.addConnectionWithOAuthClient": "Add connection with OAuth client",
"chooseConnectionSubstep.chooseConnection": "Choose connection",
"flow.createdAt": "created {datetime}",
"flow.updatedAt": "updated {datetime}",
@@ -258,7 +258,7 @@
"permissionSettings.cancel": "Cancel",
"permissionSettings.apply": "Apply",
"permissionSettings.title": "Conditions",
"appAuthClientsDialog.title": "Choose your authentication client",
"appOAuthClientsDialog.title": "Choose your authentication client",
"userInterfacePage.title": "User Interface",
"userInterfacePage.successfullyUpdated": "User interface has been updated.",
"userInterfacePage.titleFieldLabel": "Title",
@@ -290,22 +290,22 @@
"roleMappingsForm.successfullySaved": "Role mappings have been saved.",
"adminApps.title": "Apps",
"adminApps.connections": "Connections",
"adminApps.authClients": "Auth clients",
"adminApps.oauthClients": "OAuth clients",
"adminApps.settings": "Settings",
"adminAppsSettings.customConnectionAllowed": "Allow custom connection",
"adminAppsSettings.useOnlyPredefinedAuthClients": "Use only predefined OAuth clients",
"adminAppsSettings.shared": "Shared",
"adminAppsSettings.disabled": "Disabled",
"adminAppsSettings.save": "Save",
"adminAppsSettings.successfullySaved": "Settings have been saved.",
"adminAppsAuthClients.noAuthClients": "You don't have any auth clients yet.",
"adminAppsAuthClients.statusActive": "Active",
"adminAppsAuthClients.statusInactive": "Inactive",
"createAuthClient.button": "Create auth client",
"createAuthClient.title": "Create auth client",
"authClient.buttonSubmit": "Submit",
"authClient.inputName": "Name",
"authClient.inputActive": "Active",
"updateAuthClient.title": "Update auth client",
"adminAppsOAuthClients.noOauthClients": "You don't have any OAuth clients yet.",
"adminAppsOAuthClients.statusActive": "Active",
"adminAppsOAuthClients.statusInactive": "Inactive",
"createOAuthClient.button": "Create OAuth client",
"createOAuthClient.title": "Create OAuth client",
"oauthClient.buttonSubmit": "Submit",
"oauthClient.inputName": "Name",
"oauthClient.inputActive": "Active",
"updateOAuthClient.title": "Update OAuth client",
"notFoundPage.title": "We can't seem to find a page you're looking for.",
"notFoundPage.button": "Back to home page"
}

View File

@@ -21,9 +21,9 @@ import AppIcon from 'components/AppIcon';
import Container from 'components/Container';
import PageTitle from 'components/PageTitle';
import AdminApplicationSettings from 'components/AdminApplicationSettings';
import AdminApplicationAuthClients from 'components/AdminApplicationAuthClients';
import AdminApplicationCreateAuthClient from 'components/AdminApplicationCreateAuthClient';
import AdminApplicationUpdateAuthClient from 'components/AdminApplicationUpdateAuthClient';
import AdminApplicationOAuthClients from 'components/AdminApplicationOAuthClients';
import AdminApplicationCreateOAuthClient from 'components/AdminApplicationCreateOAuthClient';
import AdminApplicationUpdateOAuthClient from 'components/AdminApplicationUpdateOAuthClient';
import useApp from 'hooks/useApp';
export default function AdminApplication() {
@@ -39,7 +39,7 @@ export default function AdminApplication() {
path: URLS.ADMIN_APP_SETTINGS_PATTERN,
end: false,
});
const authClientsPathMatch = useMatch({
const oauthClientsPathMatch = useMatch({
path: URLS.ADMIN_APP_AUTH_CLIENTS_PATTERN,
end: false,
});
@@ -49,7 +49,7 @@ export default function AdminApplication() {
const app = data?.data || {};
const goToAuthClientsPage = () => navigate('auth-clients');
const goToAuthClientsPage = () => navigate('oauth-clients');
if (loading) return null;
@@ -77,7 +77,7 @@ export default function AdminApplication() {
value={
settingsPathMatch?.pattern?.path ||
connectionsPathMatch?.pattern?.path ||
authClientsPathMatch?.pattern?.path
oauthClientsPathMatch?.pattern?.path
}
>
<Tab
@@ -87,18 +87,12 @@ export default function AdminApplication() {
component={Link}
/>
<Tab
label={formatMessage('adminApps.authClients')}
data-test="oauth-clients-tab"
label={formatMessage('adminApps.oauthClients')}
to={URLS.ADMIN_APP_AUTH_CLIENTS(appKey)}
value={URLS.ADMIN_APP_AUTH_CLIENTS_PATTERN}
component={Link}
/>
<Tab
label={formatMessage('adminApps.connections')}
to={URLS.ADMIN_APP_CONNECTIONS(appKey)}
value={URLS.ADMIN_APP_CONNECTIONS_PATTERN}
disabled={!app.supportsConnections}
component={Link}
/>
</Tabs>
</Box>
@@ -108,12 +102,8 @@ export default function AdminApplication() {
element={<AdminApplicationSettings appKey={appKey} />}
/>
<Route
path={`/auth-clients/*`}
element={<AdminApplicationAuthClients appKey={appKey} />}
/>
<Route
path={`/connections/*`}
element={<div>App connections</div>}
path={`/oauth-clients/*`}
element={<AdminApplicationOAuthClients appKey={appKey} />}
/>
<Route
path="/"
@@ -128,9 +118,9 @@ export default function AdminApplication() {
</Container>
<Routes>
<Route
path="/auth-clients/create"
path="/oauth-clients/create"
element={
<AdminApplicationCreateAuthClient
<AdminApplicationCreateOAuthClient
application={app}
onClose={goToAuthClientsPage}
appKey={appKey}
@@ -138,9 +128,9 @@ export default function AdminApplication() {
}
/>
<Route
path="/auth-clients/:clientId"
path="/oauth-clients/:clientId"
element={
<AdminApplicationUpdateAuthClient
<AdminApplicationUpdateOAuthClient
application={app}
onClose={goToAuthClientsPage}
/>

View File

@@ -6,7 +6,6 @@ import {
Navigate,
Routes,
useParams,
useSearchParams,
useMatch,
useNavigate,
} from 'react-router-dom';
@@ -31,6 +30,7 @@ import AppIcon from 'components/AppIcon';
import Container from 'components/Container';
import PageTitle from 'components/PageTitle';
import useApp from 'hooks/useApp';
import useOAuthClients from 'hooks/useOAuthClients';
import Can from 'components/Can';
import { AppPropType } from 'propTypes/propTypes';
@@ -61,47 +61,59 @@ export default function Application() {
end: false,
});
const flowsPathMatch = useMatch({ path: URLS.APP_FLOWS_PATTERN, end: false });
const [searchParams] = useSearchParams();
const { appKey } = useParams();
const navigate = useNavigate();
const { data: appOAuthClients } = useOAuthClients(appKey);
const { data, loading } = useApp(appKey);
const app = data?.data || {};
const { data: appConfig } = useAppConfig(appKey);
const connectionId = searchParams.get('connectionId') || undefined;
const currentUserAbility = useCurrentUserAbility();
const goToApplicationPage = () => navigate('connections');
const connectionOptions = React.useMemo(() => {
const shouldHaveCustomConnection =
appConfig?.data?.connectionAllowed &&
appConfig?.data?.customConnectionAllowed;
const addCustomConnection = {
label: formatMessage('app.addConnection'),
key: 'addConnection',
'data-test': 'add-connection-button',
to: URLS.APP_ADD_CONNECTION(appKey, false),
disabled:
!currentUserAbility.can('create', 'Connection') ||
appConfig?.data?.useOnlyPredefinedAuthClients === true ||
appConfig?.data?.disabled === true,
};
const options = [
{
label: formatMessage('app.addConnection'),
key: 'addConnection',
'data-test': 'add-connection-button',
to: URLS.APP_ADD_CONNECTION(appKey, appConfig?.data?.connectionAllowed),
disabled: !currentUserAbility.can('create', 'Connection'),
},
];
const addConnectionWithOAuthClient = {
label: formatMessage('app.addConnectionWithOAuthClient'),
key: 'addConnectionWithOAuthClient',
'data-test': 'add-connection-with-auth-client-button',
to: URLS.APP_ADD_CONNECTION(appKey, true),
disabled:
!currentUserAbility.can('create', 'Connection') ||
appOAuthClients?.data?.length === 0 ||
appConfig?.data?.disabled === true,
};
if (shouldHaveCustomConnection) {
options.push({
label: formatMessage('app.addCustomConnection'),
key: 'addCustomConnection',
'data-test': 'add-custom-connection-button',
to: URLS.APP_ADD_CONNECTION(appKey),
disabled: !currentUserAbility.can('create', 'Connection'),
});
// means there is no app config. defaulting to custom connections only
if (!appConfig?.data) {
return [addCustomConnection];
}
return options;
}, [appKey, appConfig?.data, currentUserAbility, formatMessage]);
// means only OAuth clients are allowed for connection creation
if (appConfig?.data?.useOnlyPredefinedAuthClients === true) {
return [addConnectionWithOAuthClient];
}
// means there is no OAuth client. so we don't show the `addConnectionWithOAuthClient`
if (appOAuthClients?.data?.length === 0) {
return [addCustomConnection];
}
return [addCustomConnection, addConnectionWithOAuthClient];
}, [appKey, appConfig, appOAuthClients, currentUserAbility, formatMessage]);
if (loading) return null;
@@ -153,14 +165,7 @@ export default function Application() {
<Can I="create" a="Connection" passThrough>
{(allowed) => (
<SplitButton
disabled={
!allowed ||
(appConfig?.data &&
!appConfig?.data?.disabled &&
!appConfig?.data?.connectionAllowed &&
!appConfig?.data?.customConnectionAllowed) ||
connectionOptions.every(({ disabled }) => disabled)
}
disabled={!allowed}
options={connectionOptions}
/>
)}

View File

@@ -66,8 +66,8 @@ function RoleMappings({ provider, providerLoading }) {
const enqueueSnackbar = useEnqueueSnackbar();
const {
mutateAsync: updateSamlAuthProvidersRoleMappings,
isPending: isUpdateSamlAuthProvidersRoleMappingsPending,
mutateAsync: updateRoleMappings,
isPending: isUpdateRoleMappingsPending,
} = useAdminUpdateSamlAuthProviderRoleMappings(provider?.id);
const { data, isLoading: isAdminSamlAuthProviderRoleMappingsLoading } =
@@ -79,7 +79,7 @@ function RoleMappings({ provider, providerLoading }) {
const handleRoleMappingsUpdate = async (values) => {
try {
if (provider?.id) {
await updateSamlAuthProvidersRoleMappings(
await updateRoleMappings(
values.roleMappings.map(({ roleId, remoteRoleName }) => ({
roleId,
remoteRoleName,
@@ -148,7 +148,7 @@ function RoleMappings({ provider, providerLoading }) {
variant="contained"
color="primary"
sx={{ boxShadow: 2 }}
loading={isUpdateSamlAuthProvidersRoleMappingsPending}
loading={isUpdateRoleMappingsPending}
>
{formatMessage('roleMappingsForm.save')}
</LoadingButton>

View File

@@ -25,7 +25,8 @@ export default function CreateRole() {
const enqueueSnackbar = useEnqueueSnackbar();
const { mutateAsync: createRole, isPending: isCreateRolePending } =
useAdminCreateRole();
const { data: permissionCatalogData } = usePermissionCatalog();
const { data: permissionCatalogData, isLoading: isPermissionCatalogLoading } =
usePermissionCatalog();
const defaultValues = React.useMemo(
() => ({
@@ -91,6 +92,7 @@ export default function CreateRole() {
label={formatMessage('roleForm.name')}
fullWidth
data-test="name-input"
disabled={isPermissionCatalogLoading}
/>
<TextField
@@ -98,6 +100,7 @@ export default function CreateRole() {
label={formatMessage('roleForm.description')}
fullWidth
data-test="description-input"
disabled={isPermissionCatalogLoading}
/>
<PermissionCatalogField name="computedPermissions" />

View File

@@ -1,6 +1,5 @@
import LoadingButton from '@mui/lab/LoadingButton';
import Grid from '@mui/material/Grid';
import Skeleton from '@mui/material/Skeleton';
import Stack from '@mui/material/Stack';
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
import * as React from 'react';
@@ -30,7 +29,8 @@ export default function EditRole() {
const { data: roleData, isLoading: isRoleLoading } = useRole({ roleId });
const { mutateAsync: updateRole, isPending: isUpdateRolePending } =
useAdminUpdateRole(roleId);
const { data: permissionCatalogData } = usePermissionCatalog();
const { data: permissionCatalogData, isLoading: isPermissionCatalogLoading } =
usePermissionCatalog();
const role = roleData?.data;
const permissionCatalog = permissionCatalogData?.data;
const enqueueSnackbar = useEnqueueSnackbar();
@@ -84,36 +84,30 @@ export default function EditRole() {
<Grid item xs={12} justifyContent="flex-end" sx={{ pt: 5 }}>
<Form defaultValues={defaultValues} onSubmit={handleRoleUpdate}>
<Stack direction="column" gap={2}>
{isRoleLoading && (
<>
<Skeleton variant="rounded" height={55} />
<Skeleton variant="rounded" height={55} />
</>
)}
{!isRoleLoading && role && (
<>
<TextField
disabled={role.isAdmin}
required={true}
name="name"
label={formatMessage('roleForm.name')}
data-test="name-input"
fullWidth
/>
<TextField
disabled={role.isAdmin}
name="description"
label={formatMessage('roleForm.description')}
data-test="description-input"
fullWidth
/>
</>
)}
<TextField
disabled={
role?.isAdmin || isRoleLoading || isPermissionCatalogLoading
}
required={true}
name="name"
label={formatMessage('roleForm.name')}
data-test="name-input"
fullWidth
/>
<TextField
disabled={
role?.isAdmin || isRoleLoading || isPermissionCatalogLoading
}
name="description"
label={formatMessage('roleForm.description')}
data-test="description-input"
fullWidth
/>
<PermissionCatalogField
name="computedPermissions"
disabled={role?.isAdmin}
syncIsCreator
loading={isRoleLoading}
/>
<LoadingButton
type="submit"

View File

@@ -211,8 +211,7 @@ export const ConnectionPropType = PropTypes.shape({
flowCount: PropTypes.number,
appData: AppPropType,
createdAt: PropTypes.number,
reconnectable: PropTypes.bool,
appAuthClientId: PropTypes.string,
oauthClientId: PropTypes.string,
});
AppPropType.connection = PropTypes.arrayOf(ConnectionPropType);
@@ -459,13 +458,12 @@ export const SamlAuthProviderRolePropType = PropTypes.shape({
export const AppConfigPropType = PropTypes.shape({
id: PropTypes.string,
key: PropTypes.string,
customConnectionAllowed: PropTypes.bool,
connectionAllowed: PropTypes.bool,
useOnlyPredefinedAuthClients: PropTypes.bool,
shared: PropTypes.bool,
disabled: PropTypes.bool,
});
export const AppAuthClientPropType = PropTypes.shape({
export const OAuthClientPropType = PropTypes.shape({
id: PropTypes.string,
name: PropTypes.string,
appConfigKey: PropTypes.string,

View File

@@ -2126,6 +2126,11 @@
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz#427d5549943a9c6fce808e39ea64dbe60d4047f1"
integrity sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==
"@simbathesailor/use-what-changed@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@simbathesailor/use-what-changed/-/use-what-changed-2.0.0.tgz#7f82d78f92c8588b5fadd702065dde93bd781403"
integrity sha512-ulBNrPSvfho9UN6zS2fii3AsdEcp2fMaKeqUZZeCNPaZbB6aXyTUhpEN9atjMAbu/eyK3AY8L4SYJUG62Ekocw==
"@sinclair/typebox@^0.24.1":
version "0.24.51"
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f"
@@ -9784,7 +9789,16 @@ string-natural-compare@^3.0.1:
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.1.0, string-width@^4.2.0:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -9888,7 +9902,14 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -10952,7 +10973,16 @@ workbox-window@6.6.1:
"@types/trusted-types" "^2.0.2"
workbox-core "6.6.1"
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==