[ML] ML Ensure correct permissions applied to Settings Calendars/Filter Lists (#27346)

* Update calendar management permissions

* update filter lists permissions

* update calendars tests

* update settings and filterLists tests

* update filterList edit save button permission check

* Disable event delete in edit form if no permission

* Update tests for eventTable change
This commit is contained in:
Melissa Alvarez 2018-12-18 09:18:38 -07:00 committed by GitHub
parent 76b89a6ebe
commit ea3725a11f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 260 additions and 70 deletions

View file

@ -13,6 +13,8 @@ exports[`NewCalendar Renders new calendar form 1`] = `
>
<CalendarForm
calendarId=""
canCreateCalendar={true}
canDeleteCalendar={true}
description=""
eventsList={Array []}
groupIds={Array []}

View file

@ -51,9 +51,9 @@ exports[`CalendarForm Renders calendar form 1`] = `
>
<EuiComboBox
compressed={false}
disabled={false}
fullWidth={false}
isClearable={true}
isDisabled={false}
onChange={[MockFunction]}
options={Array []}
selectedOptions={Array []}
@ -68,9 +68,9 @@ exports[`CalendarForm Renders calendar form 1`] = `
>
<EuiComboBox
compressed={false}
disabled={false}
fullWidth={false}
isClearable={true}
isDisabled={false}
onChange={[MockFunction]}
onCreateOption={[MockFunction]}
options={Array []}
@ -88,6 +88,8 @@ exports[`CalendarForm Renders calendar form 1`] = `
label="Events"
>
<EventsTable
canCreateCalendar={true}
canDeleteCalendar={true}
eventsList={Array []}
onDeleteClick={[MockFunction]}
showImportModal={[MockFunction]}
@ -113,9 +115,10 @@ exports[`CalendarForm Renders calendar form 1`] = `
>
<EuiButton
color="primary"
disabled={true}
data-testid="ml_save_calendar_button"
fill={true}
iconSide="left"
isDisabled={true}
onClick={[MockFunction]}
type="button"
>
@ -128,10 +131,10 @@ exports[`CalendarForm Renders calendar form 1`] = `
>
<EuiButton
color="primary"
disabled={false}
fill={false}
href="undefined/app/ml#/settings/calendars_list"
iconSide="left"
isDisabled={false}
type="button"
>
Cancel

View file

@ -47,6 +47,8 @@ function EditHeader({
export function CalendarForm({
calendarId,
canCreateCalendar,
canDeleteCalendar,
description,
eventsList,
groupIds,
@ -71,6 +73,7 @@ export function CalendarForm({
must start and end with an alphanumeric character`;
const helpText = (isNewCalendarIdValid === true && !isEdit) ? msg : undefined;
const error = (isNewCalendarIdValid === false && !isEdit) ? [msg] : undefined;
const saveButtonDisabled = (canCreateCalendar === false || saving || !isNewCalendarIdValid || calendarId === '');
return (
<EuiForm>
@ -114,7 +117,7 @@ export function CalendarForm({
options={jobIds}
selectedOptions={selectedJobOptions}
onChange={onJobSelection}
disabled={saving === true}
isDisabled={saving === true || canCreateCalendar === false}
/>
</EuiFormRow>
@ -126,7 +129,7 @@ export function CalendarForm({
options={groupIds}
selectedOptions={selectedGroupOptions}
onChange={onGroupSelection}
disabled={saving === true}
isDisabled={saving === true || canCreateCalendar === false}
/>
</EuiFormRow>
@ -137,6 +140,8 @@ export function CalendarForm({
fullWidth
>
<EventsTable
canCreateCalendar={canCreateCalendar}
canDeleteCalendar={canDeleteCalendar}
eventsList={eventsList}
onDeleteClick={onEventDelete}
showImportModal={showImportModal}
@ -148,16 +153,17 @@ export function CalendarForm({
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiButton
data-testid="ml_save_calendar_button"
fill
onClick={isEdit ? onEdit : onCreate}
disabled={saving || !isNewCalendarIdValid || calendarId === ''}
isDisabled={saveButtonDisabled}
>
{saving ? 'Saving...' : 'Save'}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
disabled={saving}
isDisabled={saving}
href={`${chrome.getBasePath()}/app/ml#/settings/calendars_list`}
>
Cancel
@ -170,6 +176,8 @@ export function CalendarForm({
CalendarForm.propTypes = {
calendarId: PropTypes.string.isRequired,
canCreateCalendar: PropTypes.bool.isRequired,
canDeleteCalendar: PropTypes.bool.isRequired,
description: PropTypes.string.isRequired,
groupIds: PropTypes.array.isRequired,
isEdit: PropTypes.bool.isRequired,

View file

@ -17,6 +17,8 @@ import { CalendarForm } from './calendar_form';
const testProps = {
calendarId: '',
canCreateCalendar: true,
canDeleteCalendar: true,
description: '',
eventsList: [],
groupIds: [],

View file

@ -13,7 +13,7 @@ import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import { checkFullLicense } from '../../../license/check_license';
import { checkGetJobsPrivilege } from '../../../privilege/check_privilege';
import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege';
import { checkMlNodesAvailable } from '../../../ml_nodes_check/check_ml_nodes';
import { initPromise } from 'plugins/ml/util/promise';
import { getCreateCalendarBreadcrumbs, getEditCalendarBreadcrumbs } from '../../breadcrumbs';
@ -58,7 +58,9 @@ module.directive('mlNewCalendar', function ($route) {
scope: {},
link: function (scope, element) {
const props = {
calendarId: $route.current.params.calendarId
calendarId: $route.current.params.calendarId,
canCreateCalendar: checkPermission('canCreateCalendar'),
canDeleteCalendar: checkPermission('canDeleteCalendar')
};
ReactDOM.render(

View file

@ -135,6 +135,7 @@ exports[`EventsTable Renders events table with search bar 1`] = `
fill={false}
iconSide="left"
iconType="plusInCircle"
isDisabled={false}
onClick={[MockFunction]}
size="s"
type="button"
@ -147,6 +148,7 @@ exports[`EventsTable Renders events table with search bar 1`] = `
fill={false}
iconSide="left"
iconType="importAction"
isDisabled={false}
onClick={[MockFunction]}
size="s"
type="button"

View file

@ -19,13 +19,14 @@ import {
export const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
function DeleteButton({ onClick }) {
function DeleteButton({ onClick, canDeleteCalendar }) {
return (
<Fragment>
<EuiButtonEmpty
size="xs"
color="danger"
onClick={onClick}
isDisabled={canDeleteCalendar === false}
>
Delete
</EuiButtonEmpty>
@ -34,6 +35,8 @@ function DeleteButton({ onClick }) {
}
export function EventsTable({
canCreateCalendar,
canDeleteCalendar,
eventsList,
onDeleteClick,
showSearchBar,
@ -83,6 +86,7 @@ export function EventsTable({
render: (event) => (
<DeleteButton
data-testid="event_delete"
canDeleteCalendar={canDeleteCalendar}
onClick={() => { onDeleteClick(event.event_id); }}
/>
)
@ -92,6 +96,7 @@ export function EventsTable({
const search = {
toolsRight: [(
<EuiButton
isDisabled={canCreateCalendar === false}
key="ml_new_event"
data-testid="ml_new_event"
size="s"
@ -102,6 +107,7 @@ export function EventsTable({
</EuiButton>),
(
<EuiButton
isDisabled={canCreateCalendar === false}
key="ml_import_event"
data-testid="ml_import_events"
size="s"
@ -133,6 +139,8 @@ export function EventsTable({
}
EventsTable.propTypes = {
canCreateCalendar: PropTypes.bool,
canDeleteCalendar: PropTypes.bool,
eventsList: PropTypes.array.isRequired,
onDeleteClick: PropTypes.func.isRequired,
showImportModal: PropTypes.func,
@ -142,4 +150,6 @@ EventsTable.propTypes = {
EventsTable.defaultProps = {
showSearchBar: false,
canCreateCalendar: true,
canDeleteCalendar: true
};

View file

@ -16,6 +16,7 @@ import React from 'react';
import { EventsTable } from './events_table';
const testProps = {
canCreateCalendar: true,
eventsList: [{
calendar_id: 'test-calendar',
description: 'test description',

View file

@ -12,7 +12,8 @@ import { ImportModal } from './import_modal';
const testProps = {
addImportedEvents: jest.fn(),
closeImportModal: jest.fn()
closeImportModal: jest.fn(),
canCreateCalendar: true
};
const events = [{

View file

@ -24,6 +24,8 @@ exports[`ImportedEvents Renders imported events 1`] = `
grow={false}
>
<EventsTable
canCreateCalendar={true}
canDeleteCalendar={true}
eventsList={
Array [
Object {

View file

@ -27,6 +27,7 @@ const testProps = {
includePastEvents: false,
onCheckboxToggle: jest.fn(),
onEventDelete: jest.fn(),
canCreateCalendar: true,
};
describe('ImportedEvents', () => {

View file

@ -303,6 +303,8 @@ export class NewCalendar extends Component {
>
<CalendarForm
calendarId={selectedCalendar ? selectedCalendar.calendar_id : formCalendarId}
canCreateCalendar={this.props.canCreateCalendar}
canDeleteCalendar={this.props.canDeleteCalendar}
description={selectedCalendar ? selectedCalendar.description : description}
eventsList={events}
groupIds={groupIdOptions}
@ -332,4 +334,6 @@ export class NewCalendar extends Component {
NewCalendar.propTypes = {
calendarId: PropTypes.string,
canCreateCalendar: PropTypes.bool.isRequired,
canDeleteCalendar: PropTypes.bool.isRequired,
};

View file

@ -76,11 +76,16 @@ const calendars = [
}]
}];
const props = {
canCreateCalendar: true,
canDeleteCalendar: true
};
describe('NewCalendar', () => {
test('Renders new calendar form', () => {
const wrapper = shallow(
<NewCalendar />
<NewCalendar {...props}/>
);
expect(wrapper).toMatchSnapshot();
@ -88,7 +93,7 @@ describe('NewCalendar', () => {
test('Import modal shown on Import Events button click', () => {
const wrapper = mount(
<NewCalendar />
<NewCalendar {...props}/>
);
const importButton = wrapper.find('[data-testid="ml_import_events"]');
@ -100,7 +105,7 @@ describe('NewCalendar', () => {
test('New event modal shown on New event button click', () => {
const wrapper = mount(
<NewCalendar />
<NewCalendar {...props}/>
);
const importButton = wrapper.find('[data-testid="ml_new_event"]');
@ -112,7 +117,7 @@ describe('NewCalendar', () => {
test('isDuplicateId returns true if form calendar id already exists in calendars', () => {
const wrapper = mount(
<NewCalendar />
<NewCalendar {...props}/>
);
const instance = wrapper.instance();
@ -124,4 +129,20 @@ describe('NewCalendar', () => {
expect(instance.isDuplicateId()).toBe(true);
});
test('Save button is disabled if canCreateCalendar is false', () => {
const noCreateProps = {
...props,
canCreateCalendar: false,
};
const wrapper = mount(
<NewCalendar {...noCreateProps} />
);
const buttons = wrapper.find('[data-testid="ml_save_calendar_button"]');
const saveButton = buttons.find('EuiButton');
expect(saveButton.prop('isDisabled')).toBe(true);
});
});

View file

@ -9,6 +9,7 @@
import React, {
Component
} from 'react';
import { PropTypes } from 'prop-types';
import {
EuiConfirmModal,
@ -21,7 +22,6 @@ import {
import { CalendarsListTable } from './table/';
import { ml } from '../../../services/ml_api_service';
import { toastNotifications } from 'ui/notify';
import { checkPermission } from '../../../privilege/check_privilege';
import { mlNodesAvailable } from '../../../ml_nodes_check/check_ml_nodes';
import { deleteCalendars } from './delete_calendars';
@ -34,8 +34,6 @@ export class CalendarsList extends Component {
isDestroyModalVisible: false,
calendarId: null,
selectedForDeletion: [],
canCreateCalendar: checkPermission('canCreateCalendar'),
canDeleteCalendar: checkPermission('canDeleteCalendar'),
nodesAvailable: mlNodesAvailable()
};
}
@ -94,10 +92,9 @@ export class CalendarsList extends Component {
calendars,
selectedForDeletion,
loading,
canCreateCalendar,
canDeleteCalendar,
nodesAvailable
} = this.state;
const { canCreateCalendar, canDeleteCalendar } = this.props;
let destroyModal = '';
if (this.state.isDestroyModalVisible) {
@ -147,3 +144,8 @@ export class CalendarsList extends Component {
);
}
}
CalendarsListTable.propTypes = {
canCreateCalendar: PropTypes.bool.isRequired,
canDeleteCalendar: PropTypes.bool.isRequired,
};

View file

@ -66,17 +66,20 @@ const testingState = {
isDestroyModalVisible: false,
calendarId: null,
selectedForDeletion: [],
canCreateCalendar: true,
canDeleteCalendar: true,
nodesAvailable: true,
};
const props = {
canCreateCalendar: true,
canDeleteCalendar: true
};
describe('CalendarsList', () => {
test('loads calendars on mount', () => {
ml.calendars = jest.fn();
shallow(
<CalendarsList />
<CalendarsList {...props}/>
);
expect(ml.calendars).toHaveBeenCalled();
@ -84,7 +87,7 @@ describe('CalendarsList', () => {
test('Renders calendar list with calendars', () => {
const wrapper = shallow(
<CalendarsList />
<CalendarsList {...props}/>
);
wrapper.instance().setState(testingState);
@ -94,7 +97,7 @@ describe('CalendarsList', () => {
test('Sets selected calendars list on checkbox change', () => {
const wrapper = mount(
<CalendarsList />
<CalendarsList {...props}/>
);
const instance = wrapper.instance();

View file

@ -13,7 +13,7 @@ import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import { checkFullLicense } from '../../../license/check_license';
import { checkGetJobsPrivilege } from '../../../privilege/check_privilege';
import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege';
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
import { initPromise } from '../../../util/promise';
import { getCalendarManagementBreadcrumbs } from '../../breadcrumbs';
@ -48,8 +48,13 @@ module.directive('mlCalendarsList', function () {
replace: false,
scope: {},
link: function (scope, element) {
const props = {
canCreateCalendar: checkPermission('canCreateCalendar'),
canDeleteCalendar: checkPermission('canDeleteCalendar'),
};
ReactDOM.render(
React.createElement(CalendarsList),
React.createElement(CalendarsList, props),
element[0]
);
}

View file

@ -10,6 +10,7 @@ exports[`AddItemPopover calls addItems with multiple items on clicking Add butto
fill={false}
iconSide="right"
iconType="arrowDown"
isDisabled={false}
onClick={[Function]}
size="s"
type="button"
@ -88,6 +89,7 @@ exports[`AddItemPopover opens the popover onButtonClick 1`] = `
fill={false}
iconSide="right"
iconType="arrowDown"
isDisabled={false}
onClick={[Function]}
size="s"
type="button"
@ -166,6 +168,7 @@ exports[`AddItemPopover renders the popover 1`] = `
fill={false}
iconSide="right"
iconType="arrowDown"
isDisabled={false}
onClick={[Function]}
size="s"
type="button"

View file

@ -80,6 +80,7 @@ export class AddItemPopover extends Component {
iconType="arrowDown"
iconSide="right"
onClick={this.onButtonClick}
isDisabled={this.props.canCreateFilter === false}
>
Add item
</EuiButton>
@ -125,6 +126,7 @@ export class AddItemPopover extends Component {
}
}
AddItemPopover.propTypes = {
addItems: PropTypes.func.isRequired
addItems: PropTypes.func.isRequired,
canCreateFilter: PropTypes.bool.isRequired
};

View file

@ -13,7 +13,8 @@ import { AddItemPopover } from './add_item_popover';
function prepareTest(addItemsFn) {
const props = {
addItems: addItemsFn
addItems: addItemsFn,
canCreateFilter: true
};
const wrapper = shallow(

View file

@ -7,7 +7,7 @@ exports[`DeleteFilterListModal false canDeleteFilter privilege renders as disabl
fill={false}
iconSide="left"
iconType="trash"
isDisabled={true}
isDisabled={false}
key="delete_filter_list"
onClick={[Function]}
type="button"
@ -41,7 +41,7 @@ exports[`DeleteFilterListModal renders as disabled delete button when no lists s
fill={false}
iconSide="left"
iconType="trash"
isDisabled={true}
isDisabled={false}
key="delete_filter_list"
onClick={[Function]}
type="button"

View file

@ -16,7 +16,6 @@ import {
EUI_MODAL_CONFIRM_BUTTON,
} from '@elastic/eui';
import { checkPermission } from '../../../../privilege/check_privilege';
import { deleteFilterLists } from './delete_filter_lists';
/*
@ -29,7 +28,6 @@ export class DeleteFilterListModal extends Component {
this.state = {
isModalVisible: false
};
this.canDeleteFilter = checkPermission('canDeleteFilter');
}
closeModal = () => {
@ -53,7 +51,7 @@ export class DeleteFilterListModal extends Component {
}
render() {
const { selectedFilterLists } = this.props;
const { selectedFilterLists, canDeleteFilter } = this.props;
let modal;
if (this.state.isModalVisible) {
@ -87,7 +85,9 @@ export class DeleteFilterListModal extends Component {
iconType="trash"
color="danger"
onClick={this.showModal}
isDisabled={(selectedFilterLists === undefined || selectedFilterLists.length === 0 || this.canDeleteFilter === false)}
isDisabled={(selectedFilterLists === undefined ||
selectedFilterLists.length === 0 ||
canDeleteFilter === false)}
>
Delete
</EuiButton>
@ -98,6 +98,7 @@ export class DeleteFilterListModal extends Component {
}
}
DeleteFilterListModal.propTypes = {
canDeleteFilter: PropTypes.bool.isRequired,
selectedFilterLists: PropTypes.array,
};

View file

@ -25,14 +25,15 @@ const testSelectedLists = [
];
const testProps = {
selectedFilterLists: testSelectedLists
selectedFilterLists: testSelectedLists,
canDeleteFilter: true
};
describe('DeleteFilterListModal', () => {
test('renders as disabled delete button when no lists selected', () => {
const component = shallow(
<DeleteFilterListModal />
<DeleteFilterListModal {...testProps}/>
);
expect(component).toMatchSnapshot();

View file

@ -10,6 +10,7 @@ exports[`FilterListUsagePopover opens the popover onButtonClick 1`] = `
color="primary"
iconSize="m"
iconType="pencil"
isDisabled={false}
onClick={[Function]}
size="s"
type="button"
@ -61,6 +62,7 @@ exports[`FilterListUsagePopover renders the popover with a description 1`] = `
color="primary"
iconSize="m"
iconType="pencil"
isDisabled={false}
onClick={[Function]}
size="s"
type="button"
@ -112,6 +114,7 @@ exports[`FilterListUsagePopover renders the popover with no description 1`] = `
color="primary"
iconSize="m"
iconType="pencil"
isDisabled={false}
onClick={[Function]}
size="s"
type="button"

View file

@ -69,6 +69,7 @@ export class EditDescriptionPopover extends Component {
onClick={this.onButtonClick}
iconType="pencil"
aria-label="Edit description"
isDisabled={this.props.canCreateFilter === false}
/>
);
@ -100,6 +101,7 @@ export class EditDescriptionPopover extends Component {
}
}
EditDescriptionPopover.propTypes = {
canCreateFilter: PropTypes.bool.isRequired,
description: PropTypes.string,
updateDescription: PropTypes.func.isRequired
};

View file

@ -14,7 +14,8 @@ function prepareTest(updateDescriptionFn) {
const props = {
description: 'A list of known safe domains',
updateDescription: updateDescriptionFn
updateDescription: updateDescriptionFn,
canCreateFilter: true
};
const wrapper = shallow(
@ -30,7 +31,8 @@ describe('FilterListUsagePopover', () => {
const updateDescription = jest.fn(() => {});
const props = {
updateDescription
updateDescription,
canCreateFilter: true
};
const component = shallow(

View file

@ -12,6 +12,7 @@ exports[`EditFilterList adds new items to filter list 1`] = `
verticalPosition="center"
>
<EditFilterListHeader
canCreateFilter={true}
description="List of known safe domains"
isNewFilterIdInvalid={false}
newFilterId=""
@ -31,6 +32,8 @@ exports[`EditFilterList adds new items to filter list 1`] = `
/>
<EditFilterListToolbar
addItems={[Function]}
canCreateFilter={true}
canDeleteFilter={true}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
@ -120,6 +123,7 @@ exports[`EditFilterList renders after selecting an item and deleting it 1`] = `
verticalPosition="center"
>
<EditFilterListHeader
canCreateFilter={true}
description="List of known safe domains"
isNewFilterIdInvalid={false}
newFilterId=""
@ -139,6 +143,8 @@ exports[`EditFilterList renders after selecting an item and deleting it 1`] = `
/>
<EditFilterListToolbar
addItems={[Function]}
canCreateFilter={true}
canDeleteFilter={true}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={1}
@ -230,6 +236,7 @@ exports[`EditFilterList renders after selecting an item and deleting it 2`] = `
verticalPosition="center"
>
<EditFilterListHeader
canCreateFilter={true}
description="List of known safe domains"
isNewFilterIdInvalid={false}
newFilterId=""
@ -249,6 +256,8 @@ exports[`EditFilterList renders after selecting an item and deleting it 2`] = `
/>
<EditFilterListToolbar
addItems={[Function]}
canCreateFilter={true}
canDeleteFilter={true}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
@ -335,6 +344,7 @@ exports[`EditFilterList renders the edit page for a new filter list and updates
verticalPosition="center"
>
<EditFilterListHeader
canCreateFilter={true}
description=""
isNewFilterIdInvalid={true}
newFilterId=""
@ -344,6 +354,8 @@ exports[`EditFilterList renders the edit page for a new filter list and updates
/>
<EditFilterListToolbar
addItems={[Function]}
canCreateFilter={true}
canDeleteFilter={true}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
@ -424,6 +436,7 @@ exports[`EditFilterList renders the edit page for a new filter list and updates
verticalPosition="center"
>
<EditFilterListHeader
canCreateFilter={true}
description=""
isNewFilterIdInvalid={false}
newFilterId="new_filter_list"
@ -433,6 +446,8 @@ exports[`EditFilterList renders the edit page for a new filter list and updates
/>
<EditFilterListToolbar
addItems={[Function]}
canCreateFilter={true}
canDeleteFilter={true}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
@ -513,6 +528,7 @@ exports[`EditFilterList renders the edit page for an existing filter list and up
verticalPosition="center"
>
<EditFilterListHeader
canCreateFilter={true}
description="List of known safe domains"
isNewFilterIdInvalid={false}
newFilterId=""
@ -532,6 +548,8 @@ exports[`EditFilterList renders the edit page for an existing filter list and up
/>
<EditFilterListToolbar
addItems={[Function]}
canCreateFilter={true}
canDeleteFilter={true}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
@ -619,6 +637,7 @@ exports[`EditFilterList renders the edit page for an existing filter list and up
verticalPosition="center"
>
<EditFilterListHeader
canCreateFilter={true}
description="Known safe web domains"
isNewFilterIdInvalid={false}
newFilterId=""
@ -638,6 +657,8 @@ exports[`EditFilterList renders the edit page for an existing filter list and up
/>
<EditFilterListToolbar
addItems={[Function]}
canCreateFilter={true}
canDeleteFilter={true}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
@ -725,6 +746,7 @@ exports[`EditFilterList updates the items per page 1`] = `
verticalPosition="center"
>
<EditFilterListHeader
canCreateFilter={true}
description="List of known safe domains"
isNewFilterIdInvalid={false}
newFilterId=""
@ -744,6 +766,8 @@ exports[`EditFilterList updates the items per page 1`] = `
/>
<EditFilterListToolbar
addItems={[Function]}
canCreateFilter={true}
canDeleteFilter={true}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}

View file

@ -104,6 +104,7 @@ exports[`EditFilterListHeader renders the header when creating a new filter list
grow={false}
>
<EditDescriptionPopover
canCreateFilter={true}
description="A test filter list"
updateDescription={[MockFunction]}
/>
@ -226,6 +227,7 @@ exports[`EditFilterListHeader renders the header when creating a new filter list
grow={false}
>
<EditDescriptionPopover
canCreateFilter={true}
updateDescription={[MockFunction]}
/>
</EuiFlexItem>
@ -325,6 +327,7 @@ exports[`EditFilterListHeader renders the header when editing an existing unused
grow={false}
>
<EditDescriptionPopover
canCreateFilter={true}
updateDescription={[MockFunction]}
/>
</EuiFlexItem>
@ -434,6 +437,7 @@ exports[`EditFilterListHeader renders the header when editing an existing used f
grow={false}
>
<EditDescriptionPopover
canCreateFilter={true}
description="A test filter list"
updateDescription={[MockFunction]}
/>

View file

@ -17,6 +17,7 @@ exports[`EditFilterListToolbar renders the toolbar with no items selected 1`] =
>
<AddItemPopover
addItems={[MockFunction]}
canCreateFilter={true}
/>
</EuiFlexItem>
</EuiFlexGroup>
@ -73,6 +74,7 @@ exports[`EditFilterListToolbar renders the toolbar with one item selected 1`] =
>
<AddItemPopover
addItems={[MockFunction]}
canCreateFilter={true}
/>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -14,7 +14,7 @@ const module = uiModules.get('apps/ml', ['react']);
import { getCreateFilterListBreadcrumbs, getEditFilterListBreadcrumbs } from '../../breadcrumbs';
import { checkFullLicense } from 'plugins/ml/license/check_license';
import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
import { checkGetJobsPrivilege, checkPermission } from 'plugins/ml/privilege/check_privilege';
import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
import { initPromise } from 'plugins/ml/util/promise';
@ -59,7 +59,9 @@ module.directive('mlEditFilterList', function ($route) {
scope: {},
link: function (scope, element) {
const props = {
filterId: $route.current.params.filterId
filterId: $route.current.params.filterId,
canCreateFilter: checkPermission('canCreateFilter'),
canDeleteFilter: checkPermission('canDeleteFilter'),
};
ReactDOM.render(

View file

@ -275,6 +275,7 @@ export class EditFilterList extends Component {
itemsPerPage,
activePage,
saveInProgress } = this.state;
const { canCreateFilter, canDeleteFilter } = this.props;
const totalItemCount = (items !== undefined) ? items.length : 0;
@ -286,6 +287,7 @@ export class EditFilterList extends Component {
horizontalPosition="center"
>
<EditFilterListHeader
canCreateFilter={canCreateFilter}
filterId={this.props.filterId}
newFilterId={newFilterId}
isNewFilterIdInvalid={isNewFilterIdInvalid}
@ -296,6 +298,8 @@ export class EditFilterList extends Component {
usedBy={loadedFilter.used_by}
/>
<EditFilterListToolbar
canCreateFilter={canCreateFilter}
canDeleteFilter={canDeleteFilter}
onSearchChange={this.onSearchChange}
addItems={this.addItems}
deleteSelectedItems={this.deleteSelectedItems}
@ -323,7 +327,10 @@ export class EditFilterList extends Component {
<EuiFlexItem grow={false}>
<EuiButton
onClick={this.save}
disabled={(saveInProgress === true) || (isNewFilterIdInvalid === true)}
disabled={(saveInProgress === true) ||
(isNewFilterIdInvalid === true) ||
(canCreateFilter === false)
}
fill
>
Save
@ -336,6 +343,8 @@ export class EditFilterList extends Component {
}
}
EditFilterList.propTypes = {
filterId: PropTypes.string
filterId: PropTypes.string,
canCreateFilter: PropTypes.bool.isRequired,
canDeleteFilter: PropTypes.bool.isRequired
};

View file

@ -38,10 +38,15 @@ import React from 'react';
import { EditFilterList } from './edit_filter_list';
const props = {
canCreateFilter: true,
canDeleteFilter: true
};
function prepareEditTest() {
const wrapper = shallow(
<EditFilterList />
<EditFilterList {...props}/>
);
// Cannot find a way to generate the snapshot after the Promise in the mock ml.filters
@ -59,7 +64,7 @@ describe('EditFilterList', () => {
test('renders the edit page for a new filter list and updates ID', () => {
const wrapper = shallow(
<EditFilterList />
<EditFilterList {...props}/>
);
expect(wrapper).toMatchSnapshot();

View file

@ -28,6 +28,7 @@ import { EditDescriptionPopover } from '../components/edit_description_popover';
import { FilterListUsagePopover } from '../components/filter_list_usage_popover';
export function EditFilterListHeader({
canCreateFilter,
filterId,
totalItemCount,
description,
@ -147,6 +148,7 @@ export function EditFilterListHeader({
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EditDescriptionPopover
canCreateFilter={canCreateFilter}
description={description}
updateDescription={updateDescription}
/>
@ -159,6 +161,7 @@ export function EditFilterListHeader({
}
EditFilterListHeader.propTypes = {
canCreateFilter: PropTypes.bool.isRequired,
filterId: PropTypes.string,
newFilterId: PropTypes.string,
isNewFilterIdInvalid: PropTypes.bool,

View file

@ -18,6 +18,8 @@ describe('EditFilterListHeader', () => {
const requiredProps = {
updateNewFilterId,
updateDescription,
canCreateFilter: true,
canDeleteFilter: true,
};
test('renders the header when creating a new filter list with the ID not set', () => {

View file

@ -23,6 +23,8 @@ import {
import { AddItemPopover } from '../components/add_item_popover';
export function EditFilterListToolbar({
canCreateFilter,
canDeleteFilter,
onSearchChange,
addItems,
deleteSelectedItems,
@ -34,6 +36,7 @@ export function EditFilterListToolbar({
<EuiFlexItem grow={false}>
<AddItemPopover
addItems={addItems}
canCreateFilter={canCreateFilter}
/>
</EuiFlexItem>
</EuiFlexGroup>
@ -41,7 +44,7 @@ export function EditFilterListToolbar({
<EuiFlexItem grow={false}>
<EuiButton
color="danger"
disabled={(selectedItemCount === 0)}
disabled={(selectedItemCount === 0 || canDeleteFilter === false)}
onClick={deleteSelectedItems}
>
Delete item
@ -58,6 +61,8 @@ export function EditFilterListToolbar({
);
}
EditFilterListToolbar.propTypes = {
canCreateFilter: PropTypes.bool.isRequired,
canDeleteFilter: PropTypes.bool.isRequired,
onSearchChange: PropTypes.func.isRequired,
addItems: PropTypes.func.isRequired,
deleteSelectedItems: PropTypes.func.isRequired,

View file

@ -20,6 +20,8 @@ describe('EditFilterListToolbar', () => {
onSearchChange,
addItems,
deleteSelectedItems,
canCreateFilter: true,
canDeleteFilter: true
};
test('renders the toolbar with no items selected', () => {

View file

@ -16,6 +16,8 @@ exports[`Filter Lists renders a list of filters 1`] = `
totalCount={1}
/>
<FilterListsTable
canCreateFilter={true}
canDeleteFilter={true}
filterLists={
Array [
Object {

View file

@ -64,8 +64,11 @@ exports[`Filter Lists Table renders with filter lists and selection supplied 1`]
},
"filters": Array [],
"toolsRight": Array [
<NewFilterButton />,
<NewFilterButton
canCreateFilter={true}
/>,
<DeleteFilterListModal
canDeleteFilter={true}
refreshFilterLists={[MockFunction]}
selectedFilterLists={
Array [
@ -169,8 +172,11 @@ exports[`Filter Lists Table renders with filter lists supplied 1`] = `
},
"filters": Array [],
"toolsRight": Array [
<NewFilterButton />,
<NewFilterButton
canCreateFilter={true}
/>,
<DeleteFilterListModal
canDeleteFilter={true}
refreshFilterLists={[MockFunction]}
/>,
],

View file

@ -14,7 +14,7 @@ const module = uiModules.get('apps/ml', ['react']);
import { getFilterListsBreadcrumbs } from '../../breadcrumbs';
import { checkFullLicense } from 'plugins/ml/license/check_license';
import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
import { checkGetJobsPrivilege, checkPermission } from 'plugins/ml/privilege/check_privilege';
import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
import { initPromise } from 'plugins/ml/util/promise';
@ -48,8 +48,13 @@ module.directive('mlFilterLists', function () {
replace: false,
scope: {},
link: function (scope, element) {
const props = {
canCreateFilter: checkPermission('canCreateFilter'),
canDeleteFilter: checkPermission('canDeleteFilter'),
};
ReactDOM.render(
React.createElement(FilterLists),
React.createElement(FilterLists, props),
element[0]
);
}

View file

@ -12,6 +12,7 @@
import React, {
Component
} from 'react';
import { PropTypes } from 'prop-types';
import {
EuiPage,
@ -72,6 +73,7 @@ export class FilterLists extends Component {
render() {
const { filterLists, selectedFilterLists } = this.state;
const { canCreateFilter, canDeleteFilter } = this.props;
return (
<EuiPage className="ml-list-filter-lists">
@ -85,6 +87,8 @@ export class FilterLists extends Component {
refreshFilterLists={this.refreshFilterLists}
/>
<FilterListsTable
canCreateFilter={canCreateFilter}
canDeleteFilter={canDeleteFilter}
filterLists={filterLists}
selectedFilterLists={selectedFilterLists}
setSelectedFilterLists={this.setSelectedFilterLists}
@ -96,3 +100,7 @@ export class FilterLists extends Component {
}
}
FilterLists.propTypes = {
canCreateFilter: PropTypes.bool.isRequired,
canDeleteFilter: PropTypes.bool.isRequired
};

View file

@ -33,12 +33,17 @@ import React from 'react';
import { FilterLists } from './filter_lists';
const props = {
canCreateFilter: true,
canDeleteFilter: true
};
describe('Filter Lists', () => {
test('renders a list of filters', () => {
const wrapper = shallow(
<FilterLists />
<FilterLists {...props}/>
);
// Cannot find a way to generate the snapshot after the Promise in the mock ml.filters

View file

@ -24,7 +24,6 @@ import {
} from '@elastic/eui';
import chrome from 'ui/chrome';
import { checkPermission } from '../../../privilege/check_privilege';
import { DeleteFilterListModal } from '../components/delete_filter_list_modal';
@ -44,8 +43,7 @@ UsedByIcon.propTypes = {
usedBy: PropTypes.object
};
function NewFilterButton() {
const canCreateFilter = checkPermission('canCreateFilter');
function NewFilterButton({ canCreateFilter }) {
return (
<EuiButton
key="new_filter_list"
@ -95,15 +93,22 @@ function getColumns() {
return columns;
}
function renderToolsRight(selectedFilterLists, refreshFilterLists) {
function renderToolsRight(
canCreateFilter,
canDeleteFilter,
selectedFilterLists,
refreshFilterLists
) {
return [
(
<NewFilterButton
key="new_filter_list"
canCreateFilter={canCreateFilter}
/>
),
(
<DeleteFilterListModal
canDeleteFilter={canDeleteFilter}
selectedFilterLists={selectedFilterLists}
refreshFilterLists={refreshFilterLists}
/>
@ -112,6 +117,8 @@ function renderToolsRight(selectedFilterLists, refreshFilterLists) {
export function FilterListsTable({
canCreateFilter,
canDeleteFilter,
filterLists,
selectedFilterLists,
setSelectedFilterLists,
@ -126,7 +133,12 @@ export function FilterListsTable({
};
const search = {
toolsRight: renderToolsRight(selectedFilterLists, refreshFilterLists),
toolsRight: renderToolsRight(
canCreateFilter,
canDeleteFilter,
selectedFilterLists,
refreshFilterLists
),
box: {
incremental: true,
},
@ -177,6 +189,8 @@ export function FilterListsTable({
}
FilterListsTable.propTypes = {
canCreateFilter: PropTypes.bool.isRequired,
canDeleteFilter: PropTypes.bool.isRequired,
filterLists: PropTypes.array,
selectedFilterLists: PropTypes.array,
setSelectedFilterLists: PropTypes.func.isRequired,

View file

@ -25,6 +25,8 @@ describe('Filter Lists Table', () => {
const requiredProps = {
setSelectedFilterLists,
refreshFilterLists,
canCreateFilter: true,
canDeleteFilter: true
};
const testFilterLists = [

View file

@ -23,7 +23,10 @@ import {
import chrome from 'ui/chrome';
export function Settings({ canCreateFilter, canCreateCalendar }) {
export function Settings({
canGetFilters,
canGetCalendars
}) {
return (
<EuiPage className="mlSettingsPage">
<EuiPageBody className="mlSettingsPage__body">
@ -44,7 +47,7 @@ export function Settings({ canCreateFilter, canCreateCalendar }) {
size="l"
color="primary"
href={`${chrome.getBasePath()}/app/ml#/settings/calendars_list`}
isDisabled={canCreateCalendar === false}
isDisabled={canGetCalendars === false}
>
Calendar management
</EuiButtonEmpty>
@ -56,7 +59,7 @@ export function Settings({ canCreateFilter, canCreateCalendar }) {
size="l"
color="primary"
href={`${chrome.getBasePath()}/app/ml#/settings/filter_lists`}
isDisabled={canCreateFilter === false}
isDisabled={canGetFilters === false}
>
Filter Lists
</EuiButtonEmpty>
@ -70,6 +73,6 @@ export function Settings({ canCreateFilter, canCreateCalendar }) {
}
Settings.propTypes = {
canCreateFilter: PropTypes.bool.isRequired,
canCreateCalendar: PropTypes.bool.isRequired,
canGetFilters: PropTypes.bool.isRequired,
canGetCalendars: PropTypes.bool.isRequired,
};

View file

@ -20,15 +20,15 @@ describe('Settings', () => {
test('Renders settings page', () => {
const wrapper = shallow(
<Settings canCreateFilter={true} canCreateCalendar={true}/>
<Settings canGetFilters={true} canGetCalendars={true}/>
);
expect(wrapper).toMatchSnapshot();
});
test('Filter Lists button disabled if canCreateFilter is false', () => {
test('Filter Lists button disabled if canGetFilters is false', () => {
const wrapper = mount(
<Settings canCreateFilter={false} canCreateCalendar={true}/>
<Settings canGetFilters={false} canGetCalendars={true}/>
);
const button = wrapper.find('[data-testid="ml_filter_lists_button"]');
@ -36,9 +36,9 @@ describe('Settings', () => {
expect(filterButton.prop('isDisabled')).toBe(true);
});
test('Calendar management button disabled if canCreateCalendar is false', () => {
test('Calendar management button disabled if canGetCalendars is false', () => {
const wrapper = mount(
<Settings canCreateFilter={true} canCreateCalendar={false} />
<Settings canGetFilters={true} canGetCalendars={false} />
);
const button = wrapper.find('[data-testid="ml_calendar_mng_button"]');

View file

@ -48,8 +48,8 @@ module.directive('mlSettings', function () {
timefilter.disableTimeRangeSelector(); // remove time picker from top of page
timefilter.disableAutoRefreshSelector(); // remove time picker from top of page
const canCreateFilter = checkPermission('canCreateFilter');
const canCreateCalendar = checkPermission('canCreateCalendar');
const canGetFilters = checkPermission('canGetFilters');
const canGetCalendars = checkPermission('canGetCalendars');
return {
restrict: 'E',
@ -57,7 +57,10 @@ module.directive('mlSettings', function () {
scope: {},
link: function (scope, element) {
ReactDOM.render(
React.createElement(Settings, { canCreateFilter, canCreateCalendar }),
React.createElement(Settings, {
canGetFilters,
canGetCalendars
}),
element[0]
);
}