mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
Makes items routable on the Manage Hosts Sidebar (#592)
* Makes items routable on the Manage Hosts Sidebar * slugify active label
This commit is contained in:
parent
3aad263e6d
commit
0753510cc3
@ -1,8 +1,18 @@
|
||||
import { pick } from 'lodash';
|
||||
import { kebabCase, pick } from 'lodash';
|
||||
|
||||
const ORG_INFO_ATTRS = ['org_name', 'org_logo_url'];
|
||||
const ADMIN_ATTRS = ['email', 'name', 'password', 'password_confirmation', 'username'];
|
||||
|
||||
const labelSlug = (label) => {
|
||||
const { display_text: displayText } = label;
|
||||
|
||||
if (!displayText) return undefined;
|
||||
|
||||
const lowerDisplayText = displayText.toLowerCase();
|
||||
|
||||
return kebabCase(lowerDisplayText);
|
||||
};
|
||||
|
||||
const setupData = (formData) => {
|
||||
const orgInfo = pick(formData, ORG_INFO_ATTRS);
|
||||
const adminInfo = pick(formData, ADMIN_ATTRS);
|
||||
@ -19,4 +29,4 @@ const setupData = (formData) => {
|
||||
};
|
||||
};
|
||||
|
||||
export default { setupData };
|
||||
export default { labelSlug, setupData };
|
||||
|
@ -3,6 +3,13 @@ import expect from 'expect';
|
||||
import helpers from 'kolide/helpers';
|
||||
|
||||
describe('Kolide API - helpers', () => {
|
||||
describe('#labelSlug', () => {
|
||||
it('creates a slug for the label', () => {
|
||||
expect(helpers.labelSlug({ display_text: 'All Hosts' })).toEqual('all-hosts');
|
||||
expect(helpers.labelSlug({ display_text: 'windows' })).toEqual('windows');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setupData', () => {
|
||||
const formData = {
|
||||
email: 'hi@gnar.dog',
|
||||
|
@ -9,8 +9,11 @@ class Kolide extends Base {
|
||||
|
||||
return this.authenticatedPost(this.endpoint(LABELS), JSON.stringify({ description, name, query }))
|
||||
.then((response) => {
|
||||
const { label } = response;
|
||||
|
||||
return {
|
||||
...response.label,
|
||||
...label,
|
||||
slug: helpers.labelSlug(label),
|
||||
type: 'custom',
|
||||
};
|
||||
});
|
||||
@ -124,18 +127,24 @@ class Kolide extends Base {
|
||||
|
||||
return this.authenticatedGet(this.endpoint(LABELS))
|
||||
.then((response) => {
|
||||
const labelTypeForDisplayText = {
|
||||
'All Hosts': 'all',
|
||||
'MS Windows': 'platform',
|
||||
'CentOS Linux': 'platform',
|
||||
'Mac OS X': 'platform',
|
||||
'Ubuntu Linux': 'platform',
|
||||
};
|
||||
const labels = response.labels.map((label) => {
|
||||
return { ...label, type: 'custom' };
|
||||
return {
|
||||
...label,
|
||||
slug: helpers.labelSlug(label),
|
||||
type: labelTypeForDisplayText[label.display_text] || 'custom',
|
||||
};
|
||||
});
|
||||
const stubbedLabels = [
|
||||
{ id: 100, display_text: 'All Hosts', type: 'all', count: 22 },
|
||||
{ id: 40, display_text: 'ONLINE', type: 'status', count: 20 },
|
||||
{ id: 50, display_text: 'OFFLINE', type: 'status', count: 2 },
|
||||
{ id: 55, display_text: 'MIA', description: '(offline > 30 days)', type: 'status', count: 3 },
|
||||
{ id: 60, display_text: 'macOS', type: 'platform', count: 1 },
|
||||
{ id: 70, display_text: 'Windows', type: 'platform', count: 1 },
|
||||
{ id: 80, display_text: 'Ubuntu', type: 'platform', count: 10 },
|
||||
{ id: 90, display_text: 'Centos', type: 'platform', count: 10 },
|
||||
{ id: 40, display_text: 'ONLINE', slug: 'online', type: 'status', count: 20 },
|
||||
{ id: 50, display_text: 'OFFLINE', slug: 'offline', type: 'status', count: 2 },
|
||||
{ id: 55, display_text: 'MIA', description: '(offline > 30 days)', slug: 'mia', type: 'status', count: 3 },
|
||||
];
|
||||
|
||||
return labels.concat(stubbedLabels);
|
||||
|
@ -50,7 +50,12 @@ describe('Kolide - API client', () => {
|
||||
Kolide.createLabel(labelParams)
|
||||
.then((labelResponse) => {
|
||||
expect(request.isDone()).toEqual(true);
|
||||
expect(labelResponse).toEqual({ ...labelParams, type: 'custom' });
|
||||
expect(labelResponse).toEqual({
|
||||
...labelParams,
|
||||
display_text: name,
|
||||
slug: 'label-name',
|
||||
type: 'custom',
|
||||
});
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import AceEditor from 'react-ace';
|
||||
import { connect } from 'react-redux';
|
||||
import { filter } from 'lodash';
|
||||
import { push } from 'react-router-redux';
|
||||
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
|
||||
|
||||
@ -15,12 +14,13 @@ import HostSidePanel from 'components/side_panels/HostSidePanel';
|
||||
import HostsTable from 'components/hosts/HostsTable';
|
||||
import Icon from 'components/Icon';
|
||||
import osqueryTableInterface from 'interfaces/osquery_table';
|
||||
import paths from 'router/paths';
|
||||
import QueryComposer from 'components/queries/QueryComposer';
|
||||
import QuerySidePanel from 'components/side_panels/QuerySidePanel';
|
||||
import { renderFlash } from 'redux/nodes/notifications/actions';
|
||||
import Rocker from 'components/buttons/Rocker';
|
||||
import { selectOsqueryTable } from 'redux/nodes/components/QueryPages/actions';
|
||||
import { setDisplay, setSelectedLabel } from 'redux/nodes/components/ManageHostsPage/actions';
|
||||
import { setDisplay } from 'redux/nodes/components/ManageHostsPage/actions';
|
||||
import { showRightSidePanel, removeRightSidePanel } from 'redux/nodes/app/actions';
|
||||
import validateQuery from 'components/forms/validators/validate_query';
|
||||
|
||||
@ -54,9 +54,7 @@ export class ManageHostsPage extends Component {
|
||||
dispatch,
|
||||
hosts,
|
||||
labels,
|
||||
selectedLabel,
|
||||
} = this.props;
|
||||
const allHostLabel = filter(labels, { type: 'all' })[0];
|
||||
|
||||
dispatch(showRightSidePanel);
|
||||
|
||||
@ -68,21 +66,6 @@ export class ManageHostsPage extends Component {
|
||||
dispatch(labelActions.loadAll());
|
||||
}
|
||||
|
||||
if (!selectedLabel) {
|
||||
dispatch(setSelectedLabel(allHostLabel));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
const { dispatch, labels, selectedLabel } = nextProps;
|
||||
const allHostLabel = filter(labels, { type: 'all' })[0];
|
||||
|
||||
if (!selectedLabel && !!allHostLabel) {
|
||||
dispatch(setSelectedLabel(allHostLabel));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -126,8 +109,11 @@ export class ManageHostsPage extends Component {
|
||||
evt.preventDefault();
|
||||
|
||||
const { dispatch } = this.props;
|
||||
const { MANAGE_HOSTS } = paths;
|
||||
const { slug } = selectedLabel;
|
||||
const nextLocation = slug === 'all-hosts' ? MANAGE_HOSTS : `${MANAGE_HOSTS}/${slug}`;
|
||||
|
||||
dispatch(setSelectedLabel(selectedLabel));
|
||||
dispatch(push(nextLocation));
|
||||
|
||||
return false;
|
||||
};
|
||||
@ -334,11 +320,17 @@ export class ManageHostsPage extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, { location }) => {
|
||||
const { display, selectedLabel } = state.components.ManageHostsPage;
|
||||
const mapStateToProps = (state, { location, params }) => {
|
||||
const activeLabelSlug = params.active_label || 'all-hosts';
|
||||
const { display } = state.components.ManageHostsPage;
|
||||
const { entities: hosts } = entityGetter(state).get('hosts');
|
||||
const { entities: labels } = entityGetter(state).get('labels');
|
||||
const labelEntities = entityGetter(state).get('labels');
|
||||
const { entities: labels } = labelEntities;
|
||||
const isAddLabel = location.hash === NEW_LABEL_HASH;
|
||||
const selectedLabel = labelEntities.findBy(
|
||||
{ slug: activeLabelSlug },
|
||||
{ ignoreCase: true },
|
||||
);
|
||||
const { selectedOsqueryTable } = state.components.QueryPages;
|
||||
|
||||
return {
|
||||
|
@ -21,6 +21,8 @@ const host = {
|
||||
uptime: 3600000000000,
|
||||
uuid: '1234-5678-9101',
|
||||
};
|
||||
const allHostsLabel = { id: 1, display_text: 'All Hosts', slug: 'all-hosts', type: 'all', count: 22 };
|
||||
const windowsLabel = { id: 2, display_text: 'Windows', slug: 'windows', type: 'platform', count: 22 };
|
||||
const mockStore = reduxMockStore({
|
||||
components: {
|
||||
ManageHostsPage: {
|
||||
@ -31,6 +33,16 @@ const mockStore = reduxMockStore({
|
||||
selectedOsqueryTable: stubbedOsqueryTable,
|
||||
},
|
||||
},
|
||||
entities: {
|
||||
labels: {
|
||||
data: {
|
||||
1: allHostsLabel,
|
||||
2: windowsLabel,
|
||||
3: { id: 3, display_text: 'Ubuntu', slug: 'ubuntu', type: 'platform', count: 22 },
|
||||
4: { id: 4, display_text: 'ONLINE', slug: 'online', type: 'status', count: 22 },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
describe('ManageHostsPage - component', () => {
|
||||
@ -54,7 +66,7 @@ describe('ManageHostsPage - component', () => {
|
||||
});
|
||||
|
||||
it('renders a QuerySidePanel when adding a new label', () => {
|
||||
const ownProps = { location: { hash: '#new_label' } };
|
||||
const ownProps = { location: { hash: '#new_label' }, params: {} };
|
||||
const component = connectedComponent(ConnectedManageHostsPage, { props: ownProps, mockStore });
|
||||
const page = mount(component);
|
||||
|
||||
@ -76,7 +88,7 @@ describe('ManageHostsPage - component', () => {
|
||||
});
|
||||
|
||||
it('toggles between displays', () => {
|
||||
const ownProps = { location: {} };
|
||||
const ownProps = { location: {}, params: {} };
|
||||
const component = connectedComponent(ConnectedManageHostsPage, { props: ownProps, mockStore });
|
||||
const page = mount(component);
|
||||
const button = page.find('Rocker').find('input');
|
||||
@ -97,7 +109,7 @@ describe('ManageHostsPage - component', () => {
|
||||
beforeEach(() => createAceSpy());
|
||||
afterEach(restoreSpies);
|
||||
|
||||
const ownProps = { location: { hash: '#new_label' } };
|
||||
const ownProps = { location: { hash: '#new_label' }, params: {} };
|
||||
const component = connectedComponent(ConnectedManageHostsPage, { props: ownProps, mockStore });
|
||||
|
||||
it('renders a QueryComposer component', () => {
|
||||
@ -112,4 +124,29 @@ describe('ManageHostsPage - component', () => {
|
||||
expect(page.find('QueryComposer').text()).toInclude('New Label Query');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Active label', () => {
|
||||
beforeEach(() => createAceSpy());
|
||||
afterEach(restoreSpies);
|
||||
|
||||
it('Displays the all hosts label as the active label by default', () => {
|
||||
const ownProps = { location: {}, params: {} };
|
||||
const component = connectedComponent(ConnectedManageHostsPage, { props: ownProps, mockStore });
|
||||
const page = mount(component);
|
||||
|
||||
expect(page.find('HostSidePanel').props()).toInclude({
|
||||
selectedLabel: allHostsLabel,
|
||||
});
|
||||
});
|
||||
|
||||
it('Displays the windows label as the active label', () => {
|
||||
const ownProps = { location: {}, params: { active_label: 'windows' } };
|
||||
const component = connectedComponent(ConnectedManageHostsPage, { props: ownProps, mockStore });
|
||||
const page = mount(component);
|
||||
|
||||
expect(page.find('HostSidePanel').props()).toInclude({
|
||||
selectedLabel: windowsLabel,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -8,17 +8,4 @@ export const setDisplay = (display) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const SET_SELECTED_LABEL = 'SET_SELECTED_LABEL';
|
||||
export const setSelectedLabel = (selectedLabel) => {
|
||||
return {
|
||||
type: SET_SELECTED_LABEL,
|
||||
payload: {
|
||||
selectedLabel,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
setDisplay,
|
||||
setSelectedLabel,
|
||||
};
|
||||
export default { setDisplay };
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { SET_DISPLAY, SET_SELECTED_LABEL } from './actions';
|
||||
import { SET_DISPLAY } from './actions';
|
||||
|
||||
export const initialState = {
|
||||
display: 'Grid',
|
||||
selectedLabel: null,
|
||||
};
|
||||
|
||||
export default (state = initialState, { type, payload }) => {
|
||||
@ -12,11 +11,6 @@ export default (state = initialState, { type, payload }) => {
|
||||
...state,
|
||||
display: payload.display,
|
||||
};
|
||||
case SET_SELECTED_LABEL:
|
||||
return {
|
||||
...state,
|
||||
selectedLabel: payload.selectedLabel,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ const routes = (
|
||||
</Route>
|
||||
<Route path="hosts">
|
||||
<Route path="new" component={NewHostPage} />
|
||||
<Route path="manage" component={ManageHostsPage} />
|
||||
<Route path="manage(/:active_label)" component={ManageHostsPage} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Route>
|
||||
|
@ -5,6 +5,7 @@ export default {
|
||||
HOME: '/',
|
||||
LOGIN: '/login',
|
||||
LOGOUT: '/logout',
|
||||
MANAGE_HOSTS: '/hosts/manage',
|
||||
NEW_PACK: '/packs/new',
|
||||
RESET_PASSWORD: '/login/reset',
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ export const validCreateLabelRequest = (bearerToken, labelParams) => {
|
||||
},
|
||||
})
|
||||
.post('/api/v1/kolide/labels', JSON.stringify(labelParams))
|
||||
.reply(201, { label: labelParams });
|
||||
.reply(201, { label: { ...labelParams, display_text: labelParams.name } });
|
||||
};
|
||||
|
||||
export const validCreateQueryRequest = (bearerToken, queryParams) => {
|
||||
|
@ -56,7 +56,7 @@
|
||||
"react-ace": "^3.6.0",
|
||||
"react-addons-css-transition-group": "^15.3.2",
|
||||
"react-dom": "^15.3.2",
|
||||
"react-entity-getter": "0.0.2",
|
||||
"react-entity-getter": "0.0.5",
|
||||
"react-redux": "^4.4.5",
|
||||
"react-router": "^2.7.0",
|
||||
"react-router-redux": "^4.0.5",
|
||||
|
Loading…
Reference in New Issue
Block a user