diff --git a/packages/common/package.json b/packages/common/package.json
index 4b5bda377..98a80ca78 100644
--- a/packages/common/package.json
+++ b/packages/common/package.json
@@ -28,7 +28,7 @@
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-notice": "^0.9.10",
"eslint-plugin-prettier": "^5.0.0",
- "jest": "^29.6.2",
+ "jest": "^29.7.0",
"prettier": "^3.0.1",
"rimraf": "^5.0.1",
"ts-jest": "^29.1.1",
diff --git a/packages/dashboard-backend/package.json b/packages/dashboard-backend/package.json
index 09f6bc84a..d953bef5a 100644
--- a/packages/dashboard-backend/package.json
+++ b/packages/dashboard-backend/package.json
@@ -65,7 +65,7 @@
"eslint-plugin-prettier": "^5.0.0",
"eslint-webpack-plugin": "^4.0.1",
"file-loader": "^6.2.0",
- "jest": "^29.6.2",
+ "jest": "^29.7.0",
"json-schema": "^0.4.0",
"nodemon": "^3.0.1",
"prettier": "^3.0.2",
diff --git a/packages/dashboard-frontend/src/Layout/ErrorBoundary/__tests__/index.spec.tsx b/packages/dashboard-frontend/src/Layout/ErrorBoundary/__tests__/index.spec.tsx
index 3adad8e11..58caff090 100644
--- a/packages/dashboard-frontend/src/Layout/ErrorBoundary/__tests__/index.spec.tsx
+++ b/packages/dashboard-frontend/src/Layout/ErrorBoundary/__tests__/index.spec.tsx
@@ -37,8 +37,9 @@ export class NoResourceComponent extends React.Component {
// mute the outputs
console.error = jest.fn();
+const mockTestBackends = jest.fn();
function wrapComponent(componentToWrap: React.ReactNode) {
- return {componentToWrap};
+ return {componentToWrap};
}
describe('Error boundary', () => {
@@ -66,6 +67,7 @@ describe('Error boundary', () => {
const showDetailsAction = screen.getByRole('button', { name: 'View stack' });
userEvent.click(showDetailsAction);
+ expect(mockTestBackends).not.toHaveBeenCalled();
expect(screen.queryByText('in BadComponent', { exact: false })).toBeTruthy();
expect(screen.queryByText('in ErrorBoundary', { exact: false })).toBeTruthy();
@@ -98,6 +100,7 @@ describe('Error boundary', () => {
const errorBoundary = wrapComponent();
render(errorBoundary);
+ expect(mockTestBackends).toHaveBeenCalledWith('Loading chunk 23 failed.');
expect(
screen.queryByText('The application has been likely updated on the server.', {
exact: false,
@@ -111,6 +114,7 @@ describe('Error boundary', () => {
const errorBoundary = wrapComponent();
render(errorBoundary);
+ expect(mockTestBackends).toHaveBeenCalledWith('Loading chunk 23 failed.');
expect(
screen.queryByText('Refreshing a page to get newer resources in', { exact: false }),
).toBeTruthy();
@@ -127,6 +131,7 @@ describe('Error boundary', () => {
const stopCountdownAction = screen.getByRole('button', { name: 'Stop countdown' });
userEvent.click(stopCountdownAction);
+ expect(mockTestBackends).toHaveBeenCalledWith('Loading chunk 23 failed.');
expect(
screen.queryByText('Refreshing a page to get newer resources in', { exact: false }),
).toBeFalsy();
@@ -148,6 +153,7 @@ describe('Error boundary', () => {
jest.advanceTimersByTime(35000);
+ expect(mockTestBackends).toHaveBeenCalledWith('Loading chunk 23 failed.');
expect(window.location.reload).toHaveBeenCalled();
expect(window.location.reload).toHaveBeenCalledTimes(1);
});
@@ -161,6 +167,7 @@ describe('Error boundary', () => {
userEvent.click(reloadNowAction);
userEvent.click(reloadNowAction);
+ expect(mockTestBackends).toHaveBeenCalledWith('Loading chunk 23 failed.');
expect(window.location.reload).toHaveBeenCalled();
expect(window.location.reload).toHaveBeenCalledTimes(3);
});
@@ -185,6 +192,7 @@ describe('Error boundary', () => {
window.dispatchEvent(new Event('beforeunload'));
render(errorBoundary);
+ expect(mockTestBackends).toHaveBeenCalledWith('Loading chunk 23 failed.');
expect(
screen.queryByText(
'Contact an administrator if refreshing continues after the next load.',
@@ -225,6 +233,7 @@ describe('Error boundary', () => {
window.dispatchEvent(new Event('beforeunload'));
render(goodComponent);
+ expect(mockTestBackends).toHaveBeenCalledWith('Loading chunk 23 failed.');
expect(sessionStorage.getItem(STORAGE_KEY_RELOAD_NUMBER)).toBeNull();
});
});
diff --git a/packages/dashboard-frontend/src/Layout/ErrorBoundary/index.tsx b/packages/dashboard-frontend/src/Layout/ErrorBoundary/index.tsx
index 527ab24a5..faeccf21c 100644
--- a/packages/dashboard-frontend/src/Layout/ErrorBoundary/index.tsx
+++ b/packages/dashboard-frontend/src/Layout/ErrorBoundary/index.tsx
@@ -28,7 +28,9 @@ export const STORAGE_KEY_RELOAD_NUMBER = 'UD:ErrorBoundary:reloaded';
const RELOAD_TIMEOUT_SEC = 30;
const RELOADS_FOR_EXTENDED_MESSAGE = 2;
-type Props = PropsWithChildren;
+type Props = PropsWithChildren & {
+ testBackends: (error?: string) => void;
+};
type State = {
hasError: boolean;
error?: Error;
@@ -43,7 +45,6 @@ type State = {
export class ErrorBoundary extends React.PureComponent {
private readonly toDispose = new DisposableCollection();
- private notFoundCallback: (error?: Error) => void;
constructor(props: Props) {
super(props);
@@ -75,9 +76,7 @@ export class ErrorBoundary extends React.PureComponent {
});
if (this.testResourceNotFound(error)) {
- if (this.notFoundCallback) {
- this.notFoundCallback(error);
- }
+ this.props.testBackends(error.message);
this.setState({
shouldReload: true,
});
@@ -99,13 +98,6 @@ export class ErrorBoundary extends React.PureComponent {
this.toDispose.dispose();
}
- /**
- * This method is used from parent component by reference.
- */
- public setCallback(notFoundCallback: (error?: Error) => void): void {
- this.notFoundCallback = notFoundCallback;
- }
-
private testResourceNotFound(error: Error): boolean {
return /loading chunk [\d]+ failed/i.test(error.message);
}
diff --git a/packages/dashboard-frontend/src/Layout/index.tsx b/packages/dashboard-frontend/src/Layout/index.tsx
index 1a8f34b5c..17d6e9e4b 100644
--- a/packages/dashboard-frontend/src/Layout/index.tsx
+++ b/packages/dashboard-frontend/src/Layout/index.tsx
@@ -31,6 +31,7 @@ import { ToggleBarsContext } from '../contexts/ToggleBars';
import { signOut } from '../services/helpers/login';
import { selectDashboardLogo } from '../store/ServerConfig/selectors';
import * as SanityCheckStore from '../store/SanityCheck';
+import { selectSanityCheckError } from '../store/SanityCheck/selectors';
const IS_MANAGED_SIDEBAR = false;
@@ -46,13 +47,10 @@ type State = {
export class Layout extends React.PureComponent {
@lazyInject(IssuesReporterService)
private readonly issuesReporterService: IssuesReporterService;
- private readonly errorBoundaryRef: React.RefObject;
constructor(props: Props) {
super(props);
- this.errorBoundaryRef = React.createRef();
-
this.state = {
isHeaderVisible: true,
isSidebarVisible: true,
@@ -89,11 +87,13 @@ export class Layout extends React.PureComponent {
if (matchFactoryLoaderPath !== null || matchIdeLoaderPath !== null) {
this.hideAllBars();
}
- this.errorBoundaryRef.current?.setCallback((error?: Error) => {
- if (error?.message) {
- console.error(error.message);
+ }
+ private testBackends(error?: string): void {
+ this.props.testBackends().catch(() => {
+ if (error) {
+ console.error(error);
}
- this.props.testBackends();
+ console.error('Error testing backends:', this.props.sanityCheckError);
});
}
@@ -145,7 +145,7 @@ export class Layout extends React.PureComponent {
}
isManagedSidebar={IS_MANAGED_SIDEBAR}
>
-
+ this.testBackends(error)}>
{this.props.children}
@@ -159,6 +159,7 @@ export class Layout extends React.PureComponent {
const mapStateToProps = (state: AppState) => ({
branding: selectBranding(state),
dashboardLogo: selectDashboardLogo(state),
+ sanityCheckError: selectSanityCheckError(state),
});
const connector = connect(mapStateToProps, SanityCheckStore.actionCreators);
diff --git a/packages/dashboard-frontend/src/services/helpers/types.ts b/packages/dashboard-frontend/src/services/helpers/types.ts
index 80a2b4af6..8c4cf7511 100644
--- a/packages/dashboard-frontend/src/services/helpers/types.ts
+++ b/packages/dashboard-frontend/src/services/helpers/types.ts
@@ -13,6 +13,7 @@
import { AlertVariant } from '@patternfly/react-core';
import * as React from 'react';
import devfileApi from '../devfileApi';
+import * as cheApi from '@eclipse-che/api';
export type ActionCallback = {
title: string;
@@ -35,7 +36,7 @@ export interface FactoryResolver {
devfile: devfileApi.Devfile | che.WorkspaceDevfile;
location?: string;
scm_info?: FactoryResolverScmInfo;
- links: api.che.core.rest.Link[];
+ links: cheApi.che.core.rest.Link[];
}
export type FactoryResolverScmInfo = {
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 a3cf0a4c3..527384f0a 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
@@ -13,6 +13,7 @@
import {
getCustomEditor,
hasLoginPage,
+ getErrorMessage,
isForbidden,
isInternalServerError,
isUnauthorized,
@@ -24,6 +25,28 @@ import devfileApi from '../../devfileApi';
import { FakeStoreBuilder } from '../../../store/__mocks__/storeBuilder';
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 unknown an error message', () => {
+ expect(
+ getErrorMessage({
+ response: {
+ status: 401,
+ },
+ request: {
+ responseURL: 'http://dummyurl.com',
+ },
+ }),
+ ).toEqual(
+ 'HTTP Error code 401. Endpoint which throws an error http://dummyurl.com. Check the browser logs message.',
+ );
+ });
+ });
describe('checks for HTML login page in response data', () => {
it('should return false without HTML login page', () => {
expect(
diff --git a/packages/dashboard-frontend/src/services/workspace-client/helpers.ts b/packages/dashboard-frontend/src/services/workspace-client/helpers.ts
index d1befeef4..c64cc1df6 100644
--- a/packages/dashboard-frontend/src/services/workspace-client/helpers.ts
+++ b/packages/dashboard-frontend/src/services/workspace-client/helpers.ts
@@ -31,7 +31,11 @@ export function getErrorMessage(error: unknown): string {
const code = response?.status ? response?.status : response?.request?.status;
const endpoint = request?.responseURL ? request?.responseURL : response?.request?.responseURL;
- errorMessage = `HTTP Error code ${code}. Endpoint which throw an error ${endpoint}. ${errorMessage}`;
+ if (!code || !endpoint) {
+ return 'Unexpected error type. Please report a bug.';
+ }
+
+ errorMessage = `HTTP Error code ${code}. Endpoint which throws an error ${endpoint}. ${errorMessage}`;
}
if (isUnauthorized(error) || isForbidden(error)) {
diff --git a/packages/dashboard-frontend/src/store/DevfileRegistries/__tests__/index.spec.ts b/packages/dashboard-frontend/src/store/DevfileRegistries/__tests__/index.spec.ts
index 58fa8b3f8..ce2a9380c 100644
--- a/packages/dashboard-frontend/src/store/DevfileRegistries/__tests__/index.spec.ts
+++ b/packages/dashboard-frontend/src/store/DevfileRegistries/__tests__/index.spec.ts
@@ -58,6 +58,14 @@ describe('Devfile registries', () => {
const actions = store.getActions();
const expectedActions: devfileRegistriesStore.KnownAction[] = [
+ {
+ type: devfileRegistriesStore.Type.REQUEST_REGISTRY_METADATA,
+ check: AUTHORIZED,
+ },
+ {
+ type: devfileRegistriesStore.Type.REQUEST_REGISTRY_METADATA,
+ check: AUTHORIZED,
+ },
{
type: devfileRegistriesStore.Type.REQUEST_REGISTRY_METADATA,
check: AUTHORIZED,
@@ -116,6 +124,14 @@ describe('Devfile registries', () => {
const actions = store.getActions();
const expectedActions: devfileRegistriesStore.KnownAction[] = [
+ {
+ type: devfileRegistriesStore.Type.REQUEST_REGISTRY_METADATA,
+ check: AUTHORIZED,
+ },
+ {
+ type: devfileRegistriesStore.Type.REQUEST_REGISTRY_METADATA,
+ check: AUTHORIZED,
+ },
{
type: devfileRegistriesStore.Type.REQUEST_REGISTRY_METADATA,
check: AUTHORIZED,
diff --git a/packages/dashboard-frontend/src/store/DevfileRegistries/index.ts b/packages/dashboard-frontend/src/store/DevfileRegistries/index.ts
index 1363e8628..dc6128eca 100644
--- a/packages/dashboard-frontend/src/store/DevfileRegistries/index.ts
+++ b/packages/dashboard-frontend/src/store/DevfileRegistries/index.ts
@@ -19,6 +19,7 @@ import fetchAndUpdateDevfileSchema from './fetchAndUpdateDevfileSchema';
import devfileApi from '../../services/devfileApi';
import { fetchResources, loadResourcesContent } from '../../services/registry/resources';
import { AUTHORIZED, SanityCheckAction } from '../sanityCheckMiddleware';
+import { selectAsyncIsAuthorized, selectSanityCheckError } from '../SanityCheck/selectors';
export const DEFAULT_REGISTRY = '/dashboard/devfile-registry/';
@@ -170,12 +171,15 @@ export const actionCreators: ActionCreators = {
*/
requestRegistriesMetadata:
(registryUrls: string, isExternal: boolean): AppThunk> =>
- async (dispatch): Promise => {
- await dispatch({ type: Type.REQUEST_REGISTRY_METADATA, check: AUTHORIZED });
-
+ async (dispatch, getState): Promise => {
const registries: string[] = registryUrls.split(' ');
const promises = registries.map(async url => {
try {
+ await dispatch({ type: Type.REQUEST_REGISTRY_METADATA, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const metadata: che.DevfileMetaData[] = await fetchRegistryMetadata(url, isExternal);
if (!Array.isArray(metadata) || metadata.length === 0) {
return;
@@ -205,9 +209,13 @@ export const actionCreators: ActionCreators = {
requestDevfile:
(url: string): AppThunk> =>
- async (dispatch): Promise => {
- await dispatch({ type: Type.REQUEST_DEVFILE, check: AUTHORIZED });
+ async (dispatch, getState): Promise => {
try {
+ await dispatch({ type: Type.REQUEST_DEVFILE, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const devfile = await fetchDevfile(url);
dispatch({ type: Type.RECEIVE_DEVFILE, devfile, url });
return devfile;
@@ -218,10 +226,13 @@ export const actionCreators: ActionCreators = {
requestResources:
(resourcesUrl: string): AppThunk> =>
- async (dispatch): Promise => {
- await dispatch({ type: Type.REQUEST_RESOURCES, check: AUTHORIZED });
-
+ async (dispatch, getState): Promise => {
try {
+ await dispatch({ type: Type.REQUEST_RESOURCES, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const resourcesContent = await fetchResources(resourcesUrl);
const resources = loadResourcesContent(resourcesContent);
@@ -259,9 +270,13 @@ export const actionCreators: ActionCreators = {
requestJsonSchema:
(): AppThunk =>
- async (dispatch): Promise => {
- await dispatch({ type: Type.REQUEST_SCHEMA, check: AUTHORIZED });
+ async (dispatch, getState): Promise => {
try {
+ await dispatch({ type: Type.REQUEST_SCHEMA, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const schemav200 = await fetchAndUpdateDevfileSchema('2.0.0');
const schemav210 = await fetchAndUpdateDevfileSchema('2.1.0');
const schemav220 = await fetchAndUpdateDevfileSchema('2.2.0');
diff --git a/packages/dashboard-frontend/src/store/DockerConfig/index.ts b/packages/dashboard-frontend/src/store/DockerConfig/index.ts
index e708eb668..c1f4ff8ec 100644
--- a/packages/dashboard-frontend/src/store/DockerConfig/index.ts
+++ b/packages/dashboard-frontend/src/store/DockerConfig/index.ts
@@ -59,19 +59,13 @@ export const actionCreators: ActionCreators = {
requestCredentials:
(): AppThunk> =>
async (dispatch, getState): Promise => {
- dispatch({ type: Type.REQUEST_DEVWORKSPACE_CREDENTIALS, check: AUTHORIZED });
- const state = getState();
- if (!(await selectAsyncIsAuthorized(getState()))) {
- const error = selectSanityCheckError(getState());
- dispatch({
- type: Type.RECEIVE_DEVWORKSPACE_CREDENTIALS_ERROR,
- error,
- });
- throw new Error(error);
- }
-
- const namespace = selectDefaultNamespace(state).name;
+ const namespace = selectDefaultNamespace(getState()).name;
try {
+ await dispatch({ type: Type.REQUEST_DEVWORKSPACE_CREDENTIALS, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const { registries, resourceVersion } = await getDockerConfig(namespace);
dispatch({
type: Type.SET_DEVWORKSPACE_CREDENTIALS,
@@ -91,19 +85,14 @@ export const actionCreators: ActionCreators = {
updateCredentials:
(registries: RegistryEntry[]): AppThunk> =>
async (dispatch, getState): Promise => {
- dispatch({ type: Type.REQUEST_DEVWORKSPACE_CREDENTIALS, check: AUTHORIZED });
const state = getState();
- if (!(await selectAsyncIsAuthorized(getState()))) {
- const error = selectSanityCheckError(getState());
- dispatch({
- type: Type.RECEIVE_DEVWORKSPACE_CREDENTIALS_ERROR,
- error,
- });
- throw new Error(error);
- }
-
const namespace = selectDefaultNamespace(state).name;
try {
+ await dispatch({ type: Type.REQUEST_DEVWORKSPACE_CREDENTIALS, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const { resourceVersion } = await putDockerConfig(
namespace,
registries,
diff --git a/packages/dashboard-frontend/src/store/FactoryResolver/index.ts b/packages/dashboard-frontend/src/store/FactoryResolver/index.ts
index 87b86a3e5..aa52b9eeb 100644
--- a/packages/dashboard-frontend/src/store/FactoryResolver/index.ts
+++ b/packages/dashboard-frontend/src/store/FactoryResolver/index.ts
@@ -30,6 +30,7 @@ import { CHE_EDITOR_YAML_PATH } from '../../services/workspace-client/helpers';
import { FactoryParams } from '../../services/helpers/factoryFlow/buildFactoryParams';
import { getFactoryResolver } from '../../services/backend-client/factoryApi';
import { selectAsyncIsAuthorized, selectSanityCheckError } from '../SanityCheck/selectors';
+import * as cheApi from '@eclipse-che/api';
export type OAuthResponse = {
attributes: {
@@ -88,7 +89,7 @@ export type ActionCreators = {
};
export async function grabLink(
- links: api.che.core.rest.Link,
+ links: cheApi.che.core.rest.Link[],
filename: string,
): Promise {
// handle servers not yet providing links
@@ -96,8 +97,8 @@ export async function grabLink(
return undefined;
}
// grab the one matching
- const foundLink = links.find(link => link.href.includes(`file=${filename}`));
- if (!foundLink) {
+ const foundLink = links.find(link => link.href?.includes(`file=${filename}`));
+ if (!foundLink || !foundLink.href) {
return undefined;
}
@@ -130,16 +131,6 @@ export const actionCreators: ActionCreators = {
factoryParams: Partial = {},
): AppThunk> =>
async (dispatch, getState): Promise => {
- dispatch({ type: 'REQUEST_FACTORY_RESOLVER', check: AUTHORIZED });
- if (!(await selectAsyncIsAuthorized(getState()))) {
- const error = selectSanityCheckError(getState());
- dispatch({
- type: 'RECEIVE_FACTORY_RESOLVER_ERROR',
- error,
- });
- throw new Error(error);
- }
-
const state = getState();
const namespace = selectDefaultNamespace(state).name;
const optionalFilesContent = {};
@@ -167,6 +158,11 @@ export const actionCreators: ActionCreators = {
};
try {
+ await dispatch({ type: 'REQUEST_FACTORY_RESOLVER', check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
let data: FactoryResolver;
if (isDevfileRegistryLocation(location)) {
data = await getYamlResolver(namespace, location);
diff --git a/packages/dashboard-frontend/src/store/GitConfig/index.ts b/packages/dashboard-frontend/src/store/GitConfig/index.ts
index 2181cba47..479a469e6 100644
--- a/packages/dashboard-frontend/src/store/GitConfig/index.ts
+++ b/packages/dashboard-frontend/src/store/GitConfig/index.ts
@@ -29,19 +29,14 @@ export const actionCreators: ActionCreators = {
requestGitConfig:
(): AppThunk> =>
async (dispatch, getState): Promise => {
- dispatch({ type: Type.REQUEST_GITCONFIG, check: AUTHORIZED });
- if (!(await selectAsyncIsAuthorized(getState()))) {
- const error = selectSanityCheckError(getState());
- dispatch({
- type: Type.RECEIVE_GITCONFIG_ERROR,
- error,
- });
- throw new Error(error);
- }
-
const state = getState();
const namespace = selectDefaultNamespace(state).name;
try {
+ await dispatch({ type: Type.REQUEST_GITCONFIG, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const config = await fetchGitConfig(namespace);
dispatch({
type: Type.RECEIVE_GITCONFIG,
@@ -68,16 +63,6 @@ export const actionCreators: ActionCreators = {
updateGitConfig:
(changedGitConfig: GitConfigUser): AppThunk> =>
async (dispatch, getState): Promise => {
- dispatch({ type: Type.REQUEST_GITCONFIG, check: AUTHORIZED });
- if (!(await selectAsyncIsAuthorized(getState()))) {
- const error = selectSanityCheckError(getState());
- dispatch({
- type: Type.RECEIVE_GITCONFIG_ERROR,
- error,
- });
- throw new Error(error);
- }
-
const state = getState();
const namespace = selectDefaultNamespace(state).name;
const { gitConfig } = state;
@@ -87,6 +72,11 @@ export const actionCreators: ActionCreators = {
},
} as api.IGitConfig);
try {
+ await dispatch({ type: Type.REQUEST_GITCONFIG, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const updated = await patchGitConfig(namespace, gitconfig);
dispatch({
type: Type.RECEIVE_GITCONFIG,
diff --git a/packages/dashboard-frontend/src/store/GitOauthConfig/types.ts b/packages/dashboard-frontend/src/store/GitOauthConfig/types.ts
index 5a1bef2b1..c470f1961 100644
--- a/packages/dashboard-frontend/src/store/GitOauthConfig/types.ts
+++ b/packages/dashboard-frontend/src/store/GitOauthConfig/types.ts
@@ -11,9 +11,10 @@
*/
import { api as commonApi } from '@eclipse-che/common';
+import * as cheApi from '@eclipse-che/api';
export interface IGitOauth {
name: commonApi.GitOauthProvider;
endpointUrl: string;
- links?: api.che.core.rest.Link[];
+ links?: cheApi.che.core.rest.Link[];
}
diff --git a/packages/dashboard-frontend/src/store/InfrastructureNamespaces/index.ts b/packages/dashboard-frontend/src/store/InfrastructureNamespaces/index.ts
index 66b3fcb7b..94a6a2e5c 100644
--- a/packages/dashboard-frontend/src/store/InfrastructureNamespaces/index.ts
+++ b/packages/dashboard-frontend/src/store/InfrastructureNamespaces/index.ts
@@ -16,6 +16,7 @@ import { AppThunk } from '..';
import { createObject } from '../helpers';
import { AUTHORIZED, SanityCheckAction } from '../sanityCheckMiddleware';
import { getKubernetesNamespace } from '../../services/backend-client/kubernetesNamespaceApi';
+import { selectAsyncIsAuthorized, selectSanityCheckError } from '../SanityCheck/selectors';
export interface State {
isLoading: boolean;
@@ -46,10 +47,13 @@ export type ActionCreators = {
export const actionCreators: ActionCreators = {
requestNamespaces:
(): AppThunk>> =>
- async (dispatch): Promise> => {
- await dispatch({ type: 'REQUEST_NAMESPACES', check: AUTHORIZED });
-
+ async (dispatch, getState): Promise> => {
try {
+ await dispatch({ type: 'REQUEST_NAMESPACES', check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const namespaces = await getKubernetesNamespace();
dispatch({
type: 'RECEIVE_NAMESPACES',
diff --git a/packages/dashboard-frontend/src/store/Plugins/chePlugins/index.ts b/packages/dashboard-frontend/src/store/Plugins/chePlugins/index.ts
index 545702b32..4b9a0783b 100644
--- a/packages/dashboard-frontend/src/store/Plugins/chePlugins/index.ts
+++ b/packages/dashboard-frontend/src/store/Plugins/chePlugins/index.ts
@@ -16,6 +16,7 @@ import common from '@eclipse-che/common';
import { AppThunk } from '../..';
import { createObject } from '../../helpers';
import { AUTHORIZED, SanityCheckAction } from '../../sanityCheckMiddleware';
+import { selectAsyncIsAuthorized, selectSanityCheckError } from '../../SanityCheck/selectors';
// create new instance of `axios` to avoid adding an authorization header
const axiosInstance = axios.create();
@@ -49,10 +50,13 @@ export type ActionCreators = {
export const actionCreators: ActionCreators = {
requestPlugins:
(registryUrl: string): AppThunk> =>
- async (dispatch): Promise => {
- await dispatch({ type: 'REQUEST_PLUGINS', check: AUTHORIZED });
-
+ async (dispatch, getState): Promise => {
try {
+ await dispatch({ type: 'REQUEST_PLUGINS', check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const response = await axiosInstance.request({
method: 'GET',
url: `${registryUrl}/plugins/`,
diff --git a/packages/dashboard-frontend/src/store/User/Id/index.ts b/packages/dashboard-frontend/src/store/User/Id/index.ts
index 4827bcfe6..7a697e6e7 100644
--- a/packages/dashboard-frontend/src/store/User/Id/index.ts
+++ b/packages/dashboard-frontend/src/store/User/Id/index.ts
@@ -16,6 +16,7 @@ import { fetchCheUserId } from '../../../services/che-user-id';
import { createObject } from '../../helpers';
import { AppThunk } from '../../index';
import { AUTHORIZED, SanityCheckAction } from '../../sanityCheckMiddleware';
+import { selectAsyncIsAuthorized, selectSanityCheckError } from '../../SanityCheck/selectors';
export interface State {
cheUserId: string;
@@ -52,10 +53,13 @@ export type ActionCreators = {
export const actionCreators: ActionCreators = {
requestCheUserId:
(): AppThunk> =>
- async (dispatch): Promise => {
- await dispatch({ type: Type.REQUEST_CHE_USER_ID, check: AUTHORIZED });
-
+ async (dispatch, getState): Promise => {
try {
+ await dispatch({ type: Type.REQUEST_CHE_USER_ID, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const cheUserId = await fetchCheUserId();
dispatch({
type: Type.RECEIVE_CHE_USER_ID,
diff --git a/packages/dashboard-frontend/src/store/User/Profile/index.ts b/packages/dashboard-frontend/src/store/User/Profile/index.ts
index ccf0e7b47..f4ff0e890 100644
--- a/packages/dashboard-frontend/src/store/User/Profile/index.ts
+++ b/packages/dashboard-frontend/src/store/User/Profile/index.ts
@@ -18,6 +18,7 @@ import { fetchUserProfile } from '../../../services/backend-client/userProfileAp
import { createObject } from '../../helpers';
import { AppThunk } from '../../index';
import { AUTHORIZED, SanityCheckAction } from '../../sanityCheckMiddleware';
+import { selectAsyncIsAuthorized, selectSanityCheckError } from '../../SanityCheck/selectors';
export interface State {
userProfile: api.IUserProfile;
@@ -57,10 +58,13 @@ export type ActionCreators = {
export const actionCreators: ActionCreators = {
requestUserProfile:
(namespace: string): AppThunk> =>
- async (dispatch): Promise => {
- await dispatch({ type: Type.REQUEST_USER_PROFILE, check: AUTHORIZED });
-
+ async (dispatch, getState): Promise => {
try {
+ await dispatch({ type: Type.REQUEST_USER_PROFILE, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const userProfile = await fetchUserProfile(namespace);
dispatch({
type: Type.RECEIVE_USER_PROFILE,
diff --git a/packages/dashboard-frontend/src/store/Workspaces/devWorkspaces/index.ts b/packages/dashboard-frontend/src/store/Workspaces/devWorkspaces/index.ts
index 43db44faa..d291052b3 100644
--- a/packages/dashboard-frontend/src/store/Workspaces/devWorkspaces/index.ts
+++ b/packages/dashboard-frontend/src/store/Workspaces/devWorkspaces/index.ts
@@ -58,6 +58,7 @@ import { selectDefaultDevfile } from '../../DevfileRegistries/selectors';
import * as DwApi from '../../../services/backend-client/devWorkspaceApi';
import { selectDefaultEditor } from '../../Plugins/devWorkspacePlugins/selectors';
import { DEVWORKSPACE_STORAGE_TYPE_ATTR } from '../../../services/devfileApi/devWorkspace/spec/template';
+import { selectAsyncIsAuthorized, selectSanityCheckError } from '../../SanityCheck/selectors';
export const onStatusChangeCallbacks = new Map void>();
@@ -193,9 +194,12 @@ export const actionCreators: ActionCreators = {
requestWorkspaces:
(): AppThunk> =>
async (dispatch, getState): Promise => {
- await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
-
try {
+ await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const defaultKubernetesNamespace = selectDefaultNamespace(getState());
const defaultNamespace = defaultKubernetesNamespace.name;
const { workspaces, resourceVersion } = defaultNamespace
@@ -239,10 +243,13 @@ export const actionCreators: ActionCreators = {
requestWorkspace:
(workspace: devfileApi.DevWorkspace): AppThunk> =>
- async (dispatch): Promise => {
- await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
-
+ async (dispatch, getState): Promise => {
try {
+ await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const namespace = workspace.metadata.namespace;
const name = workspace.metadata.name;
const update = await getDevWorkspaceClient().getWorkspaceByName(namespace, name);
@@ -287,8 +294,12 @@ export const actionCreators: ActionCreators = {
return;
}
await OAuthService.refreshTokenIfNeeded(workspace);
- await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
try {
+ await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
checkRunningWorkspacesLimit(getState());
if (workspace.metadata.annotations?.[DEVWORKSPACE_NEXT_START_ANNOTATION]) {
@@ -432,10 +443,13 @@ export const actionCreators: ActionCreators = {
terminateWorkspace:
(workspace: devfileApi.DevWorkspace): AppThunk> =>
- async (dispatch): Promise => {
- await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
-
+ async (dispatch, getState): Promise => {
try {
+ await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const namespace = workspace.metadata.namespace;
const name = workspace.metadata.name;
await getDevWorkspaceClient().delete(namespace, name);
@@ -460,10 +474,13 @@ export const actionCreators: ActionCreators = {
updateWorkspaceAnnotation:
(workspace: devfileApi.DevWorkspace): AppThunk> =>
- async (dispatch): Promise => {
- await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
-
+ async (dispatch, getState): Promise => {
try {
+ await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const updated = await getDevWorkspaceClient().updateAnnotation(workspace);
dispatch({
type: Type.UPDATE_DEVWORKSPACE,
@@ -483,10 +500,13 @@ export const actionCreators: ActionCreators = {
updateWorkspace:
(workspace: devfileApi.DevWorkspace): AppThunk> =>
- async (dispatch): Promise => {
- await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
-
+ async (dispatch, getState): Promise => {
try {
+ await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const updated = await getDevWorkspaceClient().update(workspace);
dispatch({
type: Type.UPDATE_DEVWORKSPACE,
@@ -518,9 +538,12 @@ export const actionCreators: ActionCreators = {
const pluginRegistryInternalUrl = selectPluginRegistryInternalUrl(state);
const defaultNamespace = defaultKubernetesNamespace.name;
- await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
-
try {
+ await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
/* create a new DevWorkspace */
const createResp = await getDevWorkspaceClient().createDevWorkspace(
defaultNamespace,
@@ -611,9 +634,12 @@ export const actionCreators: ActionCreators = {
let devWorkspaceResource: devfileApi.DevWorkspace;
let devWorkspaceTemplateResource: devfileApi.DevWorkspaceTemplate;
- await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
-
try {
+ await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const response = await getEditor(defaultsEditor, dispatch, getState, pluginRegistryUrl);
if (response.content) {
editorContent = response.content;
@@ -793,8 +819,6 @@ export const actionCreators: ActionCreators = {
},
): AppThunk> =>
async (dispatch, getState): Promise => {
- await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
-
const state = getState();
const pluginRegistryUrl = state.dwServerConfig.config.pluginRegistryURL;
let devWorkspaceResource: devfileApi.DevWorkspace;
@@ -843,6 +867,11 @@ export const actionCreators: ActionCreators = {
}
try {
+ await dispatch({ type: Type.REQUEST_DEVWORKSPACE, check: AUTHORIZED });
+ if (!(await selectAsyncIsAuthorized(getState()))) {
+ const error = selectSanityCheckError(getState());
+ throw new Error(error);
+ }
const resourcesContent = await fetchResources({
pluginRegistryUrl,
devfileContent: dump(devfile),