diff --git a/packages/common/src/helpers/__tests__/errors.spec.ts b/packages/common/src/helpers/__tests__/errors.spec.ts index 36f8f3c17..50a999e05 100644 --- a/packages/common/src/helpers/__tests__/errors.spec.ts +++ b/packages/common/src/helpers/__tests__/errors.spec.ts @@ -14,7 +14,12 @@ import axios, { AxiosError, AxiosResponse } from 'axios'; import { HttpError } from '@kubernetes/client-node'; import AxiosMockAdapter from 'axios-mock-adapter'; import * as http from 'http'; -import { getMessage, isAxiosError, isError } from '../errors'; +import { + getMessage, + isAxiosError, + isError, + isTokenNotFoundError, +} from '../errors'; const mockAxios = new AxiosMockAdapter(axios); @@ -41,6 +46,30 @@ describe('Errors helper', () => { error.isAxiosError = true; expect(isAxiosError(error)).toEqual(true); }); + + it('should check if token not found error', () => { + const expectedError = { + name: '', + config: {}, + request: {}, + response: { + data: { + message: + 'OAuth token for user xxxxx-xxxxx-xxxxx-xxxxx was not found', + }, + status: 401, + statusText: '', + headers: {}, + config: {}, + request: {}, + }, + message: '', + }; + + const unexpectedError = new Error('Unsupported OAuth provider xxxxx'); + expect(isTokenNotFoundError(expectedError)).toEqual(true); + expect(isTokenNotFoundError(unexpectedError)).toEqual(false); + }); }); it('should return default message', () => { diff --git a/packages/common/src/helpers/errors.ts b/packages/common/src/helpers/errors.ts index 77608b10e..3392ecd6b 100644 --- a/packages/common/src/helpers/errors.ts +++ b/packages/common/src/helpers/errors.ts @@ -123,3 +123,13 @@ export function isKubeClientError(error: unknown): error is HttpError { 'body' in (error as HttpError) ); } +export function isTokenNotFoundError( + error: unknown, +): error is ObjectWithAxiosResponse { + return ( + includesAxiosResponse(error) && + /^OAuth token for user .* was not found$/.test( + (error as ObjectWithAxiosResponse).response.data.message, + ) + ); +} diff --git a/packages/dashboard-frontend/src/store/GitOauthConfig/__tests__/index.spec.ts b/packages/dashboard-frontend/src/store/GitOauthConfig/__tests__/index.spec.ts index b5b58e907..b2698a4c7 100644 --- a/packages/dashboard-frontend/src/store/GitOauthConfig/__tests__/index.spec.ts +++ b/packages/dashboard-frontend/src/store/GitOauthConfig/__tests__/index.spec.ts @@ -46,6 +46,30 @@ const mockGetOAuthToken = jest.fn().mockImplementation(provider => { } return new Promise((_resolve, reject) => reject(new Error('Token not found'))); }); +const mockDeleteOAuthToken = jest.fn().mockImplementation(provider => { + if (provider === 'github') { + return new Promise((_resolve, reject) => { + const expectedError = { + name: '', + config: {}, + request: {}, + response: { + data: { + message: 'OAuth token for user xxxxx-xxxxx-xxxxx-xxxxx was not found', + }, + status: 401, + statusText: '', + headers: {}, + config: {}, + request: {}, + }, + message: '', + }; + reject(expectedError); + }); + } + return new Promise(resolve => resolve(undefined)); +}); const mockFetchTokens = jest.fn().mockResolvedValue([ { tokenName: 'github-personal-access-token', @@ -68,6 +92,7 @@ jest.mock('@/services/backend-client/oAuthApi', () => { return { getOAuthProviders: (...args: unknown[]) => mockGetOAuthProviders(...args), getOAuthToken: (...args: unknown[]) => mockGetOAuthToken(...args), + deleteOAuthToken: (...args: unknown[]) => mockDeleteOAuthToken(...args), getDevWorkspacePreferences: (...args: unknown[]) => mockGetDevWorkspacePreferences(...args), }; }); @@ -113,4 +138,30 @@ describe('GitOauthConfig store, actions', () => { expect(actions).toContainEqual(expectedAction); }); + + it('should request DeleteGitOauthToken', async () => { + await store.dispatch(TestStore.actionCreators.revokeOauth('gitlab')); + + const actions = store.getActions(); + + const expectedAction: TestStore.KnownAction = { + provider: 'gitlab', + type: TestStore.Type.DELETE_GIT_OAUTH_TOKEN, + }; + + expect(actions).toContainEqual(expectedAction); + }); + + it('should request DeleteGitOauthToken in the case with token not found error', async () => { + await store.dispatch(TestStore.actionCreators.revokeOauth('github')); // this oauthProvider throws the token not found error + + const actions = store.getActions(); + + const expectedAction: TestStore.KnownAction = { + provider: 'github', + type: TestStore.Type.DELETE_GIT_OAUTH_TOKEN, + }; + + expect(actions).toContainEqual(expectedAction); + }); }); diff --git a/packages/dashboard-frontend/src/store/GitOauthConfig/index.ts b/packages/dashboard-frontend/src/store/GitOauthConfig/index.ts index bbee46c47..e7d60c883 100644 --- a/packages/dashboard-frontend/src/store/GitOauthConfig/index.ts +++ b/packages/dashboard-frontend/src/store/GitOauthConfig/index.ts @@ -219,18 +219,20 @@ export const actionCreators: ActionCreators = { try { await deleteOAuthToken(oauthProvider); - dispatch({ - type: Type.DELETE_GIT_OAUTH_TOKEN, - provider: oauthProvider, - }); } catch (e) { - const errorMessage = common.helpers.errors.getMessage(e); - dispatch({ - type: Type.RECEIVE_GIT_OAUTH_ERROR, - error: errorMessage, - }); - throw e; + if (!common.helpers.errors.isTokenNotFoundError(e)) { + const errorMessage = common.helpers.errors.getMessage(e); + dispatch({ + type: Type.RECEIVE_GIT_OAUTH_ERROR, + error: errorMessage, + }); + throw e; + } } + dispatch({ + type: Type.DELETE_GIT_OAUTH_TOKEN, + provider: oauthProvider, + }); }, deleteSkipOauth: