From 8aa812e0f5c80a5bc535cde0974f9f7c280f800a Mon Sep 17 00:00:00 2001
From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com>
Date: Thu, 1 Aug 2024 16:31:42 +0200
Subject: [PATCH] feat: Orphaned tokens - new API tokens list icon (#7693)
Moving warning icon from "projects" column, to left icon.
---
.../ApiTokenIcon/ApiTokenIcon.test.tsx | 46 +++++++++++++++
.../apiToken/ApiTokenIcon/ApiTokenIcon.tsx | 46 +++++++++++++++
.../ProjectsList/ProjectsList.test.tsx | 57 +------------------
.../apiToken/ProjectsList/ProjectsList.tsx | 37 +-----------
.../common/ApiTokenTable/useApiTokenTable.tsx | 8 +--
5 files changed, 99 insertions(+), 95 deletions(-)
create mode 100644 frontend/src/component/admin/apiToken/ApiTokenIcon/ApiTokenIcon.test.tsx
create mode 100644 frontend/src/component/admin/apiToken/ApiTokenIcon/ApiTokenIcon.tsx
diff --git a/frontend/src/component/admin/apiToken/ApiTokenIcon/ApiTokenIcon.test.tsx b/frontend/src/component/admin/apiToken/ApiTokenIcon/ApiTokenIcon.test.tsx
new file mode 100644
index 0000000000..3f0e7f924d
--- /dev/null
+++ b/frontend/src/component/admin/apiToken/ApiTokenIcon/ApiTokenIcon.test.tsx
@@ -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(
+ ,
+ );
+
+ 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(
+ ,
+ );
+
+ 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(
+ ,
+ );
+
+ const errorIcon = await screen.queryByTestId('orphaned-token-icon');
+ expect(errorIcon).toBeNull();
+ });
+
+ it('should not show warning for true wildcard tokens', async () => {
+ render(
+ ,
+ );
+
+ const errorIcon = await screen.queryByTestId('orphaned-token-icon');
+ expect(errorIcon).toBeNull();
+ });
+});
diff --git a/frontend/src/component/admin/apiToken/ApiTokenIcon/ApiTokenIcon.tsx b/frontend/src/component/admin/apiToken/ApiTokenIcon/ApiTokenIcon.tsx
new file mode 100644
index 0000000000..ad82509c87
--- /dev/null
+++ b/frontend/src/component/admin/apiToken/ApiTokenIcon/ApiTokenIcon.tsx
@@ -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 = ({ 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 (
+
+ 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.
+
+ }
+ placement='bottom-start'
+ arrow
+ >
+
+
+ }
+ />
+ );
+ }
+
+ return } />;
+};
diff --git a/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.test.tsx b/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.test.tsx
index c2b717834c..396e179d46 100644
--- a/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.test.tsx
+++ b/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.test.tsx
@@ -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(
- ,
- );
-
- 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(
- ,
- );
-
- 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(
- ,
- );
-
- const errorIcon = await screen.queryByTestId('ErrorIcon');
- expect(errorIcon).toBeNull();
- });
-
- it('should not show warning for wildcard tokens', async () => {
- render(
- ,
- );
-
- const errorIcon = await screen.queryByTestId('ErrorIcon');
- expect(errorIcon).toBeNull();
- });
- });
});
diff --git a/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.tsx b/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.tsx
index 4be5daae7c..7e802018d9 100644
--- a/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.tsx
+++ b/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.tsx
@@ -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 = ({
- projects,
- project,
- secret,
-}) => {
+export const ProjectsList: FC = ({ projects, project }) => {
const { searchQuery } = useSearchHighlightContext();
const projectsList =
@@ -85,35 +72,15 @@ export const ProjectsList: FC = ({
return ;
}
- 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 (
- 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
>
*
- }
- />
diff --git a/frontend/src/component/common/ApiTokenTable/useApiTokenTable.tsx b/frontend/src/component/common/ApiTokenTable/useApiTokenTable.tsx
index 4012414daf..48df548ead 100644
--- a/frontend/src/component/common/ApiTokenTable/useApiTokenTable.tsx
+++ b/frontend/src/component/common/ApiTokenTable/useApiTokenTable.tsx
@@ -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: () => } />,
+ Cell: (props: any) => (
+
+ ),
disableSortBy: true,
disableGlobalFilter: true,
width: 50,
@@ -61,7 +62,6 @@ export const useApiTokenTable = (
),
width: 160,