Skip to content

Commit 654f52b

Browse files
authored
Merge pull request #779 from actiontech/fix/user-guide
Fix/user guide
2 parents 4d86310 + 3adc9ea commit 654f52b

File tree

23 files changed

+804
-259
lines changed

23 files changed

+804
-259
lines changed

packages/base/src/App.ce.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ describe('test App ce', () => {
5959
(useSelector as jest.Mock).mockImplementation((selector) => {
6060
return selector({
6161
user: {
62-
token: 'AAh32ffdswt'
62+
token: 'AAh32ffdswt',
63+
isLoggingIn: false
6364
},
6465
nav: {
6566
modalStatus: {

packages/base/src/App.test.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ describe('App', () => {
9393
(useSelector as jest.Mock).mockImplementation((selector) => {
9494
return selector({
9595
user: {
96-
token: 'AAh32ffdswt'
96+
token: 'AAh32ffdswt',
97+
isLoggingIn: false
9798
},
9899
nav: {
99100
modalStatus: {
@@ -137,7 +138,8 @@ describe('App', () => {
137138
(useSelector as jest.Mock).mockImplementation((selector) => {
138139
return selector({
139140
user: {
140-
token: ''
141+
token: '',
142+
isLoggingIn: false
141143
}
142144
});
143145
});
@@ -210,12 +212,16 @@ describe('App', () => {
210212
(useSelector as jest.Mock).mockImplementation((selector) => {
211213
return selector({
212214
user: {
213-
token: ''
215+
token: '',
216+
isLoggingIn: false
214217
},
215218
nav: {
216219
modalStatus: {
217220
[ModalName.Company_Notice]: false
218221
}
222+
},
223+
availabilityZone: {
224+
availabilityZoneTips: []
219225
}
220226
});
221227
});

packages/base/src/App.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ export const Wrapper: React.FC<{
9696
return <>{!initRenderApp && children}</>;
9797
};
9898
function App() {
99-
const { token } = useSelector((state: IReduxState) => ({
100-
token: state.user.token
99+
const { isAfterLoggingIn } = useSelector((state: IReduxState) => ({
100+
isAfterLoggingIn: !state.user.isLoggingIn && !!state.user.token
101101
}));
102102
const dispatch = useDispatch();
103103
const { notificationContextHolder } = useNotificationContext();
@@ -160,7 +160,9 @@ function App() {
160160
userOperationPermissions
161161
]);
162162
const elements = useRoutes(
163-
token ? (AuthRouterConfigData as RouteObject[]) : unAuthRouterConfig
163+
isAfterLoggingIn
164+
? (AuthRouterConfigData as RouteObject[])
165+
: unAuthRouterConfig
164166
);
165167
useChangeTheme();
166168
const themeData = useMemo(() => {
@@ -204,10 +206,10 @@ function App() {
204206
});
205207
}, [dispatch, fetchModuleSupportStatus, getUserBySession, updateDriverList]);
206208
useEffect(() => {
207-
if (token) {
209+
if (isAfterLoggingIn) {
208210
getInitialData();
209211
}
210-
}, [token, getInitialData]);
212+
}, [getInitialData, isAfterLoggingIn]);
211213
useEffect(() => {
212214
i18n.changeLanguage(currentLanguage);
213215
}, [currentLanguage]);
@@ -314,7 +316,7 @@ function App() {
314316
<StyledEngineProvider injectFirst>
315317
<ThemeProvider theme={themeData}>
316318
{notificationContextHolder}
317-
<EmptyBox if={!!token} defaultNode={<>{elements}</>}>
319+
<EmptyBox if={isAfterLoggingIn} defaultNode={<>{elements}</>}>
318320
<ErrorBoundary>{body}</ErrorBoundary>
319321
</EmptyBox>
320322
</ThemeProvider>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { act, cleanup } from '@testing-library/react';
2+
import { superRenderHook } from '@actiontech/shared/lib/testUtil/superRender';
3+
import { useDispatch, useSelector } from 'react-redux';
4+
import {
5+
baseMockApi,
6+
createSpySuccessResponse
7+
} from '@actiontech/shared/lib/testUtil/mockApi';
8+
import useNavigateToWorkbench from '.';
9+
import { mockUseRecentlySelectedZone } from '../../testUtils/mockHooks/mockUseRecentlySelectedZone';
10+
import { mockGatewayTipsData } from '@actiontech/shared/lib/testUtil/mockApi/base/gateway/data';
11+
12+
jest.mock('react-redux', () => ({
13+
...jest.requireActual('react-redux'),
14+
useDispatch: jest.fn(),
15+
useSelector: jest.fn()
16+
}));
17+
18+
describe('useNavigateToWorkbench', () => {
19+
const dispatchSpy = jest.fn();
20+
let getGatewayTipsSpy: jest.SpyInstance;
21+
let getSQLQueryConfigurationSpy: jest.SpyInstance;
22+
23+
beforeEach(() => {
24+
(useDispatch as jest.Mock).mockImplementation(() => dispatchSpy);
25+
(useSelector as jest.Mock).mockImplementation((selector) => {
26+
return selector({
27+
availabilityZone: {
28+
availabilityZoneTips: mockGatewayTipsData
29+
}
30+
});
31+
});
32+
mockUseRecentlySelectedZone();
33+
getGatewayTipsSpy = baseMockApi.gateway.getGatewayTips();
34+
getSQLQueryConfigurationSpy = baseMockApi.cloudBeaver.getSqlQueryUrl();
35+
jest.useFakeTimers();
36+
});
37+
38+
afterEach(() => {
39+
jest.useRealTimers();
40+
jest.clearAllMocks();
41+
cleanup();
42+
});
43+
44+
it('should initialize with correct default values', () => {
45+
const { result } = superRenderHook(() => useNavigateToWorkbench());
46+
47+
expect(result.current.navigateToWorkbenchLoading).toBeFalsy();
48+
expect(result.current.getAvailabilityZoneTipsLoading).toBeFalsy();
49+
expect(typeof result.current.navigateToWorkbenchAsync).toBe('function');
50+
expect(typeof result.current.getAvailabilityZoneTipsAsync).toBe('function');
51+
});
52+
53+
it('should fetch availability zone tips successfully', async () => {
54+
const { result } = superRenderHook(() => useNavigateToWorkbench());
55+
expect(result.current.getAvailabilityZoneTipsLoading).toBeFalsy();
56+
57+
act(() => {
58+
result.current.getAvailabilityZoneTipsAsync();
59+
});
60+
expect(result.current.getAvailabilityZoneTipsLoading).toBeTruthy();
61+
await act(async () => jest.advanceTimersByTime(3000));
62+
expect(result.current.getAvailabilityZoneTipsLoading).toBeFalsy();
63+
64+
expect(getGatewayTipsSpy).toHaveBeenCalledTimes(1);
65+
expect(dispatchSpy).toHaveBeenCalledWith({
66+
type: 'availabilityZone/updateAvailabilityZoneTips',
67+
payload: { availabilityZoneTips: mockGatewayTipsData }
68+
});
69+
});
70+
71+
it('should fetch SQL query configuration successfully', async () => {
72+
const mockUpdateRecentlySelectedZone = jest.fn();
73+
getSQLQueryConfigurationSpy.mockImplementation(() =>
74+
createSpySuccessResponse({
75+
data: {
76+
enable_sql_query: true,
77+
sql_query_root_uri: '/cloudbeaver'
78+
}
79+
})
80+
);
81+
mockUseRecentlySelectedZone({
82+
availabilityZone: undefined,
83+
updateRecentlySelectedZone: mockUpdateRecentlySelectedZone
84+
});
85+
const { result } = superRenderHook(() => useNavigateToWorkbench());
86+
expect(result.current.navigateToWorkbenchLoading).toBeFalsy();
87+
88+
act(() => {
89+
result.current.navigateToWorkbenchAsync();
90+
});
91+
expect(result.current.navigateToWorkbenchLoading).toBeTruthy();
92+
await act(async () => jest.advanceTimersByTime(3000));
93+
expect(result.current.navigateToWorkbenchLoading).toBeFalsy();
94+
95+
expect(getSQLQueryConfigurationSpy).toHaveBeenCalledTimes(1);
96+
expect(mockUpdateRecentlySelectedZone).toHaveBeenCalledWith(
97+
mockGatewayTipsData[0]
98+
);
99+
});
100+
});
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { useRequest } from 'ahooks';
2+
import { useDispatch, useSelector } from 'react-redux';
3+
import { DmsApi } from '@actiontech/shared/lib/api';
4+
import { ResponseCode } from '@actiontech/dms-kit';
5+
import useRecentlySelectedZone from '@actiontech/dms-kit/es/features/useRecentlySelectedZone';
6+
import { IReduxState } from '../../store';
7+
import { updateAvailabilityZoneTips } from '../../store/availabilityZone';
8+
9+
const useNavigateToWorkbench = () => {
10+
const dispatch = useDispatch();
11+
12+
const availabilityZoneTips = useSelector(
13+
(state: IReduxState) => state.availabilityZone.availabilityZoneTips
14+
);
15+
16+
const { availabilityZone, updateRecentlySelectedZone } =
17+
useRecentlySelectedZone();
18+
19+
const {
20+
runAsync: getAvailabilityZoneTipsAsync,
21+
loading: getAvailabilityZoneTipsLoading
22+
} = useRequest(
23+
() =>
24+
DmsApi.GatewayService.GetGatewayTips().then((res) => {
25+
if (res.data.code === ResponseCode.SUCCESS) {
26+
return res.data.data;
27+
}
28+
return [];
29+
}),
30+
{
31+
onSuccess: (res) => {
32+
dispatch(
33+
updateAvailabilityZoneTips({
34+
availabilityZoneTips: res ?? []
35+
})
36+
);
37+
},
38+
manual: true
39+
}
40+
);
41+
42+
const {
43+
loading: navigateToWorkbenchLoading,
44+
runAsync: navigateToWorkbenchAsync
45+
} = useRequest(
46+
() => {
47+
return DmsApi.CloudBeaverService.GetSQLQueryConfiguration().then(
48+
(res) => {
49+
if (res.data.code === ResponseCode.SUCCESS) {
50+
return res.data.data;
51+
}
52+
}
53+
);
54+
},
55+
{
56+
manual: true,
57+
onSuccess: (res) => {
58+
if (
59+
res?.enable_sql_query &&
60+
res.sql_query_root_uri &&
61+
res.sql_query_root_uri !== location.pathname
62+
) {
63+
// 如果当前设置了可用区 并且没有最近选择的可用区记录 则设置一个默认的可用区
64+
if (!!availabilityZoneTips.length && !availabilityZone) {
65+
updateRecentlySelectedZone(availabilityZoneTips[0]);
66+
}
67+
68+
// res.sql_query_root_uri !== location.pathname 防止无限刷新
69+
// 因为sql_query_root_uri是不携带origin的,只有pathname。所以开发环境localhost不可以直接跳转到CB
70+
// #if [PROD]
71+
window.location.href = res.sql_query_root_uri;
72+
// #endif
73+
}
74+
}
75+
}
76+
);
77+
78+
return {
79+
navigateToWorkbenchLoading,
80+
navigateToWorkbenchAsync,
81+
getAvailabilityZoneTipsAsync,
82+
getAvailabilityZoneTipsLoading
83+
};
84+
};
85+
86+
export default useNavigateToWorkbench;

packages/base/src/hooks/useSessionUser/index.test.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,24 @@ describe('useSessionUser', () => {
5555
});
5656
expect(result.current.getSessionUserLoading).toBeFalsy();
5757
});
58+
59+
it('should get user system data', async () => {
60+
const { result } = superRenderHook(() => useSessionUser(), undefined, {
61+
initStore: {
62+
user: { uid: 'test' }
63+
}
64+
});
65+
expect(result.current.getSessionUserSystemLoading).toBeFalsy();
66+
expect(result.current.shouldNavigateToWorkbench).toEqual(undefined);
67+
68+
await act(async () => {
69+
result.current.getSessionUserInfoAsync();
70+
await jest.advanceTimersByTime(3000);
71+
});
72+
expect(getUserBySessionSpy).toHaveBeenCalledTimes(1);
73+
expect(getCurrentUserSpy).toHaveBeenCalledTimes(1);
74+
await act(async () => jest.advanceTimersByTime(3000));
75+
expect(result.current.getSessionUserSystemLoading).toBeFalsy();
76+
expect(result.current.shouldNavigateToWorkbench).toBeFalsy();
77+
});
5878
});

packages/base/src/hooks/useSessionUser/index.tsx

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { ResponseCode } from '@actiontech/dms-kit';
44
import { updateUserUid } from '../../store/user';
55
import { useUserInfo } from '@actiontech/shared/lib/features';
66
import Session from '@actiontech/shared/lib/api/base/service/Session';
7+
import User from '@actiontech/shared/lib/api/base/service/User';
8+
import { GetUserSystemEnum } from '@actiontech/shared/lib/api/base/service/common.enum';
79

810
const useSessionUser = () => {
911
const dispatch = useDispatch();
@@ -28,10 +30,34 @@ const useSessionUser = () => {
2830
}
2931
});
3032

33+
const {
34+
data: shouldNavigateToWorkbench,
35+
runAsync: getSessionUserInfoAsync,
36+
loading: getSessionUserSystemLoading
37+
} = useRequest(
38+
() =>
39+
Session.GetUserBySession({}).then((res) => {
40+
if (res.data.code === ResponseCode.SUCCESS) {
41+
const uid = res.data.data?.user_uid ?? '';
42+
return User.GetUser({ user_uid: uid }).then((resp) => {
43+
if (resp.data.code === ResponseCode.SUCCESS) {
44+
return resp.data.data?.system === GetUserSystemEnum.WORKBENCH;
45+
}
46+
});
47+
}
48+
}),
49+
{
50+
manual: true
51+
}
52+
);
53+
3154
return {
3255
sessionUser,
3356
getSessionUserLoading,
34-
getUserBySession
57+
getUserBySession,
58+
getSessionUserInfoAsync,
59+
shouldNavigateToWorkbench,
60+
getSessionUserSystemLoading
3561
};
3662
};
3763

0 commit comments

Comments
 (0)