Skip to content

Commit

Permalink
[RHOAIENG-1154]: Apply modal to alert user error
Browse files Browse the repository at this point in the history
check for 403 status code

add cypress test

update test title

format

format

format

remove it.only

fix relative import
  • Loading branch information
jenny-s51 committed Dec 20, 2024
1 parent f9ac99e commit 07e11ba
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 5 deletions.
9 changes: 9 additions & 0 deletions frontend/src/__tests__/cypress/cypress/pages/loginDialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Chainable = Cypress.Chainable;

export class LoginDialog {
findText(): Chainable<JQuery<HTMLElement>> {
return cy.findByTestId('timeout-text');
}
}

export const loginDialog = new LoginDialog();
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { mockDashboardConfig } from '~/__mocks__';
import { aboutDialog } from '~/__tests__/cypress/cypress/pages/aboutDialog';
import { mockConsoleLinks } from '~/__mocks__/mockConsoleLinks';
import { loginDialog } from '~/__tests__/cypress/cypress/pages/loginDialog';

describe('Application', () => {
it('should disallow access to the dashboard', () => {
Expand Down Expand Up @@ -113,4 +114,22 @@ describe('Application', () => {
aboutDialog.findText().should('contain.text', 'OpenShift');
aboutDialog.findProductName().should('contain.text', 'OpenShift AI');
});
it('should show the login modal when receiving a 403 status code', () => {
// Mock the intercept to return a 403 status code

cy.interceptOdh('GET /api/config', {
statusCode: 403,
}).as('getData403');

// Visit the page where the request is triggered
cy.visit('/');

// Wait for the intercept to be triggered
cy.wait('@getData403');

// Verify that the login modal is displayed
loginDialog
.findText()
.should('contain.text', 'Your session timed out. To continue working, log in');
});
});
35 changes: 34 additions & 1 deletion frontend/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import {
Alert,
Bullseye,
Button,
Modal,
ModalBody,
ModalFooter,
ModalHeader,
ModalVariant,
Page,
PageSection,
Spinner,
Expand Down Expand Up @@ -67,9 +72,37 @@ const App: React.FC = () => {
[buildStatuses, dashboardConfig, storageClasses],
);

const isUnauthorized = React.useMemo(() => {
if (fetchConfigError?.request?.status === 403) {
return true;
}
return false;
}, [fetchConfigError]);

// We lack the critical data to startup the app
if (userError || fetchConfigError) {
// There was an error fetching critical data
// Check for unauthorized state
if (isUnauthorized) {
return (
<Modal variant={ModalVariant.small} isOpen ouiaId="BasicModal">
<ModalHeader title="Session Expired" titleIconVariant="warning" />
<ModalBody data-testid="timeout-text">
Your session timed out. To continue working, log in.
</ModalBody>
<ModalFooter>
<Button
key="confirm"
variant="primary"
onClick={() => logout().then(() => window.location.reload())}
>
Log in
</Button>
</ModalFooter>
</Modal>
);
}

// Default error handling for other cases
return (
<Page>
<PageSection hasBodyWrapper={false}>
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/app/useApplicationSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import { AxiosError } from 'axios';
import { DashboardConfigKind } from '~/k8sTypes';
import { POLL_INTERVAL } from '~/utilities/const';
import { useDeepCompareMemoize } from '~/utilities/useDeepCompareMemoize';
Expand All @@ -8,10 +9,10 @@ import useTimeBasedRefresh from './useTimeBasedRefresh';
export const useApplicationSettings = (): {
dashboardConfig: DashboardConfigKind | null;
loaded: boolean;
loadError: Error | undefined;
loadError: AxiosError | undefined;
} => {
const [loaded, setLoaded] = React.useState(false);
const [loadError, setLoadError] = React.useState<Error>();
const [loadError, setLoadError] = React.useState<AxiosError>();
const [dashboardConfig, setDashboardConfig] = React.useState<DashboardConfigKind | null>(null);
const setRefreshMarker = useTimeBasedRefresh();

Expand All @@ -29,7 +30,7 @@ export const useApplicationSettings = (): {
setLoadError(undefined);
})
.catch((e) => {
if (e?.message?.includes('Error getting Oauth Info for user')) {
if (e?.response?.data?.message?.includes('Error getting Oauth Info for user')) {
// NOTE: this endpoint only requests oauth because of the security layer, this is not an ironclad use-case
// Something went wrong on the server with the Oauth, let us just log them out and refresh for them
/* eslint-disable-next-line no-console */
Expand Down
12 changes: 11 additions & 1 deletion frontend/src/services/dashboardConfigService.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AxiosError } from 'axios';
import axios from '~/utilities/axios';
import { DashboardConfigKind } from '~/k8sTypes';

Expand All @@ -7,6 +8,15 @@ export const fetchDashboardConfig = (): Promise<DashboardConfigKind> => {
.get(url)
.then((response) => response.data)
.catch((e) => {
throw new Error(e.response.data.message);
const message = e.response.data?.message;

// Throw the AxiosError with status code
throw new AxiosError(
message, // Error message from the server
message, // The error message also serves as the "code" argument for AxiosError
undefined, // Optional: request config that was used
e.response, // Optional: the full response object
e,
);
});
};

0 comments on commit 07e11ba

Please sign in to comment.