Skip to content

Commit 5d9eadf

Browse files
refactor: remove Redux state management and associated actions, transitioning to React Context for authentication and error handling
1 parent c4c24dd commit 5d9eadf

37 files changed

+231
-1062
lines changed

jestSetupAfterEnv.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ jest.mock('./src/react/next-architecture/ui/XCoreLibraryProvider', () => {
167167
});
168168

169169
jest.mock('@module-federation/enhanced/runtime', () => {}, { virtual: true });
170+
171+
jest.mock('./src/react/account/AccountRoleSelectButtonAndModal', () => ({
172+
__esModule: true,
173+
default: () => <button>Switch Account</button>,
174+
}));
170175
jest.mock('@scality/module-federation', () => {
171176
const router = jest.requireActual('react-router');
172177
return {

src/js/IAMClient.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,13 @@
1-
import { AppState } from '../types/state';
21
import IAM from 'aws-sdk/clients/iam';
32
import type { AwsCredentialIdentity } from '@aws-sdk/types';
43
import {
54
IAMClient as IAMClientInterface,
65
WebIdentityRoles,
76
} from '../types/iam';
8-
import { AuthUser } from '../types/auth';
9-
import { getClients } from '../react/utils/actions';
107
import { notFalsyTypeGuard } from '../types/typeGuards';
118
import { policyDocumentType } from 'aws-sdk/clients/iam';
129
import { genClientEndpoint } from '../react/utils';
1310

14-
export function getAssumeRoleWithWebIdentityIAM(
15-
state: AppState,
16-
roleArn: string,
17-
user: AuthUser,
18-
): Promise<IAMClient> {
19-
const { auth } = state;
20-
const { stsClient } = getClients(state);
21-
22-
const assumeRoleParams = {
23-
idToken: user.id_token,
24-
roleArn: roleArn,
25-
RoleSessionName: `ui-${user.profile.sub}`,
26-
};
27-
28-
return stsClient.assumeRoleWithWebIdentity(assumeRoleParams).then((creds) => {
29-
const iamClient = new IAMClient(auth.config.iamEndpoint);
30-
iamClient.login({
31-
accessKeyId: creds.Credentials.AccessKeyId,
32-
secretAccessKey: creds.Credentials.SecretAccessKey,
33-
sessionToken: creds.Credentials.SessionToken,
34-
});
35-
return iamClient;
36-
});
37-
}
38-
3911
export function getRolesForWebIdentity(
4012
endpoint: string,
4113
token: string,

src/js/mock/managementClientMSWHandlers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { LatestUsedCapacity } from '../../react/next-architecture/domain/entitie
1111
export const ACCOUNT_ID = '718643629313';
1212
export const BUCKET_NAME = 'test-bucket';
1313
export const azureblobstorage = 'azureblobstorage';
14+
export const INSTANCE_ID = '3d49e1f9-fa2f-40aa-b2d4-c7a8b04c6cde';
1415

1516
export const COLD_LOCATION_NAME = 'europe25-myroom-cold';
1617

src/js/mutations.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { act, renderHook } from '@testing-library/react-hooks';
22
import { rest } from 'msw';
33
import { setupServer } from 'msw/node';
4-
import { INSTANCE_ID } from '../react/actions/__tests__/utils/testUtil';
4+
import { INSTANCE_ID } from './mock/managementClientMSWHandlers';
55
import {
66
GET_VEEAM_IMMUTABLE_POLICY,
77
VEEAM_IMMUTABLE_POLICY_NAME,

src/react/AuthLoadingProvider.tsx

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@ import {
33
useContext,
44
useEffect,
55
useMemo,
6+
useState,
67
JSX,
78
} from 'react';
8-
import { useDispatch, useSelector } from 'react-redux';
99
import { useShellHooks } from '@scality/module-federation';
1010
import { useConfig } from './next-architecture/ui/ConfigProvider';
11-
import { loadAppConfig } from './actions';
12-
import { AppState } from '../types/state';
13-
import { useComponentError } from './ErrorProvider';
11+
import { useAuthError, useAuthFailure } from './ErrorProvider';
1412

1513
type AuthLoadingContextValue = {
1614
isConfigLoaded: boolean;
@@ -34,28 +32,39 @@ export const useAuthLoading = () => {
3432
};
3533

3634
const AuthLoadingProvider = ({ children }: { children: JSX.Element }) => {
37-
const dispatch = useDispatch();
3835
const config = useConfig();
3936
const { useAuth } = useShellHooks();
4037
const { userData } = useAuth();
38+
const { showAuthError } = useAuthError();
39+
const { setAuthFailure } = useAuthFailure();
4140

42-
const isConfigLoaded = useSelector(
43-
(state: AppState) => state.auth.isConfigLoaded,
44-
);
45-
const isClientsLoaded = useSelector(
46-
(state: AppState) => state.auth.isClientsLoaded,
47-
);
48-
const configFailure = useSelector(
49-
(state: AppState) => state.auth.configFailure,
50-
);
51-
const { componentError } = useComponentError();
52-
const configFailureErrorMessage = componentError || '';
41+
const [isConfigLoaded, setIsConfigLoaded] = useState(false);
42+
const [isClientsLoaded, setIsClientsLoaded] = useState(false);
43+
const [configFailure, setConfigFailure] = useState(false);
44+
const [configFailureErrorMessage, setConfigFailureErrorMessage] = useState('');
5345

5446
useEffect(() => {
55-
if (userData?.original?.profile?.sub && config && !isConfigLoaded) {
56-
dispatch(loadAppConfig(config, userData));
47+
if (!userData?.original?.profile?.sub || !config) {
48+
return;
49+
}
50+
51+
if (isConfigLoaded) {
52+
return;
53+
}
54+
55+
const instanceIds = userData?.original?.profile?.instanceIds;
56+
if (!instanceIds || instanceIds.length === 0) {
57+
const errorMessage = 'missing the "instanceIds" claim in ID token';
58+
showAuthError(errorMessage);
59+
setAuthFailure();
60+
setConfigFailure(true);
61+
setConfigFailureErrorMessage(errorMessage);
62+
return;
5763
}
58-
}, [dispatch, config, userData?.original?.profile?.sub, isConfigLoaded]);
64+
65+
setIsConfigLoaded(true);
66+
setIsClientsLoaded(true);
67+
}, [config, userData?.original?.profile?.sub, isConfigLoaded, showAuthError, setAuthFailure]);
5968

6069
const contextValue = useMemo(
6170
() => ({

src/react/ErrorProvider.tsx

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@ import {
44
useState,
55
useCallback,
66
useMemo,
7-
useEffect,
87
JSX,
98
} from 'react';
10-
import { useSelector } from 'react-redux';
11-
import { AppState } from '../types/state';
129

1310
type ErrorType = 'modal' | 'auth' | 'component';
1411

@@ -130,42 +127,13 @@ export const useErrorHandler = () => {
130127
return { handleClientError, handleAWSError, showModalError };
131128
};
132129

133-
const ErrorProvider = ({ children }: { children: JSX.Element }) => {
130+
const ErrorProvider = ({ children }: { children: React.ReactNode }) => {
134131
const [error, setError] = useState<ErrorState>({
135132
message: null,
136133
type: null,
137134
});
138135
const [authFailure, setAuthFailureState] = useState(false);
139136

140-
// Sync Redux uiErrors state (temporary, remove after full migration)
141-
const reduxErrorType = useSelector(
142-
(state: AppState) => state.uiErrors.errorType,
143-
);
144-
const reduxErrorMsg = useSelector(
145-
(state: AppState) => state.uiErrors.errorMsg,
146-
);
147-
const reduxAuthFailure = useSelector(
148-
(state: AppState) => state.networkActivity.authFailure,
149-
);
150-
151-
useEffect(() => {
152-
if (reduxErrorType && reduxErrorMsg) {
153-
const typeMap: Record<string, ErrorType> = {
154-
byModal: 'modal',
155-
byAuth: 'auth',
156-
byComponent: 'component',
157-
};
158-
setError({
159-
message: reduxErrorMsg,
160-
type: typeMap[reduxErrorType] || 'modal',
161-
});
162-
}
163-
}, [reduxErrorType, reduxErrorMsg]);
164-
165-
useEffect(() => {
166-
setAuthFailureState(reduxAuthFailure);
167-
}, [reduxAuthFailure]);
168-
169137
const showModalError = useCallback((message: string) => {
170138
setError({ message, type: 'modal' });
171139
}, []);

src/react/ISV/components/__tests__/ISVSteps.test.tsx

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { render, screen } from '@testing-library/react';
1+
import { render, screen, renderHook } from '@testing-library/react';
22
import * as React from 'react';
33
import { ISVStepperContext, useISVStepper } from '../ISVStepperContext';
44
import { Veeam } from '../../modules/veeam';
@@ -16,35 +16,31 @@ jest.mock('../ISVSteps', () => {
1616
};
1717
});
1818

19-
// Mock dependencies
2019
jest.mock('react-router', () => ({
20+
...jest.requireActual('react-router'),
2121
useSearchParams: jest.fn(),
2222
}));
2323

24-
// Import React hooks correctly
2524
const { useContext } = React;
2625

27-
// Test the useISVStepper hook
2826
describe('useISVStepper', () => {
2927
it('should throw an error when used outside of the Provider', () => {
30-
const TestComponent = () => {
31-
useISVStepper();
32-
return null;
33-
};
28+
const originalError = console.error;
29+
console.error = jest.fn();
3430

35-
expect(() => render(<TestComponent />)).toThrow(
36-
'useISVStepper must be used within ISVStepperProvider',
37-
);
31+
expect(() => {
32+
renderHook(() => useISVStepper());
33+
}).toThrow('useISVStepper must be used within ISVStepperProvider');
34+
35+
console.error = originalError;
3836
});
3937
});
4038

41-
// Test the ISVSteps component
4239
describe('ISVSteps', () => {
4340
beforeEach(() => {
4441
jest.clearAllMocks();
4542
});
4643

47-
// Component to access context for testing
4844
const ContextReader = () => {
4945
const context = useContext(ISVStepperContext);
5046
if (!context) return null;
@@ -58,7 +54,6 @@ describe('ISVSteps', () => {
5854
);
5955
};
6056

61-
// Set up mock implementation in each test
6257
const setupMockISVSteps = (platformId: string | null) => {
6358
const { ISVSteps } = require('../ISVSteps');
6459

@@ -89,15 +84,13 @@ describe('ISVSteps', () => {
8984
);
9085
});
9186

92-
// Configure useSearchParams mock
9387
const { useSearchParams } = require('react-router');
9488
useSearchParams.mockReturnValue([
9589
new URLSearchParams(platformId ? `?platform=${platformId}` : ''),
9690
jest.fn(),
9791
]);
9892
};
9993

100-
// Helper to render component with theme and context reader
10194
const renderWithThemeAndContextReader = () => {
10295
const { ISVSteps } = require('../ISVSteps');
10396

@@ -116,7 +109,6 @@ describe('ISVSteps', () => {
116109
setupMockISVSteps(platformId);
117110
renderWithThemeAndContextReader();
118111

119-
// Check platform ID in context
120112
expect(screen.getByTestId('platform-id')).toHaveTextContent(
121113
platformId || 'no-platform',
122114
);
@@ -127,11 +119,9 @@ describe('ISVSteps', () => {
127119
setupMockISVSteps('veeam-vbr');
128120
renderWithThemeAndContextReader();
129121

130-
// Check UI components
131122
expect(screen.getByTestId('isv-steps')).toBeInTheDocument();
132123
expect(screen.getByTestId('stepper')).toBeInTheDocument();
133124

134-
// Check step labels
135125
expect(screen.getByTestId('step-0')).toHaveTextContent('Configure');
136126
expect(screen.getByTestId('step-1')).toHaveTextContent('Apply Actions');
137127
expect(screen.getByTestId('step-2')).toHaveTextContent('Summary');

src/react/Routes.tsx

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,15 @@ import {
66
Loader,
77
Sidebar,
88
} from '@scality/core-ui';
9-
import { useCallback, useEffect, useState } from 'react';
10-
import { useDispatch, useSelector } from 'react-redux';
9+
import { useCallback, useState } from 'react';
1110
import { Navigate, Route, Routes, matchPath, useLocation } from 'react-router';
1211

13-
import makeMgtClient from '../js/managementClient';
14-
import { AppState } from '../types/state';
1512
import DataServiceRoleProvider, {
1613
useCurrentAccount,
1714
} from './DataServiceRoleProvider';
1815
import ManagementProvider from './ManagementProvider';
1916
import STSProvider from './STSProvider';
2017
import { useAuthLoading } from './AuthLoadingProvider';
21-
import { setManagementClient } from './actions';
2218
import ReauthDialog from './ui-elements/ReauthDialog';
2319

2420
import { useConfig } from './next-architecture/ui/ConfigProvider';
@@ -31,7 +27,7 @@ import AccountCreate from './account/AccountCreate';
3127
import AccountContent from './account/AccountContent';
3228
import DataBrowser from './databrowser/DataBrowser';
3329
import LocationEditor from './locations/LocationEditor';
34-
import { useBasenameRelativeNavigate, useShellHooks } from '@scality/module-federation';
30+
import { useBasenameRelativeNavigate } from '@scality/module-federation';
3531
import Attachments from './account/iamAttachment/Attachments';
3632
import AccountUpdateUser from './account/AccountUpdateUser';
3733
import UpdateAccountPolicy from './account/UpdateAccountPolicy';
@@ -93,26 +89,9 @@ export function PrivateRoutes({
9389
}: {
9490
hideSideBar?: boolean;
9591
}) {
96-
const dispatch = useDispatch();
9792
const { isClientsLoaded } = useAuthLoading();
98-
const { useAuth } = useShellHooks();
99-
const { userData } = useAuth();
10093
const config = useConfig();
10194

102-
const managementEndpoint = useSelector(
103-
(state: AppState) => state.auth?.config?.managementEndpoint,
104-
);
105-
106-
useEffect(() => {
107-
if (managementEndpoint && userData?.token) {
108-
const managementClient = makeMgtClient(
109-
managementEndpoint,
110-
userData.token,
111-
);
112-
dispatch(setManagementClient(managementClient));
113-
}
114-
}, [dispatch, managementEndpoint, userData?.token]);
115-
11695
if (!isClientsLoaded) {
11796
return (
11897
<Loader centered>

src/react/account/__tests__/AccountCreate.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event';
33
import { rest } from 'msw';
44
import { setupServer } from 'msw/node';
55
import { getConfigOverlay } from '../../../js/mock/managementClientMSWHandlers';
6-
import { INSTANCE_ID } from '../../actions/__tests__/utils/testUtil';
6+
import { INSTANCE_ID } from '../../../js/mock/managementClientMSWHandlers';
77
import {
88
TEST_API_BASE_URL,
99
reduxRender,

src/react/account/__tests__/AccountCreateUser.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { rest } from 'msw';
33
import { setupServer } from 'msw/node';
44
import { Route, Routes } from 'react-router';
55
import { getConfigOverlay } from '../../../js/mock/managementClientMSWHandlers';
6-
import { INSTANCE_ID } from '../../actions/__tests__/utils/testUtil';
6+
import { INSTANCE_ID } from '../../../js/mock/managementClientMSWHandlers';
77
import {
88
mockOffsetSize,
99
renderWithCustomRoute,

0 commit comments

Comments
 (0)