Skip to content

Commit 1eef6c2

Browse files
authored
:chore: Remove notification dependency from use-personal-token hook (#482)
1 parent 19bf0ad commit 1eef6c2

File tree

5 files changed

+131
-79
lines changed

5 files changed

+131
-79
lines changed

web_ui/src/core/personal-access-tokens/hooks/use-personal-access-token.hook.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import { useMutation, useQuery, useQueryClient, UseQueryResult } from '@tanstack
66
import { AxiosError } from 'axios';
77

88
import QUERY_KEYS from '../../../../packages/core/src/requests/query-keys';
9-
import { NOTIFICATION_TYPE } from '../../../notification/notification-toast/notification-type.enum';
10-
import { useNotification } from '../../../notification/notification.component';
119
import {
1210
BaseTokenProps,
1311
CreatePersonalAccessTokenPayload,
@@ -19,14 +17,9 @@ import {
1917
UsePersonalAccessToken,
2018
} from '../personal-access-tokens.interface';
2119

22-
export const DELETE_MESSAGE = 'Personal Access Token was not deleted due to an error.';
23-
export const RETRIEVE_ERROR = 'Personal Access Token was not retrieved due to an error.';
24-
export const UPDATE_MESSAGE = 'The expiration date has been updated.';
25-
2620
export const usePersonalAccessToken = (): UsePersonalAccessToken => {
2721
const queryClient = useQueryClient();
2822
const { personalAccessTokensService } = useApplicationServices();
29-
const { addNotification } = useNotification();
3023

3124
const createPersonalAccessTokenMutation = useMutation<
3225
PersonalAccessToken,
@@ -38,10 +31,6 @@ export const usePersonalAccessToken = (): UsePersonalAccessToken => {
3831
onSuccess: () => {
3932
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.SERVICE_ACCOUNTS_API_KEY });
4033
},
41-
onError: (error: AxiosError) => {
42-
const message = error?.message ?? RETRIEVE_ERROR;
43-
addNotification({ message, type: NOTIFICATION_TYPE.ERROR });
44-
},
4534
});
4635

4736
const deletePersonalAccessTokenMutation = useMutation<void, AxiosError, DeletePersonalAccessTokenPayload>({
@@ -50,10 +39,6 @@ export const usePersonalAccessToken = (): UsePersonalAccessToken => {
5039
onSuccess: () => {
5140
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.SERVICE_ACCOUNTS_API_KEY });
5241
},
53-
onError: (error: AxiosError) => {
54-
const message = error?.message ?? DELETE_MESSAGE;
55-
addNotification({ message, type: NOTIFICATION_TYPE.ERROR });
56-
},
5742
});
5843

5944
const updatePersonalAccessTokenMutation = useMutation<
@@ -65,11 +50,6 @@ export const usePersonalAccessToken = (): UsePersonalAccessToken => {
6550
personalAccessTokensService.updatePersonalAccessToken(props),
6651
onSuccess: () => {
6752
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.SERVICE_ACCOUNTS_API_KEY });
68-
addNotification({ message: UPDATE_MESSAGE, type: NOTIFICATION_TYPE.DEFAULT });
69-
},
70-
onError: (error: AxiosError) => {
71-
const message = error?.message ?? DELETE_MESSAGE;
72-
addNotification({ message, type: NOTIFICATION_TYPE.ERROR });
7353
},
7454
});
7555

web_ui/src/core/personal-access-tokens/hooks/use-personal-access-token.test.tsx

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,8 @@
33

44
import { waitFor } from '@testing-library/react';
55

6-
import { NOTIFICATION_TYPE } from '../../../notification/notification-toast/notification-type.enum';
76
import { renderHookWithProviders } from '../../../test-utils/render-hook-with-providers';
8-
import {
9-
DELETE_MESSAGE,
10-
RETRIEVE_ERROR,
11-
UPDATE_MESSAGE,
12-
usePersonalAccessToken,
13-
} from './use-personal-access-token.hook';
7+
import { usePersonalAccessToken } from './use-personal-access-token.hook';
148

