diff --git a/packages/dashboard-frontend/src/Layout/ErrorReporter/Issue/__tests__/__snapshots__/index.spec.tsx.snap b/packages/dashboard-frontend/src/Layout/ErrorReporter/Issue/__tests__/__snapshots__/index.spec.tsx.snap index 180d73a52..6d5e47ee3 100644 --- a/packages/dashboard-frontend/src/Layout/ErrorReporter/Issue/__tests__/__snapshots__/index.spec.tsx.snap +++ b/packages/dashboard-frontend/src/Layout/ErrorReporter/Issue/__tests__/__snapshots__/index.spec.tsx.snap @@ -6,7 +6,7 @@ exports[`Issue component should render an unknown error 1`] = ` >
`; +exports[`Issue component should render namespace provision error 1`] = ` +
++`; + exports[`Issue component should render the SSO error 1`] = `+ + Error +
++ 500 Internal Server Error ++diff --git a/packages/dashboard-frontend/src/services/bootstrap/__tests__/namespaceProvisionWarnings.spec.tsx b/packages/dashboard-frontend/src/services/bootstrap/__tests__/namespaceProvisionWarnings.spec.tsx index 8e9060877..7bd8f31ac 100644 --- a/packages/dashboard-frontend/src/services/bootstrap/__tests__/namespaceProvisionWarnings.spec.tsx +++ b/packages/dashboard-frontend/src/services/bootstrap/__tests__/namespaceProvisionWarnings.spec.tsx @@ -65,168 +65,4 @@ describe('Check namespace provision warnings', () => { }, ]); }); - - it('should register the advanced authorization warning for allowGroups', () => { - const store = new FakeStoreBuilder() - .withDwServerConfig({ - networking: { - auth: { - advancedAuthorization: { - allowGroups: ['test-group'], - }, - }, - }, - }) - .build(); - - checkNamespaceProvisionWarnings(store.getState); - - expect(warningsReporterService.hasWarning).toBeTruthy(); - expect(warningsReporterService.reportAllWarnings()).toEqual([ - { - key: 'advancedAuthorizationGroupsWarning', - title: - 'Advanced authorization is enabled. User might not be allowed. Please, contact the administrator.', - }, - ]); - }); - - it('should register the advanced authorization warning for denyGroups', () => { - const store = new FakeStoreBuilder() - .withDwServerConfig({ - networking: { - auth: { - advancedAuthorization: { - denyGroups: ['test-group'], - }, - }, - }, - }) - .build(); - - checkNamespaceProvisionWarnings(store.getState); - - expect(warningsReporterService.hasWarning).toBeTruthy(); - expect(warningsReporterService.reportAllWarnings()).toEqual([ - { - key: 'advancedAuthorizationGroupsWarning', - title: - 'Advanced authorization is enabled. User might not be allowed. Please, contact the administrator.', - }, - ]); - }); - - it('should register the advanced authorization warning for allowGroups', () => { - const store = new FakeStoreBuilder() - .withDwServerConfig({ - networking: { - auth: { - advancedAuthorization: { - allowGroups: ['test-group'], - }, - }, - }, - }) - .build(); - - checkNamespaceProvisionWarnings(store.getState); - - expect(warningsReporterService.hasWarning).toBeTruthy(); - expect(warningsReporterService.reportAllWarnings()).toEqual([ - { - key: 'advancedAuthorizationGroupsWarning', - title: - 'Advanced authorization is enabled. User might not be allowed. Please, contact the administrator.', - }, - ]); - }); - - it('should register the advanced authorization warning for allowUsers', () => { - const store = new FakeStoreBuilder() - .withDwServerConfig({ - networking: { - auth: { - advancedAuthorization: { - allowUsers: ['user0'], - }, - }, - }, - }) - .build(); - - checkNamespaceProvisionWarnings(store.getState); - - expect(warningsReporterService.hasWarning).toBeTruthy(); - expect(warningsReporterService.reportAllWarnings()).toEqual([ - { - key: 'advancedAuthorizationUsersWarning', - title: - 'Advanced authorization is enabled. User might not be allowed. Please, contact the administrator.', - }, - ]); - }); - - it('should register the advanced authorization warning for denyUsers', () => { - const store = new FakeStoreBuilder() - .withDwServerConfig({ - networking: { - auth: { - advancedAuthorization: { - denyUsers: ['test-user'], - }, - }, - }, - }) - .build(); - - checkNamespaceProvisionWarnings(store.getState); - - expect(warningsReporterService.hasWarning).toBeTruthy(); - expect(warningsReporterService.reportAllWarnings()).toEqual([ - { - key: 'advancedAuthorizationUsersWarning', - title: - 'Advanced authorization is enabled. User might not be allowed. Please, contact the administrator.', - }, - ]); - }); - - it('should register all possible warnings', () => { - const store = new FakeStoreBuilder() - .withDwServerConfig({ - defaultNamespace: { - autoProvision: false, - }, - networking: { - auth: { - advancedAuthorization: { - denyUsers: ['test-user'], - denyGroups: ['test-group'], - }, - }, - }, - }) - .build(); - - checkNamespaceProvisionWarnings(store.getState); - - expect(warningsReporterService.hasWarning).toBeTruthy(); - expect(warningsReporterService.reportAllWarnings()).toEqual([ - { - key: 'autoProvisionWarning', - title: - 'Automatic namespace provisioning is disabled. Namespace might not have been configured yet. Please, contact the administrator.', - }, - { - key: 'advancedAuthorizationGroupsWarning', - title: - 'Advanced authorization is enabled. User might not be allowed. Please, contact the administrator.', - }, - { - key: 'advancedAuthorizationUsersWarning', - title: - 'Advanced authorization is enabled. User might not be allowed. Please, contact the administrator.', - }, - ]); - }); }); diff --git a/packages/dashboard-frontend/src/services/bootstrap/namespaceProvisionWarnings.ts b/packages/dashboard-frontend/src/services/bootstrap/namespaceProvisionWarnings.ts index ebb997fbe..6c9658711 100644 --- a/packages/dashboard-frontend/src/services/bootstrap/namespaceProvisionWarnings.ts +++ b/packages/dashboard-frontend/src/services/bootstrap/namespaceProvisionWarnings.ts @@ -13,7 +13,7 @@ import { container } from '@/inversify.config'; import { WarningsReporterService } from '@/services/bootstrap/warningsReporter'; import { AppState } from '@/store'; -import { selectAdvancedAuthorization, selectAutoProvision } from '@/store/ServerConfig/selectors'; +import { selectAutoProvision } from '@/store/ServerConfig/selectors'; const warningsReporterService = container.get(WarningsReporterService); @@ -26,21 +26,4 @@ export function checkNamespaceProvisionWarnings(getState: () => AppState): void `Automatic namespace provisioning is disabled. Namespace might not have been configured yet. Please, contact the administrator.`, ); } - - const advancedAuthorization = selectAdvancedAuthorization(state); - if (advancedAuthorization === undefined || Object.keys(advancedAuthorization).length === 0) { - return; - } - if (advancedAuthorization.allowGroups || advancedAuthorization.denyGroups) { - warningsReporterService.registerWarning( - 'advancedAuthorizationGroupsWarning', - `Advanced authorization is enabled. User might not be allowed. Please, contact the administrator.`, - ); - } - if (advancedAuthorization.allowUsers || advancedAuthorization.denyUsers) { - warningsReporterService.registerWarning( - 'advancedAuthorizationUsersWarning', - `Advanced authorization is enabled. User might not be allowed. Please, contact the administrator.`, - ); - } } diff --git a/packages/dashboard-frontend/src/services/workspace-client/__tests__/helpers.spec.ts b/packages/dashboard-frontend/src/services/workspace-client/__tests__/helpers.spec.ts index 83f64158c..41afa6271 100644 --- a/packages/dashboard-frontend/src/services/workspace-client/__tests__/helpers.spec.ts +++ b/packages/dashboard-frontend/src/services/workspace-client/__tests__/helpers.spec.ts @@ -25,29 +25,63 @@ import { } from '@/services/workspace-client/helpers'; import { FakeStoreBuilder } from '@/store/__mocks__/storeBuilder'; +// mute console.error +console.error = jest.fn(); + describe('Workspace-client helpers', () => { describe('get an error message', () => { it('should return the default error message', () => { expect(getErrorMessage(undefined)).toEqual('Check the browser logs message.'); }); + it('should return the unknown error message', () => { expect(getErrorMessage({})).toEqual('Unexpected error type. Please report a bug.'); }); + it('should return the error message', () => { + const message = getErrorMessage({ + response: { + status: 500, + statusText: 'Internal Server Error', + headers: {}, + config: {}, + data: 'Some error message', + }, + request: { + responseURL: 'http://dummyurl.com', + }, + }); + expect(message).toEqual('Some error message'); + }); + + it('should return the error details', () => { expect( getErrorMessage({ response: { - status: 401, + status: 500, }, request: { responseURL: 'http://dummyurl.com', }, }), ).toEqual( - 'HTTP Error code 401. Endpoint which throws an error http://dummyurl.com. Check the browser logs message.', + 'HTTP Error code 500. Endpoint which throws an error http://dummyurl.com. Check the browser logs message.', ); }); + + it('should report the Unauthorized (or Forbidden) error', () => { + const message = getErrorMessage({ + response: { + status: 401, + request: { + responseURL: 'http://dummyurl.com', + }, + }, + }); + expect(message).toContain('User session has expired. You need to re-login to the Dashboard.'); + }); }); + describe('checks for HTML login page in response data', () => { it('should return false without HTML login page', () => { expect( @@ -62,6 +96,7 @@ describe('Workspace-client helpers', () => { }), ).toBeFalsy(); }); + it('should return true in the case with HTML login page', () => { expect( hasLoginPage({ @@ -76,6 +111,7 @@ describe('Workspace-client helpers', () => { ).toBeTruthy(); }); }); + describe('checks for HTTP 401 Unauthorized response status code', () => { it('should return false in the case with HTTP 400 Bad Request', () => { expect(isUnauthorized('...HTTP Status 400 ....')).toBeFalsy(); @@ -101,6 +137,7 @@ describe('Workspace-client helpers', () => { }), ).toBeFalsy(); }); + it('should return true in the case with HTTP 401 Unauthorized', () => { expect(isUnauthorized('...HTTP Status 401 ....')).toBeTruthy(); expect( @@ -126,6 +163,7 @@ describe('Workspace-client helpers', () => { ).toBeTruthy(); }); }); + describe('checks for HTTP 403 Forbidden response status code', () => { it('should return false in the case with HTTP 400 Bad Request', () => { expect(isForbidden('...HTTP Status 400 ....')).toBeFalsy(); @@ -151,6 +189,7 @@ describe('Workspace-client helpers', () => { }), ).toBeFalsy(); }); + it('should return true in the case with HTTP 403 Forbidden', () => { expect(isForbidden('...HTTP Status 403 ....')).toBeTruthy(); expect( @@ -176,6 +215,7 @@ describe('Workspace-client helpers', () => { ).toBeTruthy(); }); }); + describe('checks for HTTP 500 Internal Server Error response status code', () => { it('should return false in the case with HTTP 400 Bad Request', () => { expect(isInternalServerError('...HTTP Status 400 ....')).toBeFalsy(); @@ -201,6 +241,7 @@ describe('Workspace-client helpers', () => { }), ).toBeFalsy(); }); + it('should return true in the case with HTTP 500 Internal Server Error', () => { expect(isInternalServerError('...HTTP Status 500 ....')).toBeTruthy(); expect( @@ -479,6 +520,7 @@ describe('Workspace-client helpers', () => { ); }); }); + describe('from the custom registry', () => { it('should return an editor without changes', async () => { optionalFilesContent[CHE_EDITOR_YAML_PATH] = dump({ diff --git a/packages/dashboard-frontend/src/services/workspace-client/helpers.ts b/packages/dashboard-frontend/src/services/workspace-client/helpers.ts index ff54d54a1..f2e7b6946 100644 --- a/packages/dashboard-frontend/src/services/workspace-client/helpers.ts +++ b/packages/dashboard-frontend/src/services/workspace-client/helpers.ts @@ -11,6 +11,7 @@ */ import common from '@eclipse-che/common'; +import { AxiosResponse } from 'axios'; import { dump, load } from 'js-yaml'; import { ThunkDispatch } from 'redux-thunk'; @@ -35,7 +36,14 @@ export function getErrorMessage(error: unknown): string { return 'Unexpected error type. Please report a bug.'; } - errorMessage = `HTTP Error code ${code}. Endpoint which throws an error ${endpoint}. ${errorMessage}`; + const errorDetails = `HTTP Error code ${code}. Endpoint which throws an error ${endpoint}.`; + console.error(errorDetails); + + if (common.helpers.errors.includesAxiosResponse(error) && error.response.data) { + errorMessage = error.response.data; + } else { + errorMessage = `${errorDetails} ${errorMessage}`; + } } if (isUnauthorized(error) || isForbidden(error)) { @@ -98,8 +106,14 @@ function hasStatus(error: unknown, _status: number): boolean { return true; } } else if (typeof error === 'object' && error !== null) { - const { status, statusCode } = error as { [propName: string]: string | number }; - if (statusCode == _status || status == _status) { + const { status, statusCode, response } = error as { + [propName: string]: string | number | unknown; + }; + if ( + statusCode == _status || + status == _status || + (response && (response as AxiosResponse).status == _status) + ) { return true; } else { try {
`; +exports[`Issue component should render the sessionExpired error 1`] = ` +
++`; + exports[`Issue component should render the workspaceInactive error 1`] = `+ + Error +
++ 401 Unauthorized +++ +
+
-
{ + it('should render the sessionExpired error', () => { + const issue = { + type: 'sessionExpired', + error: new Error('401 Unauthorized'), + } as Issue; + const component =
; + + expect(renderer.create(component).toJSON()).toMatchSnapshot(); + }); + it('should render the SSO error', () => { const issue = { type: 'sso', @@ -143,6 +153,16 @@ describe('Issue component', () => { expect(renderer.create(component).toJSON()).toMatchSnapshot(); }); + it('should render namespace provision error', () => { + const issue = { + type: 'namespaceProvisioningError', + error: new Error('500 Internal Server Error'), + } as Issue; + const component = ; + + expect(renderer.create(component).toJSON()).toMatchSnapshot(); + }); + it('should render an unknown error', () => { const issue = { type: 'unknown', diff --git a/packages/dashboard-frontend/src/Layout/ErrorReporter/Issue/index.tsx b/packages/dashboard-frontend/src/Layout/ErrorReporter/Issue/index.tsx index 0b5324d4b..0bffbed0e 100644 --- a/packages/dashboard-frontend/src/Layout/ErrorReporter/Issue/index.tsx +++ b/packages/dashboard-frontend/src/Layout/ErrorReporter/Issue/index.tsx @@ -41,6 +41,8 @@ export class IssueComponent extends React.PureComponent { return this.renderWorkspaceStoppedWithError(issue.error, issue.data); case 'workspaceStopped': return this.renderWorkspaceStopped(issue.data); + case 'namespaceProvisioningError': + return this.renderNamespaceProvisionError(issue.error); default: return this.renderUnknownError(issue.error); } @@ -254,6 +256,24 @@ export class IssueComponent extends React.PureComponent { ); } + private renderNamespaceProvisionError(error: Error): React.ReactNode { + const errorTextbox = !error ? undefined : ( + + {error.message} + + ); + + return ( ++ + ); + } + private renderUnknownError(error: Error): React.ReactNode { const errorTextbox = !error ? undefined : (+ + {errorTextbox} ++ Error + diff --git a/packages/dashboard-frontend/src/Layout/__tests__/__snapshots__/index.spec.tsx.snap b/packages/dashboard-frontend/src/Layout/__tests__/__snapshots__/index.spec.tsx.snap index 6e3e0a718..13f040c79 100644 --- a/packages/dashboard-frontend/src/Layout/__tests__/__snapshots__/index.spec.tsx.snap +++ b/packages/dashboard-frontend/src/Layout/__tests__/__snapshots__/index.spec.tsx.snap @@ -118,26 +118,6 @@ exports[`Layout component snapshot of the namespace provisioning error page 1`] > Namespace provisioning error - Please try - - Shift - - + - - Refresh - -