mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
refactor: restrict API tokens to enabled environments (#809)
* refactor: add missing Tooltip wrapper elements * refactor: rewrite useEnvironments * refactor: disable environments in select box * refactor: make sure initial environment is enabled
This commit is contained in:
parent
cc0b9f7291
commit
2ca88b019a
@ -1,7 +1,7 @@
|
||||
import { Button } from '@material-ui/core';
|
||||
import { KeyboardArrowDownOutlined } from '@material-ui/icons';
|
||||
import React from 'react';
|
||||
import useEnvironments from '../../../../hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import { useEnvironments } from '../../../../hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import useProjects from '../../../../hooks/api/getters/useProjects/useProjects';
|
||||
import GeneralSelect from '../../../common/GeneralSelect/GeneralSelect';
|
||||
import Input from '../../../common/Input/Input';
|
||||
@ -10,11 +10,11 @@ interface IApiTokenFormProps {
|
||||
username: string;
|
||||
type: string;
|
||||
project: string;
|
||||
environment: string;
|
||||
environment?: string;
|
||||
setTokenType: (value: string) => void;
|
||||
setUsername: React.Dispatch<React.SetStateAction<string>>;
|
||||
setProject: React.Dispatch<React.SetStateAction<string>>;
|
||||
setEnvironment: React.Dispatch<React.SetStateAction<string>>;
|
||||
setEnvironment: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
handleSubmit: (e: any) => void;
|
||||
handleCancel: () => void;
|
||||
errors: { [key: string]: string };
|
||||
@ -54,13 +54,15 @@ const ApiTokenForm: React.FC<IApiTokenFormProps> = ({
|
||||
title: i.name,
|
||||
})
|
||||
);
|
||||
|
||||
const selectableEnvs =
|
||||
type === TYPE_ADMIN
|
||||
? [{ key: '*', label: 'ALL' }]
|
||||
: environments.map(i => ({
|
||||
key: i.name,
|
||||
label: i.name,
|
||||
title: i.name,
|
||||
: environments.map(environment => ({
|
||||
key: environment.name,
|
||||
label: environment.name,
|
||||
title: environment.name,
|
||||
disabled: !environment.enabled,
|
||||
}));
|
||||
|
||||
return (
|
||||
|
@ -5,7 +5,7 @@ import { CreateButton } from 'component/common/CreateButton/CreateButton';
|
||||
import useApiTokensApi from 'hooks/api/actions/useApiTokensApi/useApiTokensApi';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import useApiTokenForm from '../hooks/useApiTokenForm';
|
||||
import { useApiTokenForm } from '../hooks/useApiTokenForm';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import { ConfirmToken } from '../ConfirmToken/ConfirmToken';
|
||||
import { useState } from 'react';
|
||||
|
@ -1,37 +1,19 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||||
|
||||
const useApiToken = (
|
||||
initialUserName = '',
|
||||
initialtype = 'CLIENT',
|
||||
initialProject = '*',
|
||||
initialEnvironment = 'default'
|
||||
) => {
|
||||
const [username, setUsername] = useState(initialUserName);
|
||||
const [type, setType] = useState(initialtype);
|
||||
const [project, setProject] = useState(initialtype);
|
||||
const [environment, setEnvironment] = useState(initialEnvironment);
|
||||
export const useApiTokenForm = () => {
|
||||
const { environments } = useEnvironments();
|
||||
const initialEnvironment = environments?.find(e => e.enabled)?.name;
|
||||
|
||||
const [username, setUsername] = useState('');
|
||||
const [type, setType] = useState('CLIENT');
|
||||
const [project, setProject] = useState('*');
|
||||
const [environment, setEnvironment] = useState<string>();
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
setUsername(initialUserName);
|
||||
}, [initialUserName]);
|
||||
|
||||
useEffect(() => {
|
||||
setType(initialtype);
|
||||
if (type === 'ADMIN') {
|
||||
setProject('*');
|
||||
setEnvironment('*');
|
||||
}
|
||||
//eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [initialtype]);
|
||||
|
||||
useEffect(() => {
|
||||
setProject(initialProject);
|
||||
}, [initialProject]);
|
||||
|
||||
useEffect(() => {
|
||||
setEnvironment(initialEnvironment);
|
||||
}, [initialEnvironment]);
|
||||
setEnvironment(type === 'ADMIN' ? '*' : initialEnvironment);
|
||||
}, [type, initialEnvironment]);
|
||||
|
||||
const setTokenType = (value: string) => {
|
||||
if (value === 'ADMIN') {
|
||||
@ -82,5 +64,3 @@ const useApiToken = (
|
||||
errors,
|
||||
};
|
||||
};
|
||||
|
||||
export default useApiToken;
|
||||
|
@ -7,6 +7,7 @@ export interface ISelectOption {
|
||||
key: string;
|
||||
title?: string;
|
||||
label?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface ISelectMenuProps {
|
||||
@ -52,6 +53,7 @@ const GeneralSelect: React.FC<ISelectMenuProps> = ({
|
||||
value={option.key}
|
||||
title={option.title || ''}
|
||||
data-test={`${SELECT_ITEM_ID}-${option.label}`}
|
||||
disabled={option.disabled}
|
||||
>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
|
@ -8,7 +8,7 @@ import { CreateButton } from 'component/common/CreateButton/CreateButton';
|
||||
import useEnvironmentApi from 'hooks/api/actions/useEnvironmentApi/useEnvironmentApi';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import useEnvironments from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import useProjectRolePermissions from 'hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import PageContent from 'component/common/PageContent/PageContent';
|
||||
|
@ -3,9 +3,7 @@ import ResponsiveButton from '../../common/ResponsiveButton/ResponsiveButton';
|
||||
import { Add } from '@material-ui/icons';
|
||||
import PageContent from '../../common/PageContent';
|
||||
import { List } from '@material-ui/core';
|
||||
import useEnvironments, {
|
||||
ENVIRONMENT_CACHE_KEY,
|
||||
} from '../../../hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import { useEnvironments } from '../../../hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import {
|
||||
IEnvironment,
|
||||
ISortOrderPayload,
|
||||
@ -16,7 +14,6 @@ import EnvironmentDeleteConfirm from './EnvironmentDeleteConfirm/EnvironmentDele
|
||||
import useToast from '../../../hooks/useToast';
|
||||
import useEnvironmentApi from '../../../hooks/api/actions/useEnvironmentApi/useEnvironmentApi';
|
||||
import EnvironmentListItem from './EnvironmentListItem/EnvironmentListItem';
|
||||
import { mutate } from 'swr';
|
||||
import EnvironmentToggleConfirm from './EnvironmentToggleConfirm/EnvironmentToggleConfirm';
|
||||
import useProjectRolePermissions from '../../../hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
@ -32,7 +29,7 @@ const EnvironmentList = () => {
|
||||
enabled: true,
|
||||
protected: false,
|
||||
};
|
||||
const { environments, refetch } = useEnvironments();
|
||||
const { environments, refetchEnvironments } = useEnvironments();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { refetch: refetchProjectRolePermissions } =
|
||||
useProjectRolePermissions();
|
||||
@ -58,8 +55,7 @@ const EnvironmentList = () => {
|
||||
const item = newEnvList.splice(dragIndex, 1)[0];
|
||||
|
||||
newEnvList.splice(hoverIndex, 0, item);
|
||||
|
||||
mutate(ENVIRONMENT_CACHE_KEY, { environments: newEnvList }, false);
|
||||
refetchEnvironments({ environments: newEnvList }, false);
|
||||
return newEnvList;
|
||||
};
|
||||
|
||||
@ -75,7 +71,6 @@ const EnvironmentList = () => {
|
||||
|
||||
try {
|
||||
await sortOrderAPICall(sortOrder);
|
||||
refetch();
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
@ -104,7 +99,7 @@ const EnvironmentList = () => {
|
||||
setDeldialogue(false);
|
||||
setSelectedEnv(defaultEnv);
|
||||
setConfirmName('');
|
||||
refetch();
|
||||
refetchEnvironments();
|
||||
}
|
||||
};
|
||||
|
||||
@ -128,7 +123,7 @@ const EnvironmentList = () => {
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
} finally {
|
||||
refetch();
|
||||
refetchEnvironments();
|
||||
}
|
||||
};
|
||||
|
||||
@ -144,7 +139,7 @@ const EnvironmentList = () => {
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
} finally {
|
||||
refetch();
|
||||
refetchEnvironments();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -152,9 +152,11 @@ const EnvironmentListItem = ({
|
||||
condition={updatePermission}
|
||||
show={
|
||||
<Tooltip title="Drag to reorder">
|
||||
<IconButton>
|
||||
<DragIndicator />
|
||||
</IconButton>
|
||||
<div>
|
||||
<IconButton>
|
||||
<DragIndicator titleAccess="Drag" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
@ -162,15 +164,16 @@ const EnvironmentListItem = ({
|
||||
condition={updatePermission}
|
||||
show={
|
||||
<Tooltip title={`${tooltipText} environment`}>
|
||||
<IconButton
|
||||
aria-label="disable"
|
||||
onClick={() => {
|
||||
setSelectedEnv(env);
|
||||
setToggleDialog(prev => !prev);
|
||||
}}
|
||||
>
|
||||
<OfflineBolt />
|
||||
</IconButton>
|
||||
<div>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setSelectedEnv(env);
|
||||
setToggleDialog(prev => !prev);
|
||||
}}
|
||||
>
|
||||
<OfflineBolt titleAccess="Toggle" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
@ -178,15 +181,16 @@ const EnvironmentListItem = ({
|
||||
condition={updatePermission}
|
||||
show={
|
||||
<Tooltip title="Update environment">
|
||||
<IconButton
|
||||
aria-label="update"
|
||||
disabled={env.protected}
|
||||
onClick={() => {
|
||||
history.push(`/environments/${env.name}`);
|
||||
}}
|
||||
>
|
||||
<Edit />
|
||||
</IconButton>
|
||||
<div>
|
||||
<IconButton
|
||||
disabled={env.protected}
|
||||
onClick={() => {
|
||||
history.push(`/environments/${env.name}`);
|
||||
}}
|
||||
>
|
||||
<Edit titleAccess="Edit" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
@ -194,16 +198,17 @@ const EnvironmentListItem = ({
|
||||
condition={hasAccess(DELETE_ENVIRONMENT)}
|
||||
show={
|
||||
<Tooltip title="Delete environment">
|
||||
<IconButton
|
||||
aria-label="delete"
|
||||
disabled={env.protected}
|
||||
onClick={() => {
|
||||
setDeldialogue(true);
|
||||
setSelectedEnv(env);
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
<div>
|
||||
<IconButton
|
||||
disabled={env.protected}
|
||||
onClick={() => {
|
||||
setDeldialogue(true);
|
||||
setSelectedEnv(env);
|
||||
}}
|
||||
>
|
||||
<Delete titleAccess="Delete" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
|
@ -10,7 +10,7 @@ import { UPDATE_PROJECT } from '../../providers/AccessProvider/permissions';
|
||||
import ApiError from '../../common/ApiError/ApiError';
|
||||
import useToast from '../../../hooks/useToast';
|
||||
import useUiConfig from '../../../hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useEnvironments from '../../../hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import { useEnvironments } from '../../../hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import useProject from '../../../hooks/api/getters/useProject/useProject';
|
||||
import { FormControlLabel, FormGroup } from '@material-ui/core';
|
||||
import useProjectApi from '../../../hooks/api/actions/useProjectApi/useProjectApi';
|
||||
@ -32,12 +32,8 @@ const ProjectEnvironmentList = ({ projectId }: ProjectEnvironmentListProps) => {
|
||||
const [envs, setEnvs] = useState<IProjectEnvironment[]>([]);
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const {
|
||||
environments,
|
||||
loading,
|
||||
error,
|
||||
refetch: refetchEnvs,
|
||||
} = useEnvironments();
|
||||
const { environments, loading, error, refetchEnvironments } =
|
||||
useEnvironments();
|
||||
const { project, refetch: refetchProject } = useProject(projectId);
|
||||
const { removeEnvironmentFromProject, addEnvironmentToProject } =
|
||||
useProjectApi();
|
||||
@ -59,7 +55,7 @@ const ProjectEnvironmentList = ({ projectId }: ProjectEnvironmentListProps) => {
|
||||
}, [environments, project?.environments]);
|
||||
|
||||
const refetch = () => {
|
||||
refetchEnvs();
|
||||
refetchEnvironments();
|
||||
refetchProject();
|
||||
};
|
||||
|
||||
|
@ -1,42 +1,35 @@
|
||||
import useSWR, { mutate, SWRConfiguration } from 'swr';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { IEnvironmentResponse } from '../../../../interfaces/environments';
|
||||
import { formatApiPath } from '../../../../utils/format-path';
|
||||
import useSWR, { mutate } from 'swr';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { IEnvironmentResponse } from 'interfaces/environments';
|
||||
import { formatApiPath } from 'utils/format-path';
|
||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||
|
||||
export const ENVIRONMENT_CACHE_KEY = `api/admin/environments`;
|
||||
const PATH = formatApiPath(`api/admin/environments`);
|
||||
|
||||
const useEnvironments = (options: SWRConfiguration = {}) => {
|
||||
const fetcher = () => {
|
||||
const path = formatApiPath(`api/admin/environments`);
|
||||
return fetch(path, {
|
||||
method: 'GET',
|
||||
})
|
||||
.then(handleErrorResponses('Environments'))
|
||||
.then(res => res.json());
|
||||
};
|
||||
export const useEnvironments = () => {
|
||||
const { data, error } = useSWR<IEnvironmentResponse>(PATH, fetcher);
|
||||
|
||||
const { data, error } = useSWR<IEnvironmentResponse>(
|
||||
ENVIRONMENT_CACHE_KEY,
|
||||
fetcher,
|
||||
options
|
||||
const refetchEnvironments = useCallback(
|
||||
(data?: IEnvironmentResponse, revalidate?: boolean) => {
|
||||
mutate(PATH, data, revalidate).catch(console.warn);
|
||||
},
|
||||
[]
|
||||
);
|
||||
const [loading, setLoading] = useState(!error && !data);
|
||||
|
||||
const refetch = () => {
|
||||
mutate(ENVIRONMENT_CACHE_KEY);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(!error && !data);
|
||||
}, [data, error]);
|
||||
const environments = useMemo(() => {
|
||||
return data?.environments || [];
|
||||
}, [data]);
|
||||
|
||||
return {
|
||||
environments: data?.environments || [],
|
||||
environments,
|
||||
refetchEnvironments,
|
||||
loading: !error && !data,
|
||||
error,
|
||||
loading,
|
||||
refetch,
|
||||
};
|
||||
};
|
||||
|
||||
export default useEnvironments;
|
||||
const fetcher = (): Promise<IEnvironmentResponse> => {
|
||||
return fetch(PATH)
|
||||
.then(handleErrorResponses('Environments'))
|
||||
.then(res => res.json());
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user