Skip to content

Commit e7ffb2e

Browse files
authored
Changes to remove tenant panels from roles pages when multitenancy is disabled (opensearch-project#2215)
* Changes to remove tenant panels from roles pages when multitenancy is disabled Signed-off-by: nishthm <[email protected]> * Modified test files as per comments on PR Signed-off-by: nishthm <[email protected]> --------- Signed-off-by: nishthm <[email protected]>
1 parent 1920e76 commit e7ffb2e

File tree

6 files changed

+474
-92
lines changed

6 files changed

+474
-92
lines changed

public/apps/configuration/panels/role-edit/role-edit.tsx

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import { NameRow } from '../../utils/name-row';
5959
import { DataSourceContext } from '../../app-router';
6060
import { SecurityPluginTopNavMenu } from '../../top-nav-menu';
6161
import { PageHeader } from '../../header/header-components';
62+
import { getDashboardsInfoSafe } from '../../../../utils/dashboards-info-utils';
6263

6364
interface RoleEditDeps extends BreadcrumbsPageDependencies {
6465
action: 'create' | 'edit' | 'duplicate';
@@ -145,6 +146,22 @@ export function RoleEdit(props: RoleEditDeps) {
145146
fetchTenantNames();
146147
}, [addToast, props.coreStart.http, dataSource]);
147148

149+
const [isMultiTenancyEnabled, setIsMultiTenancyEnabled] = useState(true);
150+
React.useEffect(() => {
151+
const fetchIsMultiTenancyEnabled = async () => {
152+
try {
153+
const dashboardsInfo = await getDashboardsInfoSafe(props.coreStart.http);
154+
setIsMultiTenancyEnabled(
155+
dashboardsInfo?.multitenancy_enabled && props.config.multitenancy.enabled
156+
);
157+
} catch (e) {
158+
console.error(e);
159+
}
160+
};
161+
162+
fetchIsMultiTenancyEnabled();
163+
}, [props.coreStart.http, props.config.multitenancy]);
164+
148165
const updateRoleHandler = async () => {
149166
try {
150167
// Remove index/tenant permissions with empty patterns.
@@ -309,12 +326,16 @@ export function RoleEdit(props: RoleEditDeps) {
309326
optionUniverse={indexWisePermissionOptions}
310327
/>
311328
<EuiSpacer size="m" />
312-
<TenantPanel
313-
state={roleTenantPermission}
314-
setState={setRoleTenantPermission}
315-
optionUniverse={tenantOptions}
316-
/>
317-
<EuiSpacer size="m" />
329+
{isMultiTenancyEnabled && (
330+
<>
331+
<TenantPanel
332+
state={roleTenantPermission}
333+
setState={setRoleTenantPermission}
334+
optionUniverse={tenantOptions}
335+
/>
336+
<EuiSpacer size="m" />
337+
</>
338+
)}
318339
<EuiFlexGroup justifyContent="flexEnd">
319340
<EuiFlexItem grow={false}>
320341
<EuiSmallButton

public/apps/configuration/panels/role-edit/test/role-edit.test.tsx

Lines changed: 103 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*/
1515

1616
import { EuiSmallButton } from '@elastic/eui';
17-
import { shallow } from 'enzyme';
17+
import { mount, shallow } from 'enzyme';
1818
import React from 'react';
1919
import { updateRole } from '../../../utils/role-detail-utils';
2020
import { setCrossPageToast } from '../../../utils/storage-utils';
@@ -23,6 +23,8 @@ import { ClusterPermissionPanel } from '../cluster-permission-panel';
2323
import { IndexPermissionPanel } from '../index-permission-panel';
2424
import { RoleEdit } from '../role-edit';
2525
import { TenantPanel } from '../tenant-panel';
26+
import { getDashboardsInfoSafe } from '../../../../../utils/dashboards-info-utils';
27+
import { act } from 'react-dom/test-utils';
2628

2729
jest.mock('../../../utils/role-detail-utils', () => ({
2830
getRoleDetail: jest.fn().mockReturnValue({
@@ -42,16 +44,21 @@ jest.mock('react', () => ({
4244
...jest.requireActual('react'),
4345
useContext: jest.fn().mockReturnValue({ dataSource: { id: 'test' }, setDataSource: jest.fn() }), // Mock the useContext hook to return dummy datasource and setdatasource function
4446
}));
47+
jest.mock('../../../../../utils/dashboards-info-utils');
4548

46-
describe('Role edit', () => {
47-
const sampleSourceRole = 'role';
48-
const mockCoreStart = {
49-
http: 1,
50-
uiSettings: {
51-
get: jest.fn().mockReturnValue(false),
52-
},
53-
};
49+
const sampleSourceRole = 'role';
50+
const mockCoreStart = {
51+
http: 1,
52+
uiSettings: {
53+
get: jest.fn().mockReturnValue(false),
54+
},
55+
chrome: {
56+
navGroup: { getNavGroupEnabled: jest.fn().mockReturnValue(false) },
57+
setBreadcrumbs: jest.fn(),
58+
},
59+
};
5460

61+
describe('Role edit', () => {
5562
const useEffect = jest.spyOn(React, 'useEffect');
5663
const useState = jest.spyOn(React, 'useState');
5764
// useEffect.mockImplementationOnce((f) => f());
@@ -135,3 +142,90 @@ describe('Role edit', () => {
135142
});
136143
});
137144
});
145+
146+
describe('Role Create/Edit base on Multitenancy', () => {
147+
const mockDepsStart = { navigation: { ui: { HeaderControl: {} } } };
148+
const actions = ['create', 'edit'] as const;
149+
describe.each(actions)('when action is %s', (action) => {
150+
const testScenarios = [
151+
{
152+
description: 'When multitenancy is disabled in config and enabled in dashboards',
153+
configEnabled: false,
154+
dashboardsEnabled: true,
155+
shouldRenderTenantPanel: false,
156+
},
157+
{
158+
description: 'When multitenancy is disabled in both config and dashboards',
159+
configEnabled: false,
160+
dashboardsEnabled: false,
161+
shouldRenderTenantPanel: false,
162+
},
163+
{
164+
description: 'When multitenancy is enabled in config and disabled in dashboards',
165+
configEnabled: true,
166+
dashboardsEnabled: false,
167+
shouldRenderTenantPanel: false,
168+
},
169+
{
170+
description: 'When multitenancy is enabled in both config and dashboards',
171+
configEnabled: true,
172+
dashboardsEnabled: true,
173+
shouldRenderTenantPanel: true,
174+
},
175+
] as const;
176+
177+
beforeEach(() => {
178+
jest.clearAllMocks();
179+
});
180+
181+
it.each(testScenarios)(
182+
'$description → should render TenantPanel: $shouldRenderTenantPanel',
183+
async function ({ configEnabled, dashboardsEnabled, shouldRenderTenantPanel }) {
184+
const mockConfig = { multitenancy: { enabled: configEnabled } };
185+
const mockDashboardsInfo = { multitenancy_enabled: dashboardsEnabled };
186+
(getDashboardsInfoSafe as jest.Mock).mockResolvedValue(mockDashboardsInfo);
187+
188+
let wrapper;
189+
await act(async () => {
190+
wrapper = mount(
191+
<RoleEdit
192+
action={action}
193+
sourceRoleName={sampleSourceRole}
194+
coreStart={mockCoreStart as any}
195+
depsStart={mockDepsStart as any}
196+
params={{} as any}
197+
config={mockConfig as any}
198+
/>
199+
);
200+
await new Promise((resolve) => setTimeout(resolve, 0));
201+
});
202+
wrapper.update();
203+
expect(wrapper.find(TenantPanel).exists()).toBe(shouldRenderTenantPanel);
204+
}
205+
);
206+
207+
it('should handle error when fetching dashboards info', async () => {
208+
(getDashboardsInfoSafe as jest.Mock).mockRejectedValue(new Error());
209+
210+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementationOnce(() => {});
211+
212+
let wrapper;
213+
await act(async () => {
214+
wrapper = mount(
215+
<RoleEdit
216+
action={action}
217+
sourceRoleName={sampleSourceRole}
218+
coreStart={mockCoreStart as any}
219+
depsStart={mockDepsStart as any}
220+
params={{} as any}
221+
config={{} as any}
222+
/>
223+
);
224+
await new Promise((resolve) => setTimeout(resolve, 0));
225+
});
226+
wrapper.update();
227+
expect(consoleErrorSpy).toHaveBeenCalled();
228+
consoleErrorSpy.mockRestore();
229+
});
230+
});
231+
});

public/apps/configuration/panels/role-list.tsx

Lines changed: 80 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -59,51 +59,59 @@ import { DataSourceContext } from '../app-router';
5959
import { SecurityPluginTopNavMenu } from '../top-nav-menu';
6060
import { AccessErrorComponent } from '../access-error-component';
6161
import { PageHeader } from '../header/header-components';
62+
import { getDashboardsInfoSafe } from '../../../utils/dashboards-info-utils';
6263

63-
const columns: Array<EuiBasicTableColumn<RoleListing>> = [
64-
{
65-
field: 'roleName',
66-
name: 'Role',
67-
render: (text: string) => (
68-
<a href={buildHashUrl(ResourceType.roles, Action.view, text)}>{text}</a>
69-
),
70-
sortable: true,
71-
},
72-
{
73-
field: 'clusterPermissions',
74-
name: 'Cluster permissions',
75-
render: truncatedListView(tableItemsUIProps),
76-
truncateText: true,
77-
},
78-
{
79-
field: 'indexPermissions',
80-
name: 'Index',
81-
render: truncatedListView(tableItemsUIProps),
82-
truncateText: true,
83-
},
84-
{
85-
field: 'internalUsers',
86-
name: 'Internal users',
87-
render: truncatedListView(tableItemsUIProps),
88-
},
89-
{
90-
field: 'backendRoles',
91-
name: 'Backend roles',
92-
render: truncatedListView(tableItemsUIProps),
93-
},
94-
{
95-
field: 'tenantPermissions',
96-
name: 'Tenants',
97-
render: truncatedListView(tableItemsUIProps),
98-
},
99-
{
100-
field: 'reserved',
101-
name: 'Customization',
102-
render: (reserved: boolean) => {
103-
return renderCustomization(reserved, tableItemsUIProps);
64+
const getColumnList = (multitenancyEnabled: boolean): Array<EuiBasicTableColumn<RoleListing>> => {
65+
const columns: Array<EuiBasicTableColumn<RoleListing>> = [
66+
{
67+
field: 'roleName',
68+
name: 'Role',
69+
render: (text: string) => (
70+
<a href={buildHashUrl(ResourceType.roles, Action.view, text)}>{text}</a>
71+
),
72+
sortable: true,
73+
},
74+
{
75+
field: 'clusterPermissions',
76+
name: 'Cluster permissions',
77+
render: truncatedListView(tableItemsUIProps),
78+
truncateText: true,
79+
},
80+
{
81+
field: 'indexPermissions',
82+
name: 'Index',
83+
render: truncatedListView(tableItemsUIProps),
84+
truncateText: true,
85+
},
86+
{
87+
field: 'internalUsers',
88+
name: 'Internal users',
89+
render: truncatedListView(tableItemsUIProps),
90+
},
91+
{
92+
field: 'backendRoles',
93+
name: 'Backend roles',
94+
render: truncatedListView(tableItemsUIProps),
95+
},
96+
...(multitenancyEnabled
97+
? [
98+
{
99+
field: 'tenantPermissions',
100+
name: 'Tenants',
101+
render: truncatedListView(tableItemsUIProps),
102+
},
103+
]
104+
: []),
105+
{
106+
field: 'reserved',
107+
name: 'Customization',
108+
render: (reserved: boolean) => {
109+
return renderCustomization(reserved, tableItemsUIProps);
110+
},
104111
},
105-
},
106-
];
112+
];
113+
return columns;
114+
};
107115

108116
export function RoleList(props: AppDependencies) {
109117
const [roleData, setRoleData] = React.useState<RoleListing[]>([]);
@@ -138,6 +146,22 @@ export function RoleList(props: AppDependencies) {
138146
fetchData();
139147
}, [props.coreStart.http, dataSource]);
140148

149+
const [isMultiTenancyEnabled, setIsMultiTenancyEnabled] = useState(true);
150+
React.useEffect(() => {
151+
const fetchIsMultiTenancyEnabled = async () => {
152+
try {
153+
const dashboardsInfo = await getDashboardsInfoSafe(props.coreStart.http);
154+
setIsMultiTenancyEnabled(
155+
Boolean(dashboardsInfo?.multitenancy_enabled && props.config.multitenancy.enabled)
156+
);
157+
} catch (e) {
158+
console.error(e);
159+
}
160+
};
161+
162+
fetchIsMultiTenancyEnabled();
163+
}, [props.coreStart.http, props.config.multitenancy]);
164+
141165
const handleDelete = async () => {
142166
const rolesToDelete: string[] = selection.map((r) => r.roleName);
143167
try {
@@ -235,13 +259,17 @@ export function RoleList(props: AppDependencies) {
235259
multiSelect: 'or',
236260
options: buildSearchFilterOptions(roleData, 'backendRoles'),
237261
},
238-
{
239-
type: 'field_value_selection',
240-
field: 'tenantPermissions',
241-
name: 'Tenants',
242-
multiSelect: 'or',
243-
options: buildSearchFilterOptions(roleData, 'tenantPermissions'),
244-
},
262+
...(isMultiTenancyEnabled
263+
? [
264+
{
265+
type: 'field_value_selection',
266+
field: 'tenantPermissions',
267+
name: 'Tenants',
268+
multiSelect: 'or',
269+
options: buildSearchFilterOptions(roleData, 'tenantPermissions'),
270+
},
271+
]
272+
: []),
245273
{
246274
type: 'field_value_selection',
247275
field: 'reserved',
@@ -260,7 +288,7 @@ export function RoleList(props: AppDependencies) {
260288
},
261289
],
262290
});
263-
}, [roleData]);
291+
}, [roleData, isMultiTenancyEnabled]);
264292

265293
const useUpdatedUX = props.coreStart.uiSettings.get('home:useNewHomePage');
266294
const buttonData = [
@@ -359,7 +387,7 @@ export function RoleList(props: AppDependencies) {
359387
data-test-subj="role-list"
360388
tableLayout={'auto'}
361389
loading={roleData === [] && !errorFlag}
362-
columns={columns}
390+
columns={getColumnList(isMultiTenancyEnabled)}
363391
items={roleData}
364392
itemId={'roleName'}
365393
pagination={true}

0 commit comments

Comments
 (0)