зеркало из https://github.com/mozilla/fleet.git
756 status labels (#967)
* API client to get status label summary * Handle status label counts in state * Display status counts in hosts side panel
This commit is contained in:
Родитель
066ec298b5
Коммит
630ba45448
|
@ -6,6 +6,7 @@ import InputField from 'components/forms/fields/InputField';
|
|||
import labelInterface from 'interfaces/label';
|
||||
import PanelGroup from 'components/side_panels/HostSidePanel/PanelGroup';
|
||||
import SecondarySidePanelContainer from 'components/side_panels/SecondarySidePanelContainer';
|
||||
import statusLabelsInterface from 'interfaces/status_labels';
|
||||
|
||||
const baseClass = 'host-side-panel';
|
||||
|
||||
|
@ -15,6 +16,7 @@ class HostSidePanel extends Component {
|
|||
onAddLabelClick: PropTypes.func,
|
||||
onLabelClick: PropTypes.func,
|
||||
selectedLabel: labelInterface,
|
||||
statusLabels: statusLabelsInterface,
|
||||
};
|
||||
|
||||
constructor (props) {
|
||||
|
@ -31,7 +33,7 @@ class HostSidePanel extends Component {
|
|||
}
|
||||
|
||||
render () {
|
||||
const { labels, onAddLabelClick, onLabelClick, selectedLabel } = this.props;
|
||||
const { labels, onAddLabelClick, onLabelClick, selectedLabel, statusLabels } = this.props;
|
||||
const { labelFilter } = this.state;
|
||||
const { onFilterLabels } = this;
|
||||
const allHostLabels = filter(labels, { type: 'all' });
|
||||
|
@ -56,6 +58,7 @@ class HostSidePanel extends Component {
|
|||
<PanelGroup
|
||||
groupItems={hostStatusLabels}
|
||||
onLabelClick={onLabelClick}
|
||||
statusLabels={statusLabels}
|
||||
selectedLabel={selectedLabel}
|
||||
type="status"
|
||||
/>
|
||||
|
|
|
@ -2,13 +2,15 @@ import React, { Component, PropTypes } from 'react';
|
|||
import { isEqual, noop } from 'lodash';
|
||||
|
||||
import labelInterface from 'interfaces/label';
|
||||
import PanelGroupItem from '../PanelGroupItem';
|
||||
import PanelGroupItem from 'components/side_panels/HostSidePanel/PanelGroupItem';
|
||||
import statusLabelsInterface from 'interfaces/status_labels';
|
||||
|
||||
class PanelGroup extends Component {
|
||||
static propTypes = {
|
||||
groupItems: PropTypes.arrayOf(labelInterface),
|
||||
onLabelClick: PropTypes.func,
|
||||
selectedLabel: labelInterface,
|
||||
statusLabels: statusLabelsInterface,
|
||||
type: PropTypes.string,
|
||||
};
|
||||
|
||||
|
@ -20,6 +22,7 @@ class PanelGroup extends Component {
|
|||
const {
|
||||
onLabelClick,
|
||||
selectedLabel,
|
||||
statusLabels,
|
||||
type,
|
||||
} = this.props;
|
||||
const selected = isEqual(selectedLabel, item);
|
||||
|
@ -30,6 +33,7 @@ class PanelGroup extends Component {
|
|||
item={item}
|
||||
key={item.display_text}
|
||||
onLabelClick={onLabelClick(item)}
|
||||
statusLabels={statusLabels}
|
||||
type={type}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -4,6 +4,7 @@ import classnames from 'classnames';
|
|||
import Icon from 'components/icons/Icon';
|
||||
import iconClassForLabel from 'utilities/icon_class_for_label';
|
||||
import PlatformIcon from 'components/icons/PlatformIcon';
|
||||
import statusLabelsInterface from 'interfaces/status_labels';
|
||||
|
||||
const baseClass = 'panel-group-item';
|
||||
|
||||
|
@ -17,9 +18,24 @@ class PanelGroupItem extends Component {
|
|||
}).isRequired,
|
||||
onLabelClick: PropTypes.func,
|
||||
isSelected: PropTypes.bool,
|
||||
statusLabels: statusLabelsInterface,
|
||||
type: PropTypes.string,
|
||||
};
|
||||
|
||||
displayCount = () => {
|
||||
const { item, statusLabels, type } = this.props;
|
||||
|
||||
if (type !== 'status') {
|
||||
return item.count;
|
||||
}
|
||||
|
||||
if (statusLabels.loading_counts) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return statusLabels[`${item.id}_count`];
|
||||
}
|
||||
|
||||
renderIcon = () => {
|
||||
const { item, type } = this.props;
|
||||
|
||||
|
@ -42,10 +58,9 @@ class PanelGroupItem extends Component {
|
|||
}
|
||||
|
||||
render () {
|
||||
const { renderDescription, renderIcon } = this;
|
||||
const { displayCount, renderDescription, renderIcon } = this;
|
||||
const { item, onLabelClick, isSelected } = this.props;
|
||||
const {
|
||||
count,
|
||||
display_text: displayText,
|
||||
type,
|
||||
} = item;
|
||||
|
@ -68,7 +83,7 @@ class PanelGroupItem extends Component {
|
|||
{displayText}
|
||||
{renderDescription()}
|
||||
</span>
|
||||
<span className={`${baseClass}__count`}>{count}</span>
|
||||
<span className={`${baseClass}__count`}>{displayCount()}</span>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
|
|
|
@ -10,13 +10,35 @@ describe('PanelGroupItem - component', () => {
|
|||
display_text: 'All Hosts',
|
||||
type: 'all',
|
||||
};
|
||||
const validStatusGroupItem = {
|
||||
count: 111,
|
||||
display_text: 'Online Hosts',
|
||||
id: 'online',
|
||||
type: 'status',
|
||||
};
|
||||
const statusLabels = {
|
||||
online_count: 20,
|
||||
loading_counts: false,
|
||||
};
|
||||
const loadingStatusLabels = {
|
||||
online_count: 20,
|
||||
loading_counts: true,
|
||||
};
|
||||
|
||||
const labelComponent = mount(
|
||||
<PanelGroupItem item={validPanelGroupItem} />
|
||||
<PanelGroupItem item={validPanelGroupItem} statusLabels={statusLabels} />
|
||||
);
|
||||
|
||||
const platformComponent = mount(
|
||||
<PanelGroupItem item={validPanelGroupItem} type="platform" />
|
||||
<PanelGroupItem item={validPanelGroupItem} statusLabels={statusLabels} type="platform" />
|
||||
);
|
||||
|
||||
const statusLabelComponent = mount(
|
||||
<PanelGroupItem item={validStatusGroupItem} statusLabels={statusLabels} type="status" />
|
||||
);
|
||||
|
||||
const loadingStatusLabelComponent = mount(
|
||||
<PanelGroupItem item={validStatusGroupItem} statusLabels={loadingStatusLabels} type="status" />
|
||||
);
|
||||
|
||||
it('renders the appropriate icon', () => {
|
||||
|
@ -32,5 +54,9 @@ describe('PanelGroupItem - component', () => {
|
|||
|
||||
it('renders the item count', () => {
|
||||
expect(labelComponent.text()).toContain(validPanelGroupItem.count);
|
||||
expect(statusLabelComponent.text()).toNotContain(validStatusGroupItem.count);
|
||||
expect(statusLabelComponent.text()).toContain(statusLabels.online_count);
|
||||
expect(loadingStatusLabelComponent.text()).toNotContain(statusLabels.online_count);
|
||||
expect(loadingStatusLabelComponent.text()).toNotContain(validPanelGroupItem.count);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import { PropTypes } from 'react';
|
|||
|
||||
export default PropTypes.shape({
|
||||
hosts_count: PropTypes.number,
|
||||
id: PropTypes.number,
|
||||
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
title: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
});
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import { PropTypes } from 'react';
|
||||
|
||||
export default PropTypes.shape({
|
||||
loading_counts: PropTypes.bool,
|
||||
online_count: PropTypes.number,
|
||||
offline_count: PropTypes.number,
|
||||
mia_count: PropTypes.number,
|
||||
});
|
|
@ -20,6 +20,7 @@ export default {
|
|||
return `/v1/kolide/packs/${pack.id}/scheduled`;
|
||||
},
|
||||
SETUP: '/v1/setup',
|
||||
STATUS_LABEL_COUNTS: '/v1/kolide/host_summary',
|
||||
TARGETS: '/v1/kolide/targets',
|
||||
USERS: '/v1/kolide/users',
|
||||
};
|
||||
|
|
|
@ -32,6 +32,14 @@ class Kolide extends Base {
|
|||
},
|
||||
}
|
||||
|
||||
statusLabels = {
|
||||
getCounts: () => {
|
||||
const { STATUS_LABEL_COUNTS } = endpoints;
|
||||
|
||||
return this.authenticatedGet(this.endpoint(STATUS_LABEL_COUNTS));
|
||||
},
|
||||
}
|
||||
|
||||
createLabel = ({ description, name, query }) => {
|
||||
const { LABELS } = endpoints;
|
||||
|
||||
|
@ -243,9 +251,9 @@ class Kolide extends Base {
|
|||
};
|
||||
});
|
||||
const stubbedLabels = [
|
||||
{ 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: 'online', display_text: 'ONLINE', slug: 'online', type: 'status', count: 0 },
|
||||
{ id: 'offline', display_text: 'OFFLINE', slug: 'offline', type: 'status', count: 0 },
|
||||
{ id: 'mia', display_text: 'MIA', description: '(offline > 30 days)', slug: 'mia', type: 'status', count: 0 },
|
||||
];
|
||||
|
||||
return labels.concat(stubbedLabels);
|
||||
|
|
|
@ -34,6 +34,7 @@ const {
|
|||
validRevokeInviteRequest,
|
||||
validRunQueryRequest,
|
||||
validSetupRequest,
|
||||
validStatusLabelsGetCountsRequest,
|
||||
validUpdateConfigOptionsRequest,
|
||||
validUpdateConfigRequest,
|
||||
validUpdatePackRequest,
|
||||
|
@ -43,7 +44,10 @@ const {
|
|||
} = mocks;
|
||||
|
||||
describe('Kolide - API client', () => {
|
||||
afterEach(() => { nock.cleanAll(); });
|
||||
afterEach(() => {
|
||||
nock.cleanAll();
|
||||
Kolide.setBearerToken(null);
|
||||
});
|
||||
|
||||
describe('defaults', () => {
|
||||
it('sets the base URL', () => {
|
||||
|
@ -51,6 +55,23 @@ describe('Kolide - API client', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('statusLabels', () => {
|
||||
it('#getCounts', (done) => {
|
||||
const bearerToken = 'valid-bearer-token';
|
||||
const request = validStatusLabelsGetCountsRequest(bearerToken);
|
||||
|
||||
Kolide.setBearerToken(bearerToken);
|
||||
Kolide.statusLabels.getCounts()
|
||||
.then(() => {
|
||||
expect(request.isDone()).toEqual(true);
|
||||
done();
|
||||
})
|
||||
.catch(() => {
|
||||
throw new Error('Endpoint not reached');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createLabel', () => {
|
||||
it('calls the appropriate endpoint with the correct parameters', (done) => {
|
||||
const bearerToken = 'valid-bearer-token';
|
||||
|
|
|
@ -5,6 +5,7 @@ import { push } from 'react-router-redux';
|
|||
import { orderBy, sortBy } from 'lodash';
|
||||
|
||||
import entityGetter from 'redux/utilities/entityGetter';
|
||||
import { getStatusLabelCounts, setDisplay } from 'redux/nodes/components/ManageHostsPage/actions';
|
||||
import hostActions from 'redux/nodes/entities/hosts/actions';
|
||||
import labelActions from 'redux/nodes/entities/labels/actions';
|
||||
import labelInterface from 'interfaces/label';
|
||||
|
@ -19,7 +20,7 @@ import QueryForm from 'components/forms/queries/QueryForm';
|
|||
import QuerySidePanel from 'components/side_panels/QuerySidePanel';
|
||||
import Rocker from 'components/buttons/Rocker';
|
||||
import { selectOsqueryTable } from 'redux/nodes/components/QueryPages/actions';
|
||||
import { setDisplay } from 'redux/nodes/components/ManageHostsPage/actions';
|
||||
import statusLabelsInterface from 'interfaces/status_labels';
|
||||
import iconClassForLabel from 'utilities/icon_class_for_label';
|
||||
|
||||
const NEW_LABEL_HASH = '#new_label';
|
||||
|
@ -37,6 +38,7 @@ export class ManageHostsPage extends Component {
|
|||
labels: PropTypes.arrayOf(labelInterface),
|
||||
selectedLabel: labelInterface,
|
||||
selectedOsqueryTable: osqueryTableInterface,
|
||||
statusLabels: statusLabelsInterface,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -52,19 +54,11 @@ export class ManageHostsPage extends Component {
|
|||
}
|
||||
|
||||
componentWillMount () {
|
||||
const {
|
||||
dispatch,
|
||||
hosts,
|
||||
labels,
|
||||
} = this.props;
|
||||
const { dispatch } = this.props;
|
||||
|
||||
if (!hosts.length) {
|
||||
dispatch(hostActions.loadAll());
|
||||
}
|
||||
|
||||
if (!labels.length) {
|
||||
dispatch(labelActions.loadAll());
|
||||
}
|
||||
dispatch(hostActions.loadAll());
|
||||
dispatch(labelActions.loadAll());
|
||||
dispatch(getStatusLabelCounts);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -281,6 +275,7 @@ export class ManageHostsPage extends Component {
|
|||
labels,
|
||||
selectedLabel,
|
||||
selectedOsqueryTable,
|
||||
statusLabels,
|
||||
} = this.props;
|
||||
const { onAddLabelClick, onLabelClick, onOsqueryTableSelect } = this;
|
||||
|
||||
|
@ -300,6 +295,7 @@ export class ManageHostsPage extends Component {
|
|||
onAddLabelClick={onAddLabelClick}
|
||||
onLabelClick={onLabelClick}
|
||||
selectedLabel={selectedLabel}
|
||||
statusLabels={statusLabels}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -331,7 +327,7 @@ export class ManageHostsPage extends Component {
|
|||
|
||||
const mapStateToProps = (state, { location, params }) => {
|
||||
const activeLabelSlug = params.active_label || 'all-hosts';
|
||||
const { display } = state.components.ManageHostsPage;
|
||||
const { display, status_labels: statusLabels } = state.components.ManageHostsPage;
|
||||
const { entities: hosts } = entityGetter(state).get('hosts');
|
||||
const labelEntities = entityGetter(state).get('labels');
|
||||
const { entities: labels } = labelEntities;
|
||||
|
@ -351,6 +347,7 @@ const mapStateToProps = (state, { location, params }) => {
|
|||
labels,
|
||||
selectedLabel,
|
||||
selectedOsqueryTable,
|
||||
statusLabels,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ const mockStore = reduxMockStore({
|
|||
ManageHostsPage: {
|
||||
display: 'Grid',
|
||||
selectedLabel: { id: 100, display_text: 'All Hosts', type: 'all', count: 22 },
|
||||
status_labels: {},
|
||||
},
|
||||
QueryPages: {
|
||||
selectedOsqueryTable: stubbedOsqueryTable,
|
||||
|
|
|
@ -1,4 +1,45 @@
|
|||
import Kolide from 'kolide';
|
||||
import { formatErrorResponse } from 'redux/nodes/entities/base/helpers';
|
||||
|
||||
// Action Types
|
||||
export const GET_STATUS_LABEL_COUNTS_FAILURE = 'GET_STATUS_LABEL_COUNTS_FAILURE';
|
||||
export const GET_STATUS_LABEL_COUNTS_SUCCESS = 'GET_STATUS_LABEL_COUNTS_SUCCESS';
|
||||
export const LOAD_STATUS_LABEL_COUNTS = 'LOAD_STATUS_LABEL_COUNTS';
|
||||
export const SET_DISPLAY = 'SET_DISPLAY';
|
||||
|
||||
// Actions
|
||||
export const loadStatusLabelCounts = { type: LOAD_STATUS_LABEL_COUNTS };
|
||||
export const getStatusLabelCountsFailure = (errors) => {
|
||||
return {
|
||||
type: GET_STATUS_LABEL_COUNTS_FAILURE,
|
||||
payload: { errors },
|
||||
};
|
||||
};
|
||||
export const getStatusLabelCountsSuccess = (statusLabelCounts) => {
|
||||
return {
|
||||
type: GET_STATUS_LABEL_COUNTS_SUCCESS,
|
||||
payload: { status_labels: statusLabelCounts },
|
||||
};
|
||||
};
|
||||
|
||||
export const getStatusLabelCounts = (dispatch) => {
|
||||
dispatch(loadStatusLabelCounts);
|
||||
|
||||
return Kolide.statusLabels.getCounts()
|
||||
.then((counts) => {
|
||||
dispatch(getStatusLabelCountsSuccess(counts));
|
||||
|
||||
return counts;
|
||||
})
|
||||
.catch((response) => {
|
||||
const errorsObject = formatErrorResponse(response);
|
||||
|
||||
dispatch(getStatusLabelCountsFailure(errorsObject));
|
||||
|
||||
throw errorsObject;
|
||||
});
|
||||
};
|
||||
|
||||
export const setDisplay = (display) => {
|
||||
return {
|
||||
type: SET_DISPLAY,
|
||||
|
@ -8,4 +49,4 @@ export const setDisplay = (display) => {
|
|||
};
|
||||
};
|
||||
|
||||
export default { setDisplay };
|
||||
export default { getStatusLabelCounts, setDisplay };
|
||||
|
|
|
@ -1,11 +1,49 @@
|
|||
import { SET_DISPLAY } from './actions';
|
||||
import {
|
||||
GET_STATUS_LABEL_COUNTS_FAILURE,
|
||||
GET_STATUS_LABEL_COUNTS_SUCCESS,
|
||||
LOAD_STATUS_LABEL_COUNTS,
|
||||
SET_DISPLAY,
|
||||
} from './actions';
|
||||
|
||||
export const initialState = {
|
||||
display: 'Grid',
|
||||
status_labels: {
|
||||
errors: {},
|
||||
loading_counts: false,
|
||||
online_count: 0,
|
||||
offline_count: 0,
|
||||
mia_count: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export default (state = initialState, { type, payload }) => {
|
||||
switch (type) {
|
||||
case GET_STATUS_LABEL_COUNTS_FAILURE:
|
||||
return {
|
||||
...state,
|
||||
status_labels: {
|
||||
...state.status_labels,
|
||||
errors: payload.errors,
|
||||
loading_counts: false,
|
||||
},
|
||||
};
|
||||
case GET_STATUS_LABEL_COUNTS_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
status_labels: {
|
||||
...payload.status_labels,
|
||||
errors: {},
|
||||
loading_counts: false,
|
||||
},
|
||||
};
|
||||
case LOAD_STATUS_LABEL_COUNTS:
|
||||
return {
|
||||
...state,
|
||||
status_labels: {
|
||||
...state.status_labels,
|
||||
loading_counts: true,
|
||||
},
|
||||
};
|
||||
case SET_DISPLAY:
|
||||
return {
|
||||
...state,
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
import expect from 'expect';
|
||||
import expect, { spyOn, restoreSpies } from 'expect';
|
||||
|
||||
import { setDisplay } from './actions';
|
||||
import Kolide from 'kolide';
|
||||
import { reduxMockStore } from 'test/helpers';
|
||||
import {
|
||||
getStatusLabelCounts,
|
||||
getStatusLabelCountsFailure,
|
||||
getStatusLabelCountsSuccess,
|
||||
loadStatusLabelCounts,
|
||||
setDisplay,
|
||||
} from './actions';
|
||||
import reducer, { initialState } from './reducer';
|
||||
|
||||
describe('ManageHostsPage - reducer', () => {
|
||||
afterEach(restoreSpies);
|
||||
|
||||
it('sets the initial state', () => {
|
||||
expect(reducer(undefined, { type: 'SOME_ACTION' })).toEqual(initialState);
|
||||
});
|
||||
|
@ -16,4 +26,93 @@ describe('ManageHostsPage - reducer', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getStatusLabelCounts', () => {
|
||||
it('sets the loading boolean', () => {
|
||||
expect(reducer(initialState, loadStatusLabelCounts)).toEqual({
|
||||
...initialState,
|
||||
status_labels: {
|
||||
...initialState.status_labels,
|
||||
loading_counts: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
it('dispatches the correct actions when successful', (done) => {
|
||||
const statusLabelCounts = { online_count: 23, offline_count: 100, mia_count: 2 };
|
||||
const store = { components: { ManageHostsPage: initialState } };
|
||||
const mockStore = reduxMockStore(store);
|
||||
const expectedActions = [
|
||||
{ type: 'LOAD_STATUS_LABEL_COUNTS' },
|
||||
{
|
||||
type: 'GET_STATUS_LABEL_COUNTS_SUCCESS',
|
||||
payload: { status_labels: statusLabelCounts },
|
||||
},
|
||||
];
|
||||
|
||||
spyOn(Kolide.statusLabels, 'getCounts')
|
||||
.andReturn(Promise.resolve(statusLabelCounts));
|
||||
|
||||
mockStore.dispatch(getStatusLabelCounts)
|
||||
.then(() => {
|
||||
expect(mockStore.getActions()).toEqual(expectedActions);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('dispatches the correct actions when unsuccessful', (done) => {
|
||||
const store = { components: { ManageHostsPage: initialState } };
|
||||
const mockStore = reduxMockStore(store);
|
||||
const errors = [{ name: 'error_name', reason: 'error reason' }];
|
||||
const errorObject = { message: { message: 'oops', errors } };
|
||||
const expectedActions = [
|
||||
{ type: 'LOAD_STATUS_LABEL_COUNTS' },
|
||||
{
|
||||
type: 'GET_STATUS_LABEL_COUNTS_FAILURE',
|
||||
payload: { errors: { error_name: 'error reason' } },
|
||||
},
|
||||
];
|
||||
|
||||
spyOn(Kolide.statusLabels, 'getCounts')
|
||||
.andReturn(Promise.reject(errorObject));
|
||||
|
||||
mockStore.dispatch(getStatusLabelCounts)
|
||||
.then(() => {
|
||||
throw new Error('Promise should have failed');
|
||||
})
|
||||
.catch(() => {
|
||||
expect(mockStore.getActions()).toEqual(expectedActions);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('adds the label counts to state when successful', () => {
|
||||
const statusLabelCounts = { online_count: 23, offline_count: 100, mia_count: 2 };
|
||||
const successAction = getStatusLabelCountsSuccess(statusLabelCounts);
|
||||
|
||||
expect(reducer(initialState, successAction)).toEqual({
|
||||
...initialState,
|
||||
status_labels: {
|
||||
...statusLabelCounts,
|
||||
errors: {},
|
||||
loading_counts: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('adds errors to state when unsuccessful', () => {
|
||||
const errors = { error_name: 'error reason' };
|
||||
const failureAction = getStatusLabelCountsFailure(errors);
|
||||
|
||||
expect(reducer(initialState, failureAction)).toEqual({
|
||||
...initialState,
|
||||
status_labels: {
|
||||
...initialState.status_labels,
|
||||
errors,
|
||||
loading_counts: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -333,6 +333,16 @@ export const validSetupRequest = (formData) => {
|
|||
.reply(200, {});
|
||||
};
|
||||
|
||||
export const validStatusLabelsGetCountsRequest = (bearerToken) => {
|
||||
return nock('http://localhost:8080', {
|
||||
reqHeaders: {
|
||||
Authorization: `Bearer ${bearerToken}`,
|
||||
},
|
||||
})
|
||||
.get('/api/v1/kolide/host_summary')
|
||||
.reply(200, { online_count: 100, offline_count: 23, mia_count: 2 });
|
||||
};
|
||||
|
||||
export const validUpdateConfigRequest = (bearerToken, configData) => {
|
||||
return nock('http://localhost:8080', {
|
||||
reqHeaders: {
|
||||
|
@ -409,6 +419,7 @@ export default {
|
|||
validRevokeInviteRequest,
|
||||
validRunQueryRequest,
|
||||
validSetupRequest,
|
||||
validStatusLabelsGetCountsRequest,
|
||||
validUpdateConfigOptionsRequest,
|
||||
validUpdateConfigRequest,
|
||||
validUpdatePackRequest,
|
||||
|
|
Загрузка…
Ссылка в новой задаче