mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: Orphaned tokens - new API tokens list icon (#7693)
Moving warning icon from "projects" column, to left icon.
This commit is contained in:
parent
fc02581a10
commit
8aa812e0f5
@ -0,0 +1,46 @@
|
||||
import { render } from 'utils/testRenderer';
|
||||
import { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ApiTokenIcon } from './ApiTokenIcon';
|
||||
|
||||
describe('ApiTokenIcon', () => {
|
||||
it('should show warning icon if it is an orphaned token', async () => {
|
||||
render(
|
||||
<ApiTokenIcon secret='test:development.be7536c3a160ff15e3a92da45de531dd54bc1ae15d8455c0476f086b' />,
|
||||
);
|
||||
|
||||
const errorIcon = await screen.findByTestId('orphaned-token-icon');
|
||||
expect(errorIcon).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show tooltip with warning message if it is an orphaned token', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<ApiTokenIcon secret='test:development.be7536c3a160ff15e3a92da45de531dd54bc1ae15d8455c0476f086b' />,
|
||||
);
|
||||
|
||||
const errorIcon = await screen.findByTestId('orphaned-token-icon');
|
||||
user.hover(errorIcon);
|
||||
|
||||
const tooltip = await screen.findByRole('tooltip');
|
||||
expect(tooltip).toHaveTextContent(/orphaned token/);
|
||||
});
|
||||
|
||||
it('should not show warning icon if token is in v1 format', async () => {
|
||||
render(
|
||||
<ApiTokenIcon secret='be44368985f7fb3237c584ef86f3d6bdada42ddbd63a019d26955178' />,
|
||||
);
|
||||
|
||||
const errorIcon = await screen.queryByTestId('orphaned-token-icon');
|
||||
expect(errorIcon).toBeNull();
|
||||
});
|
||||
|
||||
it('should not show warning for true wildcard tokens', async () => {
|
||||
render(
|
||||
<ApiTokenIcon secret='*:development.be7536c3a160ff15e3a92da45de531dd54bc1ae15d8455c0476f086b' />,
|
||||
);
|
||||
|
||||
const errorIcon = await screen.queryByTestId('orphaned-token-icon');
|
||||
expect(errorIcon).toBeNull();
|
||||
});
|
||||
});
|
@ -0,0 +1,46 @@
|
||||
import type { FC } from 'react';
|
||||
import KeyIcon from '@mui/icons-material/Key';
|
||||
import WarningIcon from '@mui/icons-material/WarningAmber';
|
||||
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
||||
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
|
||||
|
||||
interface IApiTokenIconProps {
|
||||
project?: string;
|
||||
projects?: string | string[];
|
||||
secret?: string;
|
||||
}
|
||||
|
||||
export const ApiTokenIcon: FC<IApiTokenIconProps> = ({ secret }) => {
|
||||
const tokenFormat = secret?.includes(':') ? 'v2' : 'v1'; // see https://docs.getunleash.io/reference/api-tokens-and-client-keys#format
|
||||
const isWildcardToken = secret?.startsWith('*:');
|
||||
const isOrphanedToken = tokenFormat === 'v2' && !isWildcardToken;
|
||||
|
||||
if (isOrphanedToken) {
|
||||
return (
|
||||
<IconCell
|
||||
icon={
|
||||
<HtmlTooltip
|
||||
title={
|
||||
<p>
|
||||
This is an orphaned token. All of its original
|
||||
projects have been deleted and it now has access
|
||||
to all current and future projects. You should
|
||||
stop using this token and delete it.
|
||||
</p>
|
||||
}
|
||||
placement='bottom-start'
|
||||
arrow
|
||||
>
|
||||
<WarningIcon
|
||||
aria-label='Orphaned token'
|
||||
color='warning'
|
||||
data-testid='orphaned-token-icon'
|
||||
/>
|
||||
</HtmlTooltip>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <IconCell icon={<KeyIcon color='disabled' />} />;
|
||||
};
|
@ -1,7 +1,6 @@
|
||||
import { render } from 'utils/testRenderer';
|
||||
import { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ProjectsList } from 'component/admin/apiToken/ProjectsList/ProjectsList';
|
||||
import { ProjectsList } from './ProjectsList';
|
||||
|
||||
describe('ProjectsList', () => {
|
||||
it('should prioritize new "projects" array over deprecated "project"', async () => {
|
||||
@ -56,58 +55,4 @@ describe('ProjectsList', () => {
|
||||
|
||||
expect(container.textContent).toEqual('*');
|
||||
});
|
||||
|
||||
describe('orphaned tokens', () => {
|
||||
it('should show warning icon if it is an orphaned token', async () => {
|
||||
render(
|
||||
<ProjectsList
|
||||
projects={[]}
|
||||
secret='test:development.be7536c3a160ff15e3a92da45de531dd54bc1ae15d8455c0476f086b'
|
||||
/>,
|
||||
);
|
||||
|
||||
const errorIcon = await screen.findByTestId('ErrorIcon');
|
||||
expect(errorIcon).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show tooltip with warning message if it is an orphaned token', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<ProjectsList
|
||||
projects={[]}
|
||||
secret='test:development.be7536c3a160ff15e3a92da45de531dd54bc1ae15d8455c0476f086b'
|
||||
/>,
|
||||
);
|
||||
|
||||
const errorIcon = await screen.findByTestId('ErrorIcon');
|
||||
user.hover(errorIcon);
|
||||
|
||||
const tooltip = await screen.findByRole('tooltip');
|
||||
expect(tooltip).toHaveTextContent(/orphaned token/);
|
||||
});
|
||||
|
||||
it('should not show warning icon if token is in v1 format', async () => {
|
||||
render(
|
||||
<ProjectsList
|
||||
projects={[]}
|
||||
secret='be44368985f7fb3237c584ef86f3d6bdada42ddbd63a019d26955178'
|
||||
/>,
|
||||
);
|
||||
|
||||
const errorIcon = await screen.queryByTestId('ErrorIcon');
|
||||
expect(errorIcon).toBeNull();
|
||||
});
|
||||
|
||||
it('should not show warning for wildcard tokens', async () => {
|
||||
render(
|
||||
<ProjectsList
|
||||
projects={[]}
|
||||
secret='*:development.be7536c3a160ff15e3a92da45de531dd54bc1ae15d8455c0476f086b'
|
||||
/>,
|
||||
);
|
||||
|
||||
const errorIcon = await screen.queryByTestId('ErrorIcon');
|
||||
expect(errorIcon).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -5,9 +5,7 @@ import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
||||
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||
import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||
import ErrorIcon from '@mui/icons-material/Error';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
const StyledLink = styled(Link)(({ theme }) => ({
|
||||
textDecoration: 'none',
|
||||
@ -17,12 +15,6 @@ const StyledLink = styled(Link)(({ theme }) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledErrorIcon = styled(ErrorIcon)(({ theme }) => ({
|
||||
color: theme.palette.error.main,
|
||||
marginBottom: theme.spacing(0.5),
|
||||
marginLeft: theme.spacing(0.5),
|
||||
}));
|
||||
|
||||
const StyledContainer = styled('div')({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
@ -31,14 +23,9 @@ const StyledContainer = styled('div')({
|
||||
interface IProjectsListProps {
|
||||
project?: string;
|
||||
projects?: string | string[];
|
||||
secret?: string;
|
||||
}
|
||||
|
||||
export const ProjectsList: FC<IProjectsListProps> = ({
|
||||
projects,
|
||||
project,
|
||||
secret,
|
||||
}) => {
|
||||
export const ProjectsList: FC<IProjectsListProps> = ({ projects, project }) => {
|
||||
const { searchQuery } = useSearchHighlightContext();
|
||||
|
||||
const projectsList =
|
||||
@ -85,35 +72,15 @@ export const ProjectsList: FC<IProjectsListProps> = ({
|
||||
return <LinkCell to={`/projects/${item}`} title={item} />;
|
||||
}
|
||||
|
||||
const tokenFormat = secret?.includes(':') ? 'v2' : 'v1'; // see https://docs.getunleash.io/reference/api-tokens-and-client-keys#format
|
||||
const isWildcardToken = secret?.startsWith('*:');
|
||||
const isOrphanedToken = tokenFormat === 'v2' && !isWildcardToken;
|
||||
|
||||
return (
|
||||
<TextCell>
|
||||
<HtmlTooltip
|
||||
title={
|
||||
isOrphanedToken ? (
|
||||
<>
|
||||
This is an orphaned token. All of its original
|
||||
projects have been deleted and it now has access to
|
||||
all current and future projects. You should stop
|
||||
using this token and delete it. It will lose access
|
||||
to all projects at a later date.
|
||||
</>
|
||||
) : (
|
||||
'ALL current and future projects.'
|
||||
)
|
||||
}
|
||||
title='ALL current and future projects.'
|
||||
placement='bottom'
|
||||
arrow
|
||||
>
|
||||
<StyledContainer>
|
||||
<Highlighter search={searchQuery}>*</Highlighter>
|
||||
<ConditionallyRender
|
||||
condition={isOrphanedToken}
|
||||
show={<StyledErrorIcon aria-label='Orphaned token' />}
|
||||
/>
|
||||
</StyledContainer>
|
||||
</HtmlTooltip>
|
||||
</TextCell>
|
||||
|
@ -2,7 +2,6 @@ import { useMemo } from 'react';
|
||||
import type { IApiToken } from 'hooks/api/getters/useApiTokens/useApiTokens';
|
||||
import { DateCell } from 'component/common/Table/cells/DateCell/DateCell';
|
||||
import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
|
||||
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
|
||||
import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell';
|
||||
import {
|
||||
useTable,
|
||||
@ -12,7 +11,7 @@ import {
|
||||
} from 'react-table';
|
||||
import { sortTypes } from 'utils/sortTypes';
|
||||
import { ProjectsList } from 'component/admin/apiToken/ProjectsList/ProjectsList';
|
||||
import Key from '@mui/icons-material/Key';
|
||||
import { ApiTokenIcon } from 'component/admin/apiToken/ApiTokenIcon/ApiTokenIcon';
|
||||
|
||||
export const useApiTokenTable = (
|
||||
tokens: IApiToken[],
|
||||
@ -27,7 +26,9 @@ export const useApiTokenTable = (
|
||||
return [
|
||||
{
|
||||
id: 'Icon',
|
||||
Cell: () => <IconCell icon={<Key color='disabled' />} />,
|
||||
Cell: (props: any) => (
|
||||
<ApiTokenIcon secret={props.row.original.secret} />
|
||||
),
|
||||
disableSortBy: true,
|
||||
disableGlobalFilter: true,
|
||||
width: 50,
|
||||
@ -61,7 +62,6 @@ export const useApiTokenTable = (
|
||||
<ProjectsList
|
||||
project={props.row.original.project}
|
||||
projects={props.row.original.projects}
|
||||
secret={props.row.original.secret}
|
||||
/>
|
||||
),
|
||||
width: 160,
|
||||
|
Loading…
Reference in New Issue
Block a user