159
const mockInvalidateQueries = jest.fn();
1610
jest.mock('@tanstack/react-query', () => ({
@@ -32,12 +26,6 @@ jest.mock('../../../core/personal-access-tokens/in-memory-personal-access-tokens
3226
}),
3327
}));
3428

35-
const mockAddNotification = jest.fn();
36-
jest.mock('../../../notification/notification.component', () => ({
37-
...jest.requireActual('../../../notification/notification.component'),
38-
useNotification: () => ({ addNotification: mockAddNotification }),
39-
}));
40-
4129
const renderPersonalAccessTokenHook = () => {
4230
return renderHookWithProviders(() => usePersonalAccessToken());
4331
};
@@ -84,10 +72,6 @@ describe('usePersonalAccessToken', () => {
8472

8573
await waitFor(() => {
8674
expect(mockInvalidateQueries).not.toHaveBeenCalled();
87-
expect(mockAddNotification).toHaveBeenCalledWith({
88-
message: errorMessage,
89-
type: NOTIFICATION_TYPE.ERROR,
90-
});
9175
});
9276
});
9377

@@ -104,10 +88,6 @@ describe('usePersonalAccessToken', () => {
10488
});
10589
await waitFor(() => {
10690
expect(mockInvalidateQueries).not.toHaveBeenCalled();
107-
expect(mockAddNotification).toHaveBeenCalledWith({
108-
message: RETRIEVE_ERROR,
109-
type: NOTIFICATION_TYPE.ERROR,
110-
});
11191
});
11292
});
11393
});
@@ -138,10 +118,6 @@ describe('usePersonalAccessToken', () => {
138118
});
139119
await waitFor(() => {
140120
expect(mockInvalidateQueries).not.toHaveBeenCalled();
141-
expect(mockAddNotification).toHaveBeenCalledWith({
142-
message: errorMessage,
143-
type: NOTIFICATION_TYPE.ERROR,
144-
});
145121
});
146122
});
147123

@@ -156,10 +132,6 @@ describe('usePersonalAccessToken', () => {
156132
});
157133
await waitFor(() => {
158134
expect(mockInvalidateQueries).not.toHaveBeenCalled();
159-
expect(mockAddNotification).toHaveBeenCalledWith({
160-
message: DELETE_MESSAGE,
161-
type: NOTIFICATION_TYPE.ERROR,
162-
});
163135
});
164136
});
165137
});
@@ -176,10 +148,6 @@ describe('usePersonalAccessToken', () => {
176148
});
177149
await waitFor(() => {
178150
expect(mockInvalidateQueries).toHaveBeenCalled();
179-
expect(mockAddNotification).toHaveBeenCalledWith({
180-
message: UPDATE_MESSAGE,
181-
type: NOTIFICATION_TYPE.DEFAULT,
182-
});
183151
});
184152
});
185153

@@ -196,10 +164,6 @@ describe('usePersonalAccessToken', () => {
196164
});
197165
await waitFor(() => {
198166
expect(mockInvalidateQueries).not.toHaveBeenCalled();
199-
expect(mockAddNotification).toHaveBeenCalledWith({
200-
message: errorMessage,
201-
type: NOTIFICATION_TYPE.ERROR,
202-
});
203167
});
204168
});
205169

@@ -215,10 +179,6 @@ describe('usePersonalAccessToken', () => {
215179
});
216180
await waitFor(() => {
217181
expect(mockInvalidateQueries).not.toHaveBeenCalled();
218-
expect(mockAddNotification).toHaveBeenCalledWith({
219-
message: DELETE_MESSAGE,
220-
type: NOTIFICATION_TYPE.ERROR,
221-
});
222182
});
223183
});
224184
});

web_ui/src/pages/user-management/personal-access-token-page/components/create-personal-access-token-dialog.component.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44
import { useEffect, useState } from 'react';
55

