diff --git a/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx b/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx index 8e89d62a7..05f705e24 100644 --- a/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx +++ b/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx @@ -76,7 +76,7 @@ export function AuditLoggingEditSettings(props: AuditLoggingEditSettingProps) { }; fetchConfig(); - }, [props.coreStart.http, dataSource.id]); + }, [props.coreStart.http, dataSource]); const renderSaveAndCancel = () => { return ( diff --git a/public/apps/configuration/panels/audit-logging/audit-logging.tsx b/public/apps/configuration/panels/audit-logging/audit-logging.tsx index c89acdd12..1895d529c 100644 --- a/public/apps/configuration/panels/audit-logging/audit-logging.tsx +++ b/public/apps/configuration/panels/audit-logging/audit-logging.tsx @@ -30,6 +30,7 @@ import { } from '@elastic/eui'; import React, { useContext } from 'react'; import { FormattedMessage } from '@osd/i18n/react'; +import { DataSourceOption } from 'src/plugins/data_source_management/public'; import { AppDependencies } from '../../../types'; import { ResourceType } from '../../../../../common'; import { getAuditLogging, updateAuditLogging } from '../../utils/audit-logging-utils'; @@ -94,6 +95,18 @@ function renderStatusPanel(onSwitchChange: () => void, auditLoggingEnabled: bool ); } +function renderAccessErrorPanel(dataSource: DataSourceOption) { + return ( + + +

Audit logging

+
+ + +
+ ); +} + export function renderGeneralSettings(config: AuditLoggingSettings) { return ( <> @@ -167,13 +180,15 @@ export function AuditLogging(props: AuditLoggingProps) { }; fetchData(); - }, [props.coreStart.http, props.fromType, dataSource.id]); + }, [props.coreStart.http, props.fromType, dataSource]); const statusPanel = renderStatusPanel(onSwitchChange, configuration.enabled || false); let content; - if (!configuration.enabled) { + if (errorFlag) { + content = renderAccessErrorPanel(dataSource); + } else if (!configuration.enabled) { content = statusPanel; } else { content = ( @@ -241,11 +256,7 @@ export function AuditLogging(props: AuditLoggingProps) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {errorFlag ? ( - - ) : ( - content - )} + {content} ); } diff --git a/public/apps/configuration/panels/auth-view/auth-view.tsx b/public/apps/configuration/panels/auth-view/auth-view.tsx index bb01793a5..246fe9828 100644 --- a/public/apps/configuration/panels/auth-view/auth-view.tsx +++ b/public/apps/configuration/panels/auth-view/auth-view.tsx @@ -52,7 +52,7 @@ export function AuthView(props: AppDependencies) { }; fetchData(); - }, [props.coreStart.http, dataSource.id]); + }, [props.coreStart.http, dataSource]); if (isEmpty(authentication)) { return ( diff --git a/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx b/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx index 70ba39be8..986ff8e05 100644 --- a/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx +++ b/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx @@ -98,7 +98,7 @@ export function InternalUserEdit(props: InternalUserEditDeps) { fetchData(); } - }, [addToast, props.action, props.coreStart.http, props.sourceUserName, dataSource.id]); + }, [addToast, props.action, props.coreStart.http, props.sourceUserName, dataSource]); const updateUserHandler = async () => { try { diff --git a/public/apps/configuration/panels/permission-list/permission-list.tsx b/public/apps/configuration/panels/permission-list/permission-list.tsx index 41cf84a22..8d0fd6bb7 100644 --- a/public/apps/configuration/panels/permission-list/permission-list.tsx +++ b/public/apps/configuration/panels/permission-list/permission-list.tsx @@ -217,11 +217,11 @@ export function PermissionList(props: AppDependencies) { } finally { setLoading(false); } - }, [props.coreStart.http, dataSource.id]); + }, [props.coreStart.http, dataSource]); React.useEffect(() => { fetchData(); - }, [props.coreStart.http, fetchData, dataSource.id]); + }, [props.coreStart.http, fetchData, dataSource]); const handleDelete = async () => { const groupsToDelete: string[] = selection.map((r) => r.name); diff --git a/public/apps/configuration/panels/role-edit/role-edit.tsx b/public/apps/configuration/panels/role-edit/role-edit.tsx index 42fb8c2a9..384a2e95c 100644 --- a/public/apps/configuration/panels/role-edit/role-edit.tsx +++ b/public/apps/configuration/panels/role-edit/role-edit.tsx @@ -114,7 +114,7 @@ export function RoleEdit(props: RoleEditDeps) { fetchData(); } - }, [addToast, props.action, props.coreStart.http, props.sourceRoleName, dataSource.id]); + }, [addToast, props.action, props.coreStart.http, props.sourceRoleName, dataSource]); const [actionGroups, setActionGroups] = useState>([]); React.useEffect(() => { @@ -129,7 +129,7 @@ export function RoleEdit(props: RoleEditDeps) { }; fetchActionGroupNames(); - }, [addToast, props.coreStart.http, dataSource.id]); + }, [addToast, props.coreStart.http, dataSource]); const [tenantNames, setTenantNames] = React.useState([]); React.useEffect(() => { @@ -143,7 +143,7 @@ export function RoleEdit(props: RoleEditDeps) { }; fetchTenantNames(); - }, [addToast, props.coreStart.http, dataSource.id]); + }, [addToast, props.coreStart.http, dataSource]); const updateRoleHandler = async () => { try { diff --git a/public/apps/configuration/panels/role-list.tsx b/public/apps/configuration/panels/role-list.tsx index 2d7b46e5b..4b4ff9751 100644 --- a/public/apps/configuration/panels/role-list.tsx +++ b/public/apps/configuration/panels/role-list.tsx @@ -120,7 +120,6 @@ export function RoleList(props: AppDependencies) { setRoleData(processedData); setErrorFlag(false); } catch (e) { - console.log(e); setErrorFlag(true); } finally { setLoading(false); @@ -128,7 +127,7 @@ export function RoleList(props: AppDependencies) { }; fetchData(); - }, [props.coreStart.http, dataSource.id]); + }, [props.coreStart.http, dataSource]); const handleDelete = async () => { const rolesToDelete: string[] = selection.map((r) => r.roleName); diff --git a/public/apps/configuration/panels/role-mapping/role-edit-mapped-user.tsx b/public/apps/configuration/panels/role-mapping/role-edit-mapped-user.tsx index 8bfc51b01..65e384275 100644 --- a/public/apps/configuration/panels/role-mapping/role-edit-mapped-user.tsx +++ b/public/apps/configuration/panels/role-mapping/role-edit-mapped-user.tsx @@ -86,7 +86,7 @@ export function RoleEditMappedUser(props: RoleEditMappedUserProps) { }; fetchData(); - }, [addToast, props.coreStart.http, props.roleName, dataSource.id]); + }, [addToast, props.coreStart.http, props.roleName, dataSource]); React.useEffect(() => { const fetchInternalUserNames = async () => { @@ -101,7 +101,7 @@ export function RoleEditMappedUser(props: RoleEditMappedUserProps) { }; fetchInternalUserNames(); - }, [addToast, props.coreStart.http, dataSource.id]); + }, [addToast, props.coreStart.http, dataSource]); const internalUserOptions = userNames.map(stringToComboBoxOption); const updateRoleMappingHandler = async () => { diff --git a/public/apps/configuration/panels/role-view/role-view.tsx b/public/apps/configuration/panels/role-view/role-view.tsx index caac1bfd7..0b29c3469 100644 --- a/public/apps/configuration/panels/role-view/role-view.tsx +++ b/public/apps/configuration/panels/role-view/role-view.tsx @@ -147,7 +147,7 @@ export function RoleView(props: RoleViewProps) { }; fetchData(); - }, [addToast, props.coreStart.http, props.roleName, props.prevAction, dataSource.id]); + }, [addToast, props.coreStart.http, props.roleName, props.prevAction, dataSource]); const handleRoleMappingDelete = async () => { try { diff --git a/public/apps/configuration/panels/tenant-list/test/__snapshots__/tenant-list.test.tsx.snap b/public/apps/configuration/panels/tenant-list/test/__snapshots__/tenant-list.test.tsx.snap index c817212aa..47f814978 100644 --- a/public/apps/configuration/panels/tenant-list/test/__snapshots__/tenant-list.test.tsx.snap +++ b/public/apps/configuration/panels/tenant-list/test/__snapshots__/tenant-list.test.tsx.snap @@ -138,7 +138,7 @@ exports[`Tenant list Action menu click Duplicate click 1`] = ` }, ] } - error="Load data failed, please check console log for more detail." + error="" itemId="tenant" items={ Array [ @@ -326,7 +326,7 @@ exports[`Tenant list Action menu click Edit click 1`] = ` }, ] } - error="Load data failed, please check console log for more detail." + error="" itemId="tenant" items={ Array [ diff --git a/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx b/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx index fa72caf09..0805d51d5 100644 --- a/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx +++ b/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx @@ -75,12 +75,7 @@ describe('Tenant list', () => { }; beforeEach(() => { - // jest.spyOn(React, 'useState').mockImplementation((initialValue) => [initialValue, setState]); - jest.spyOn(React, 'useState').mockRestore(); - jest - .spyOn(React, 'useState') - .mockImplementationOnce(() => [false, jest.fn()]) - .mockImplementationOnce(() => ['', jest.fn()]); + jest.spyOn(React, 'useState').mockImplementation((initialValue) => [initialValue, setState]); }); it('Render empty', () => { @@ -272,7 +267,21 @@ describe('Tenant list', () => { }; it('edit and delete should be disabled when selected tenant is reserved', () => { - jest.spyOn(React, 'useState').mockImplementation(() => [[sampleReservedTenant], jest.fn()]); + jest.spyOn(React, 'useState').mockRestore(); + jest + .spyOn(React, 'useState') + .mockImplementationOnce(() => [[sampleReservedTenant], jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [[sampleReservedTenant], jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => [null, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [null, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]); const component = shallow( { }); it('All menues should be disabled when there is multiple tenant selected including reserved tenant', () => { + jest.spyOn(React, 'useState').mockRestore(); jest .spyOn(React, 'useState') - .mockImplementation(() => [[sampleReservedTenant, sampleCustomTenant1], jest.fn()]); + .mockImplementationOnce(() => [[sampleReservedTenant, sampleCustomTenant1], jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [[sampleReservedTenant, sampleReservedTenant], jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => [null, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [null, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]); const component = shallow( { expect(component.find('#delete').prop('disabled')).toBe(true); }); - it('All menues should be disabled except delete when there is multiple custom tenant selected', () => { + it('All menus should be disabled except delete when there is multiple custom tenant selected', () => { + jest.spyOn(React, 'useState').mockRestore(); jest .spyOn(React, 'useState') - .mockImplementation(() => [[sampleCustomTenant1, sampleCustomTenant2], jest.fn()]); + .mockImplementationOnce(() => [[sampleCustomTenant1, sampleCustomTenant2], jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [[sampleCustomTenant1, sampleCustomTenant2], jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => [null, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [null, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]); const component = shallow( { }; beforeEach(() => { - // jest.spyOn(React, 'useState').mockImplementation(() => [[sampleCustomTenant1], jest.fn()]); jest.spyOn(React, 'useState').mockRestore(); jest .spyOn(React, 'useState') @@ -370,11 +402,41 @@ describe('Tenant list', () => { }); it('Edit click', () => { + jest.spyOn(React, 'useState').mockRestore(); + jest + .spyOn(React, 'useState') + .mockImplementationOnce(() => [[sampleCustomTenant1], jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [[sampleCustomTenant1], jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => [null, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [null, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]); component.find('#edit').simulate('click'); expect(component).toMatchSnapshot(); }); it('Duplicate click', () => { + jest.spyOn(React, 'useState').mockRestore(); + jest + .spyOn(React, 'useState') + .mockImplementationOnce(() => [[sampleCustomTenant1], jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [[sampleCustomTenant1], jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => [null, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [null, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]) + .mockImplementationOnce(() => ['', jest.fn()]) + .mockImplementationOnce(() => [false, jest.fn()]); component.find('#duplicate').simulate('click'); expect(component).toMatchSnapshot(); }); diff --git a/public/apps/configuration/panels/user-list.tsx b/public/apps/configuration/panels/user-list.tsx index cf3f6c832..636edb0fd 100644 --- a/public/apps/configuration/panels/user-list.tsx +++ b/public/apps/configuration/panels/user-list.tsx @@ -129,7 +129,7 @@ export function UserList(props: AppDependencies) { }; fetchData(); - }, [props.coreStart.http, dataSource.id]); + }, [props.coreStart.http, dataSource]); const handleDelete = async () => { const usersToDelete: string[] = selection.map((r) => r.username); diff --git a/public/apps/configuration/top-nav-menu.tsx b/public/apps/configuration/top-nav-menu.tsx index 669a6d805..5e5e51893 100644 --- a/public/apps/configuration/top-nav-menu.tsx +++ b/public/apps/configuration/top-nav-menu.tsx @@ -54,14 +54,21 @@ export const SecurityPluginTopNavMenu = React.memo( savedObjects: coreStart.savedObjects.client, notifications: coreStart.notifications, activeOption: - selectedDataSource.id || selectedDataSource.label ? [selectedDataSource] : undefined, + selectedDataSource && (selectedDataSource.id || selectedDataSource.label) + ? [selectedDataSource] + : undefined, onSelectedDataSources: wrapSetDataSourceWithUpdateUrl, fullWidth: true, }} /> ) : null; }, - (prevProps, newProps) => - prevProps.selectedDataSource.id === newProps.selectedDataSource.id && - prevProps.dataSourcePickerReadOnly === newProps.dataSourcePickerReadOnly + (prevProps, newProps) => { + return ( + prevProps.selectedDataSource && + newProps.selectedDataSource && + prevProps.selectedDataSource.id === newProps.selectedDataSource.id && + prevProps.dataSourcePickerReadOnly === newProps.dataSourcePickerReadOnly + ); + } );