mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 17:05:18 +00:00
Add delete button to custom Host Labels (#1014)
This commit is contained in:
parent
2b55cf3acf
commit
32c51d2be7
@ -43,6 +43,15 @@ class Kolide extends Base {
|
||||
},
|
||||
}
|
||||
|
||||
labels = {
|
||||
destroy: (label) => {
|
||||
const { LABELS } = endpoints;
|
||||
const endpoint = this.endpoint(`${LABELS}/${label.id}`);
|
||||
|
||||
return this.authenticatedDelete(endpoint);
|
||||
},
|
||||
};
|
||||
|
||||
createLabel = ({ description, name, query }) => {
|
||||
const { LABELS } = endpoints;
|
||||
|
||||
|
@ -4,7 +4,7 @@ import nock from 'nock';
|
||||
import Kolide from 'kolide';
|
||||
import helpers from 'kolide/helpers';
|
||||
import mocks from 'test/mocks';
|
||||
import { configOptionStub, hostStub, packStub, queryStub, userStub } from 'test/stubs';
|
||||
import { configOptionStub, hostStub, packStub, queryStub, userStub, labelStub } from 'test/stubs';
|
||||
|
||||
const {
|
||||
invalidForgotPasswordRequest,
|
||||
@ -13,6 +13,7 @@ const {
|
||||
validCreatePackRequest,
|
||||
validCreateQueryRequest,
|
||||
validCreateScheduledQueryRequest,
|
||||
validDestroyLabelRequest,
|
||||
validDestroyQueryRequest,
|
||||
validDestroyPackRequest,
|
||||
validDestroyScheduledQueryRequest,
|
||||
@ -72,9 +73,10 @@ describe('Kolide - API client', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createLabel', () => {
|
||||
it('calls the appropriate endpoint with the correct parameters', (done) => {
|
||||
const bearerToken = 'valid-bearer-token';
|
||||
describe('labels', () => {
|
||||
const bearerToken = 'valid-bearer-token';
|
||||
|
||||
it('#createLabel', (done) => {
|
||||
const description = 'label description';
|
||||
const name = 'label name';
|
||||
const query = 'SELECT * FROM users';
|
||||
@ -95,6 +97,20 @@ describe('Kolide - API client', () => {
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('#destroyLabel', (done) => {
|
||||
const request = validDestroyLabelRequest(bearerToken, labelStub);
|
||||
|
||||
Kolide.setBearerToken(bearerToken);
|
||||
Kolide.labels.destroy(labelStub)
|
||||
.then(() => {
|
||||
expect(request.isDone()).toEqual(true);
|
||||
done();
|
||||
})
|
||||
.catch(() => {
|
||||
throw new Error('Request should have been stubbed');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('configOptions', () => {
|
||||
|
@ -21,7 +21,10 @@ import paths from 'router/paths';
|
||||
import QueryForm from 'components/forms/queries/QueryForm';
|
||||
import QuerySidePanel from 'components/side_panels/QuerySidePanel';
|
||||
import Rocker from 'components/buttons/Rocker';
|
||||
import Button from 'components/buttons/Button';
|
||||
import Modal from 'components/modals/Modal';
|
||||
import { selectOsqueryTable } from 'redux/nodes/components/QueryPages/actions';
|
||||
import { renderFlash } from 'redux/nodes/notifications/actions';
|
||||
import statusLabelsInterface from 'interfaces/status_labels';
|
||||
import iconClassForLabel from 'utilities/icon_class_for_label';
|
||||
import platformIconClass from 'utilities/platform_icon_class';
|
||||
@ -53,6 +56,7 @@ export class ManageHostsPage extends Component {
|
||||
|
||||
this.state = {
|
||||
labelQueryText: '',
|
||||
showDeleteModal: false,
|
||||
};
|
||||
}
|
||||
|
||||
@ -137,6 +141,27 @@ export class ManageHostsPage extends Component {
|
||||
return false;
|
||||
}
|
||||
|
||||
onDeleteLabel = () => {
|
||||
const { toggleModal } = this;
|
||||
const { dispatch, selectedLabel } = this.props;
|
||||
const { MANAGE_HOSTS } = paths;
|
||||
|
||||
return dispatch(labelActions.destroy(selectedLabel))
|
||||
.then(() => {
|
||||
toggleModal();
|
||||
dispatch(push(MANAGE_HOSTS));
|
||||
dispatch(renderFlash('success', 'Label successfully deleted'));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
toggleModal = () => {
|
||||
const { showDeleteModal } = this.state;
|
||||
|
||||
this.setState({ showDeleteModal: !showDeleteModal });
|
||||
return false;
|
||||
}
|
||||
|
||||
filterHosts = () => {
|
||||
const { hosts, selectedLabel } = this.props;
|
||||
|
||||
@ -150,6 +175,44 @@ export class ManageHostsPage extends Component {
|
||||
return orderedHosts;
|
||||
}
|
||||
|
||||
renderModal = () => {
|
||||
const { showDeleteModal } = this.state;
|
||||
const { toggleModal, onDeleteLabel } = this;
|
||||
|
||||
if (!showDeleteModal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Delete Label"
|
||||
onExit={toggleModal}
|
||||
className={`${baseClass}__modal`}
|
||||
>
|
||||
<p>Are you sure you wish to delete this label?</p>
|
||||
<div>
|
||||
<Button onClick={toggleModal} variant="inverse">Cancel</Button>
|
||||
<Button onClick={onDeleteLabel} variant="alert">Delete</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderDeleteButton = () => {
|
||||
const { toggleModal } = this;
|
||||
const { selectedLabel: { type } } = this.props;
|
||||
|
||||
if (type !== 'custom') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${baseClass}__delete-label`}>
|
||||
<Button onClick={toggleModal} variant="alert">Delete</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderIcon = () => {
|
||||
const { selectedLabel } = this.props;
|
||||
|
||||
@ -188,7 +251,7 @@ export class ManageHostsPage extends Component {
|
||||
}
|
||||
|
||||
renderHeader = () => {
|
||||
const { renderIcon, renderQuery } = this;
|
||||
const { renderIcon, renderQuery, renderDeleteButton } = this;
|
||||
const { display, isAddLabel, selectedLabel, statusLabels } = this.props;
|
||||
|
||||
if (!selectedLabel || isAddLabel) {
|
||||
@ -209,6 +272,8 @@ export class ManageHostsPage extends Component {
|
||||
|
||||
return (
|
||||
<div className={`${baseClass}__header`}>
|
||||
{renderDeleteButton()}
|
||||
|
||||
<h1 className={`${baseClass}__title`}>
|
||||
{renderIcon()}
|
||||
<span>{displayText}</span>
|
||||
@ -327,7 +392,7 @@ export class ManageHostsPage extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { renderForm, renderHeader, renderHosts, renderSidePanel } = this;
|
||||
const { renderForm, renderHeader, renderHosts, renderSidePanel, renderModal } = this;
|
||||
const { display, isAddLabel } = this.props;
|
||||
|
||||
return (
|
||||
@ -343,6 +408,7 @@ export class ManageHostsPage extends Component {
|
||||
}
|
||||
|
||||
{renderSidePanel()}
|
||||
{renderModal()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import expect, { restoreSpies } from 'expect';
|
||||
import expect, { spyOn, restoreSpies } from 'expect';
|
||||
import { mount } from 'enzyme';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
import labelActions from 'redux/nodes/entities/labels/actions';
|
||||
import ConnectedManageHostsPage, { ManageHostsPage } from 'pages/hosts/ManageHostsPage/ManageHostsPage';
|
||||
import { connectedComponent, createAceSpy, reduxMockStore, stubbedOsqueryTable } from 'test/helpers';
|
||||
import { hostStub } from 'test/stubs';
|
||||
@ -11,6 +12,7 @@ const allHostsLabel = { id: 1, display_text: 'All Hosts', slug: 'all-hosts', typ
|
||||
const windowsLabel = { id: 2, display_text: 'Windows', slug: 'windows', type: 'platform', count: 22 };
|
||||
const offlineHost = { ...hostStub, id: 111, status: 'offline' };
|
||||
const offlineHostsLabel = { id: 5, display_text: 'OFFLINE', slug: 'offline', status: 'offline', type: 'status', count: 1 };
|
||||
const customLabel = { id: 6, display_text: 'Custom Label', slug: 'custom-label', type: 'custom', count: 3 };
|
||||
const mockStore = reduxMockStore({
|
||||
components: {
|
||||
ManageHostsPage: {
|
||||
@ -36,6 +38,7 @@ const mockStore = reduxMockStore({
|
||||
3: { id: 3, display_text: 'Ubuntu', slug: 'ubuntu', type: 'platform', count: 22 },
|
||||
4: { id: 4, display_text: 'ONLINE', slug: 'online', type: 'status', count: 22 },
|
||||
5: offlineHostsLabel,
|
||||
6: customLabel,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -182,4 +185,28 @@ describe('ManageHostsPage - component', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Delete a label', () => {
|
||||
it('Deleted label after confirmation modal', () => {
|
||||
const ownProps = { location: {}, params: { active_label: 'custom-label' } };
|
||||
const component = connectedComponent(ConnectedManageHostsPage, { props: ownProps, mockStore });
|
||||
const page = mount(component);
|
||||
const deleteBtn = page.find('.manage-hosts__delete-label').find('button');
|
||||
|
||||
spyOn(labelActions, 'destroy').andCallThrough();
|
||||
|
||||
expect(page.find('Modal').length).toEqual(0);
|
||||
|
||||
deleteBtn.simulate('click');
|
||||
|
||||
const confirmModal = page.find('Modal');
|
||||
|
||||
expect(confirmModal.length).toEqual(1);
|
||||
|
||||
const confirmBtn = confirmModal.find('.button--alert');
|
||||
confirmBtn.simulate('click');
|
||||
|
||||
expect(labelActions.destroy).toHaveBeenCalledWith(customLabel);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -27,6 +27,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__delete-label {
|
||||
float: right;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
&__description {
|
||||
line-height: 1.54;
|
||||
letter-spacing: 0.5px;
|
||||
|
@ -6,6 +6,7 @@ const { LABELS: schema } = schemas;
|
||||
|
||||
export default reduxConfig({
|
||||
createFunc: Kolide.createLabel,
|
||||
destroyFunc: Kolide.labels.destroy,
|
||||
entityName: 'labels',
|
||||
loadAllFunc: Kolide.getLabels,
|
||||
parseEntityFunc: (label) => {
|
||||
|
@ -64,6 +64,16 @@ export const validCreateScheduledQueryRequest = (bearerToken, formData) => {
|
||||
.reply(201, { scheduled_query: scheduledQueryStub });
|
||||
};
|
||||
|
||||
export const validDestroyLabelRequest = (bearerToken, label) => {
|
||||
return nock('http://localhost:8080', {
|
||||
reqHeaders: {
|
||||
Authorization: `Bearer ${bearerToken}`,
|
||||
},
|
||||
})
|
||||
.delete(`/api/v1/kolide/labels/${label.id}`)
|
||||
.reply(200, {});
|
||||
};
|
||||
|
||||
export const validDestroyQueryRequest = (bearerToken, query) => {
|
||||
return nock('http://localhost:8080', {
|
||||
reqHeaders: {
|
||||
@ -398,6 +408,7 @@ export default {
|
||||
validCreatePackRequest,
|
||||
validCreateQueryRequest,
|
||||
validCreateScheduledQueryRequest,
|
||||
validDestroyLabelRequest,
|
||||
validDestroyQueryRequest,
|
||||
validDestroyPackRequest,
|
||||
validDestroyScheduledQueryRequest,
|
||||
|
Loading…
Reference in New Issue
Block a user