66
import { Button, ButtonGroup, Content, Dialog, DialogContainer, Divider, Heading, TextField, View } from '@geti/ui';
7+
import { AxiosError } from 'axios';
78
import dayjs from 'dayjs';
9+
import { get } from 'lodash-es';
810

911
import { usePersonalAccessToken } from '../../../../core/personal-access-tokens/hooks/use-personal-access-token.hook';
1012
import { CreatePersonalAccessTokenDialogProps } from '../../../../core/personal-access-tokens/personal-access-tokens.interface';
13+
import { NOTIFICATION_TYPE } from '../../../../notification/notification-toast/notification-type.enum';
14+
import { useNotification } from '../../../../notification/notification.component';
1115
import { WarningMessage } from '../../../../shared/components/warning-message/warning-message.component';
1216
import { getDateTimeInISOAndUTCOffsetFormat } from '../../../../shared/utils';
1317
import { CopyPersonalAccessToken } from './copy-personal-access-token.component';
@@ -23,6 +27,8 @@ enum Steps {
2327
// NOTE: values set on backend side
2428
const NAME_MAX_LENGTH = 100;
2529
const DESCRIPTION_MAX_LENGTH = 1000;
30+
const CREATE_MESSAGE = 'Personal Access Token was created successfully.';
31+
export const CREATE_ERROR = 'Personal Access Token was not created due to an error.';
2632

2733
interface PersonalAccessTokenData {
2834
name: string;
@@ -35,6 +41,7 @@ export const CreatePersonalAccessTokenDialog = ({
3541
userId,
3642
triggerState,
3743
}: CreatePersonalAccessTokenDialogProps) => {
44+
const { addNotification } = useNotification();
3845
const { createPersonalAccessTokenMutation } = usePersonalAccessToken();
3946

4047
const [currentStep, setCurrentStep] = useState(Steps.ExpirationDate);
@@ -62,7 +69,14 @@ export const CreatePersonalAccessTokenDialog = ({
6269
userId,
6370
},
6471
{
65-
onSuccess: () => setCurrentStep(Steps.Copy),
72+
onSuccess: () => {
73+
setCurrentStep(Steps.Copy);
74+
addNotification({ message: CREATE_MESSAGE, type: NOTIFICATION_TYPE.DEFAULT });
75+
},
76+
onError: (error: AxiosError) => {
77+
const message = get(error, 'message', CREATE_ERROR);
78+
addNotification({ message, type: NOTIFICATION_TYPE.ERROR });
79+
},
6680
}
6781
);
6882
};

web_ui/src/pages/user-management/personal-access-token-page/components/create-personal-access-token-dialog.test.tsx

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { useOverlayTriggerState } from '@react-stately/overlays';
55
import { screen, waitFor } from '@testing-library/react';
66
import { userEvent } from '@testing-library/user-event';
77

8+
import { NOTIFICATION_TYPE } from '../../../../notification/notification-toast/notification-type.enum';
89
import { providersRender as render } from '../../../../test-utils/required-providers-render';
9-
import { CreatePersonalAccessTokenDialog } from './create-personal-access-token-dialog.component';
10+
import { CREATE_ERROR, CreatePersonalAccessTokenDialog } from './create-personal-access-token-dialog.component';
1011

1112
const App = () => {
1213
const createPersonalAccessTokenDialogState = useOverlayTriggerState({});
@@ -32,6 +33,23 @@ jest.mock('@geti/core/src/users/hook/use-users.hook', () => ({
3233
})),
3334
}));
3435

