Skip to content

Commit 358f8a1

Browse files
authored
Merge pull request #402 from actiontech/chore/sqle-issue-2523-4
[feature]:(SqlManagementConf) Add user and data source operation permissions
2 parents f57c71e + 1633bdd commit 358f8a1

File tree

20 files changed

+916
-93
lines changed

20 files changed

+916
-93
lines changed

packages/shared/lib/api/sqle/service/common.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1657,7 +1657,7 @@ export interface IInstanceAuditPlanResV1 {
16571657

16581658
instance_audit_plan_id?: number;
16591659

1660-
instance_id?: number;
1660+
instance_id?: string;
16611661

16621662
instance_name?: string;
16631663

packages/shared/lib/components/ActiontechTable/hooks/useTableAction.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ const useTableAction = () => {
3232

3333
const renderAction = useCallback(
3434
<T extends Record<string, any>>(
35-
actions:
36-
| ActiontechTableActionMeta<T>[]
37-
| ActiontechTableToolbarActionMeta[] = [],
35+
actions: Array<
36+
ActiontechTableActionMeta<T> | ActiontechTableToolbarActionMeta
37+
> = [],
3838
record?: T
3939
) => {
4040
return (

packages/shared/lib/global/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export { default as useDbServiceDriver } from './useDbServiceDriver';
55
export { default as useFeaturePermission } from './useFeaturePermission';
66
export { default as useCurrentPermission } from './useCurrentPermission';
77
export { default as useProjectBusinessTips } from './useProjectBusinessTips';
8+
export { default as useUserOperationPermission } from './useUserOperationPermission';
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import { act, cleanup } from '@testing-library/react';
2+
import { mockUseCurrentProject } from '../../testUtil/mockHook/mockUseCurrentProject';
3+
import { mockUseCurrentUser } from '../../testUtil/mockHook/mockUseCurrentUser';
4+
import User from '../../api/base/service/User';
5+
import {
6+
createSpyFailResponse,
7+
createSpySuccessResponse
8+
} from '../../testUtil/mockApi';
9+
import { IGetUserOpPermissionReply } from '../../api/base/service/common';
10+
import {
11+
OpPermissionItemOpPermissionTypeEnum,
12+
OpPermissionItemRangeTypeEnum
13+
} from '../../api/base/service/common.enum';
14+
import { renderHooksWithRedux } from '../../testUtil/customRender';
15+
import useUserOperationPermission from '.';
16+
import {
17+
mockCurrentUserReturn,
18+
mockProjectInfo
19+
} from '../../testUtil/mockHook/data';
20+
21+
const userOperationPermissionMockData: IGetUserOpPermissionReply['data'] = {
22+
is_admin: true,
23+
op_permission_list: [
24+
{
25+
range_uids: ['700300'],
26+
range_type: OpPermissionItemRangeTypeEnum.project,
27+
op_permission_type: OpPermissionItemOpPermissionTypeEnum.project_admin
28+
}
29+
]
30+
};
31+
32+
describe('global/hooks/useUserOperationPermission', () => {
33+
const getUserOpPermission = () => {
34+
const spy = jest.spyOn(User, 'GetUserOpPermission');
35+
spy.mockImplementation(() =>
36+
createSpySuccessResponse({
37+
data: userOperationPermissionMockData
38+
})
39+
);
40+
return spy;
41+
};
42+
let getUserOpPermissionSpy: jest.SpyInstance;
43+
beforeEach(() => {
44+
mockUseCurrentProject();
45+
mockUseCurrentUser();
46+
getUserOpPermissionSpy = getUserOpPermission();
47+
jest.useFakeTimers();
48+
});
49+
50+
afterEach(() => {
51+
jest.useRealTimers();
52+
cleanup();
53+
});
54+
55+
it('should get user operation permission from request', async () => {
56+
const { result } = renderHooksWithRedux(useUserOperationPermission, {});
57+
58+
expect(result.current.loading).toBeFalsy();
59+
expect(result.current.userOperationPermission).toBeUndefined();
60+
expect(
61+
result.current.isHaveServicePermission(
62+
OpPermissionItemOpPermissionTypeEnum.auth_db_service_data
63+
)
64+
).toBeFalsy();
65+
66+
act(() => {
67+
result.current.updateUserOperationPermission();
68+
});
69+
70+
expect(result.current.loading).toBeTruthy();
71+
expect(getUserOpPermissionSpy).toHaveBeenCalledTimes(1);
72+
expect(getUserOpPermissionSpy).toHaveBeenCalledWith({
73+
user_uid: mockCurrentUserReturn.uid,
74+
project_uid: mockProjectInfo.projectID
75+
});
76+
77+
await act(async () => jest.advanceTimersByTime(3000));
78+
79+
expect(result.current.loading).toBeFalsy();
80+
expect(result.current.userOperationPermission).toEqual(
81+
userOperationPermissionMockData
82+
);
83+
expect(
84+
result.current.isHaveServicePermission(
85+
OpPermissionItemOpPermissionTypeEnum.auth_db_service_data
86+
)
87+
).toBeTruthy();
88+
});
89+
90+
it('isHaveServicePermission should return true when user is project manager', async () => {
91+
getUserOpPermissionSpy.mockClear();
92+
getUserOpPermissionSpy.mockImplementation(() =>
93+
createSpySuccessResponse({
94+
data: {
95+
is_admin: false,
96+
op_permission_list: [
97+
{
98+
range_uids: ['700300'],
99+
range_type: OpPermissionItemRangeTypeEnum.project,
100+
op_permission_type:
101+
OpPermissionItemOpPermissionTypeEnum.project_admin
102+
}
103+
]
104+
}
105+
})
106+
);
107+
const { result } = renderHooksWithRedux(useUserOperationPermission, {});
108+
109+
await act(async () => {
110+
result.current.updateUserOperationPermission();
111+
});
112+
await act(async () => {
113+
await jest.advanceTimersByTime(3000);
114+
});
115+
116+
expect(result.current.loading).toBeFalsy();
117+
expect(
118+
result.current.isHaveServicePermission(
119+
OpPermissionItemOpPermissionTypeEnum.auth_db_service_data
120+
)
121+
).toBeTruthy();
122+
});
123+
124+
it('isHaveServicePermission should return true when user have service permission', async () => {
125+
getUserOpPermissionSpy.mockClear();
126+
getUserOpPermissionSpy.mockImplementation(() =>
127+
createSpySuccessResponse({
128+
data: {
129+
is_admin: false,
130+
op_permission_list: [
131+
{
132+
range_uids: ['123'],
133+
range_type: OpPermissionItemRangeTypeEnum.db_service,
134+
op_permission_type:
135+
OpPermissionItemOpPermissionTypeEnum.auth_db_service_data
136+
}
137+
]
138+
}
139+
})
140+
);
141+
const { result } = renderHooksWithRedux(useUserOperationPermission, {});
142+
143+
await act(async () => {
144+
result.current.updateUserOperationPermission();
145+
});
146+
await act(async () => {
147+
await jest.advanceTimersByTime(3000);
148+
});
149+
150+
expect(result.current.loading).toBeFalsy();
151+
expect(
152+
result.current.isHaveServicePermission(
153+
OpPermissionItemOpPermissionTypeEnum.auth_db_service_data,
154+
'123'
155+
)
156+
).toBeTruthy();
157+
});
158+
159+
it('when response code is not equal success code', async () => {
160+
getUserOpPermissionSpy.mockClear();
161+
getUserOpPermissionSpy.mockImplementation(() => createSpyFailResponse({}));
162+
163+
const { result } = renderHooksWithRedux(useUserOperationPermission, {});
164+
165+
await act(async () => {
166+
result.current.updateUserOperationPermission();
167+
});
168+
await act(async () => {
169+
await jest.advanceTimersByTime(3000);
170+
});
171+
172+
expect(result.current.loading).toBeFalsy();
173+
expect(
174+
result.current.isHaveServicePermission(
175+
OpPermissionItemOpPermissionTypeEnum.auth_db_service_data
176+
)
177+
).toBeFalsy();
178+
});
179+
});
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { useRequest } from 'ahooks';
2+
import { useCallback } from 'react';
3+
import useCurrentUser from '../useCurrentUser';
4+
import useCurrentProject from '../useCurrentProject';
5+
import User from '../../api/base/service/User';
6+
import { ResponseCode } from '../../enum';
7+
import {
8+
OpPermissionItemOpPermissionTypeEnum,
9+
OpPermissionItemRangeTypeEnum
10+
} from '../../api/base/service/common.enum';
11+
12+
const useUserOperationPermission = () => {
13+
const { uid } = useCurrentUser();
14+
15+
const { projectID } = useCurrentProject();
16+
17+
const {
18+
data: userOperationPermission,
19+
loading,
20+
run: updateUserOperationPermission
21+
} = useRequest(
22+
() =>
23+
User.GetUserOpPermission({ user_uid: uid, project_uid: projectID }).then(
24+
(res) => {
25+
if (res.data.code === ResponseCode.SUCCESS) {
26+
return res.data.data;
27+
}
28+
}
29+
),
30+
{
31+
manual: true
32+
}
33+
);
34+
35+
const isHaveServicePermission = useCallback(
36+
(
37+
opPermissionType: OpPermissionItemOpPermissionTypeEnum,
38+
serviceID?: string
39+
) => {
40+
if (userOperationPermission) {
41+
const { is_admin, op_permission_list } = userOperationPermission;
42+
const haveProjectPermission = op_permission_list?.some((permission) => {
43+
if (
44+
permission.range_type === OpPermissionItemRangeTypeEnum.project &&
45+
permission.range_uids?.includes(projectID) &&
46+
permission.op_permission_type ===
47+
OpPermissionItemOpPermissionTypeEnum.project_admin
48+
) {
49+
return true;
50+
}
51+
return false;
52+
});
53+
54+
const haveServicePermission = op_permission_list?.some((permission) => {
55+
if (
56+
permission.range_type ===
57+
OpPermissionItemRangeTypeEnum.db_service &&
58+
(serviceID
59+
? permission.range_uids?.includes(serviceID)
60+
: !!permission.range_uids?.length) &&
61+
permission.op_permission_type === opPermissionType
62+
) {
63+
return true;
64+
}
65+
return false;
66+
});
67+
68+
if (is_admin || haveProjectPermission || haveServicePermission) {
69+
return true;
70+
}
71+
72+
return false;
73+
}
74+
return false;
75+
},
76+
[userOperationPermission, projectID]
77+
);
78+
79+
return {
80+
userOperationPermission,
81+
loading,
82+
updateUserOperationPermission,
83+
isHaveServicePermission
84+
};
85+
};
86+
87+
export default useUserOperationPermission;

packages/shared/lib/testUtil/mockHook/data.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {
22
GetUserAuthenticationTypeEnum,
3-
GetUserStatEnum
3+
GetUserStatEnum,
4+
OpPermissionItemOpPermissionTypeEnum,
5+
OpPermissionItemRangeTypeEnum
46
} from '../../api/base/service/common.enum';
57
import { OpPermissionTypeUid, SupportTheme, SystemRole } from '../../enum';
68
import DatabaseTypeLogo from '../../components/DatabaseTypeLogo';
@@ -131,3 +133,19 @@ export const mockUseProjectBusinessTipsData = {
131133
],
132134
isFixedBusiness: true
133135
};
136+
137+
export const mockUseUserOperationPermissionData = {
138+
userOperationPermission: {
139+
is_admin: true,
140+
op_permission_list: [
141+
{
142+
range_uids: ['700300'],
143+
range_type: OpPermissionItemRangeTypeEnum.project,
144+
op_permission_type: OpPermissionItemOpPermissionTypeEnum.project_admin
145+
}
146+
]
147+
},
148+
loading: false,
149+
updateUserOperationPermission: jest.fn(),
150+
isHaveServicePermission: jest.fn()
151+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import * as useUserOperationPermission from '../../global/useUserOperationPermission';
2+
3+
import { mockUseUserOperationPermissionData } from './data';
4+
5+
export const mockUseUserOperationPermission = (
6+
mockData?: Partial<typeof mockUseUserOperationPermissionData>
7+
) => {
8+
const spy = jest.spyOn(useUserOperationPermission, 'default');
9+
spy.mockImplementation(() => ({
10+
...mockUseUserOperationPermissionData,
11+
...mockData
12+
}));
13+
return spy;
14+
};

packages/sqle/src/page/SqlManagementConf/Common/ConfForm/DataSourceSelection/index.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ConfFormContext } from '../context';
1717
import { SqlManagementConfFormFields } from '../index.type';
1818
import { useSearchParams } from 'react-router-dom';
1919
import { filterOptionByLabel } from '@actiontech/shared/lib/components/BasicSelect/utils';
20+
import { getInstanceTipListV1FunctionalModuleEnum } from '@actiontech/shared/lib/api/sqle/service/instance/index.enum';
2021

2122
const DataSourceSelection: React.FC = () => {
2223
const { t } = useTranslation();
@@ -72,7 +73,9 @@ const DataSourceSelection: React.FC = () => {
7273
updateInstanceList({
7374
project_name: projectName,
7475
filter_db_type: instanceType,
75-
filter_by_business: business
76+
filter_by_business: business,
77+
functional_module:
78+
getInstanceTipListV1FunctionalModuleEnum.create_audit_plan
7679
});
7780
}
7881
};
@@ -83,7 +86,9 @@ const DataSourceSelection: React.FC = () => {
8386
updateInstanceList({
8487
project_name: projectName,
8588
filter_db_type: type,
86-
filter_by_business: businessScope
89+
filter_by_business: businessScope,
90+
functional_module:
91+
getInstanceTipListV1FunctionalModuleEnum.create_audit_plan
8792
});
8893
}
8994
};
@@ -98,7 +103,11 @@ const DataSourceSelection: React.FC = () => {
98103
const updateInstanceListByProjectName = useCallback(
99104
(id: string) => {
100105
updateInstanceList(
101-
{ project_name: projectName },
106+
{
107+
project_name: projectName,
108+
functional_module:
109+
getInstanceTipListV1FunctionalModuleEnum.create_audit_plan
110+
},
102111
{
103112
onSuccess: (list) => {
104113
const instance = list.find((v) => v.instance_id === id);

0 commit comments

Comments
 (0)