36+
const mockMutate = jest.fn();
37+
jest.mock('../../../../core/personal-access-tokens/hooks/use-personal-access-token.hook', () => ({
38+
usePersonalAccessToken: () => ({
39+
createPersonalAccessTokenMutation: {
40+
mutate: mockMutate,
41+
isPending: false,
42+
data: undefined,
43+
},
44+
}),
45+
}));
46+
47+
const mockAddNotification = jest.fn();
48+
jest.mock('../../../../notification/notification.component', () => ({
49+
...jest.requireActual('../../../../notification/notification.component'),
50+
useNotification: () => ({ addNotification: mockAddNotification }),
51+
}));
52+
3553
describe('CreatePersonalAccessTokenDialog', () => {
3654
const renderApp = async () => {
3755
const result = render(<App />);
@@ -41,6 +59,10 @@ describe('CreatePersonalAccessTokenDialog', () => {
4159
return result;
4260
};
4361

62+
afterEach(() => {
63+
jest.clearAllMocks();
64+
});
65+
4466
it('Open and close modal', async () => {
4567
await renderApp();
4668

@@ -61,30 +83,75 @@ describe('CreatePersonalAccessTokenDialog', () => {
6183
});
6284

6385
it('renders Copy options', async () => {
64-
await renderApp();
86+
mockMutate.mockImplementation((_data, { onSuccess }) => {
87+
onSuccess({ token: 'fake-token' });
88+
});
6589

66-
expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument();
90+
await renderApp();
6791

6892
const nameTextField = screen.getByLabelText(/name/i);
6993

7094
await userEvent.type(nameTextField, 'some api key name');
71-
expect(nameTextField).toHaveValue('some api key name');
72-
7395
const selectDateButton = screen.getByLabelText('Calendar');
74-
7596
await userEvent.click(selectDateButton);
76-
7797
const firstDate = screen.getByRole('button', { name: /First available date$/i });
78-
7998
await userEvent.click(firstDate);
8099

81-
expect(screen.getByRole('button', { name: 'Create' })).toBeEnabled();
82-
83100
await userEvent.click(screen.getByRole('button', { name: 'Create' }));
84101

85102
await waitFor(() => {
86103
expect(screen.getByLabelText('copy-api-key')).toBeInTheDocument();
87104
expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument();
88105
});
89106
});
107+
108+
it('shows error notification when create fails', async () => {
109+
const errorMessage = 'error test';
110+
111+
mockMutate.mockImplementation((_data, { onError }) => {
112+
onError?.({ message: errorMessage });
113+
});
114+
115+
await renderApp();
116+
117+
const nameTextField = screen.getByLabelText(/name/i);
118+
await userEvent.type(nameTextField, 'some api key name');
119+
const selectDateButton = screen.getByLabelText('Calendar');
120+
await userEvent.click(selectDateButton);
121+
const firstDate = screen.getByRole('button', { name: /First available date$/i });
122+
await userEvent.click(firstDate);
123+
124+
await userEvent.click(screen.getByRole('button', { name: 'Create' }));
125+
126+
await waitFor(() => {
127+
expect(mockAddNotification).toHaveBeenCalledWith({
128+
message: errorMessage,
129+
type: NOTIFICATION_TYPE.ERROR,
130+
});
131+
});
132+
});
133+
134+
it('shows default error notification when create fails without message', async () => {
135+
mockMutate.mockImplementation((_data, { onError }) => {
136+
onError();
137+
});
138+
139+
await renderApp();
140+
141+
const nameTextField = screen.getByLabelText(/name/i);
142+
await userEvent.type(nameTextField, 'some api key name');
143+
const selectDateButton = screen.getByLabelText('Calendar');
144+
await userEvent.click(selectDateButton);
145+
const firstDate = screen.getByRole('button', { name: /First available date$/i });
146+
await userEvent.click(firstDate);
147+
148+
await userEvent.click(screen.getByRole('button', { name: 'Create' }));
149+
150+
await waitFor(() => {
151+
expect(mockAddNotification).toHaveBeenCalledWith({
152+
message: CREATE_ERROR,
153+
type: NOTIFICATION_TYPE.ERROR,
154+
});
155+
});
156+
});
90157
});

0 commit comments

Comments
 (0)