Skip to content

Commit

Permalink
Host Checks selection saga (#1618)
Browse files Browse the repository at this point in the history
* Move cluster check selection action next to the state

* Move cluster check selection saga in dedicated saga file

* Use cluster specific action name for checks selection

* Add a new checksSelection slice for host checks selection

* Add reducer to hosts to update checks selection

* Add host checks selection saga

* Use redux state in HostSettingsPage

* Improve imports and faked test data

* Simplify host check selection state

* Add tests for current cluster checks selection saga

* Extract callback functions to component functions

* Add targetName prop to ChecksSelection

* Rename checksSelection to hostChecksSelection

* Use useCallback when passing saving function

* Improve name for the new check selection emitted by the ChecksSelection component
  • Loading branch information
nelsonkopliku authored Jul 13, 2023
1 parent e59a5bf commit 73f91dc
Show file tree
Hide file tree
Showing 21 changed files with 408 additions and 80 deletions.
3 changes: 2 additions & 1 deletion assets/js/components/ChecksSelection/ChecksSelection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const defaultSelectedChecks = [];
function ChecksSelection({
className,
targetID,
targetName,
catalog,
selected = defaultSelectedChecks,
loading = false,
Expand Down Expand Up @@ -119,7 +120,7 @@ function ChecksSelection({
<button
className="flex justify-center items-center bg-jungle-green-500 hover:opacity-75 text-white font-bold py-2 px-4 rounded"
disabled={saving}
onClick={() => onSave(selectedChecks, targetID)}
onClick={() => onSave(selectedChecks, targetID, targetName)}
type="button"
data-testid="save-selection-button"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ describe('ChecksSelection component', () => {
const onClear = jest.fn();
const user = userEvent.setup();
const targetID = faker.datatype.uuid();
const targetName = faker.lorem.word();

const group = faker.animal.cat();
const catalog = catalogCheckFactory.buildList(2, { group });
Expand All @@ -132,6 +133,7 @@ describe('ChecksSelection component', () => {
<ChecksSelection
catalog={catalog}
targetID={targetID}
targetName={targetName}
onSave={onSave}
onUpdateCatalog={onUpdateCatalog}
onClear={onClear}
Expand All @@ -143,7 +145,7 @@ describe('ChecksSelection component', () => {
await user.click(switches[0]);
await user.click(screen.getByText('Save Check Selection'));

expect(onSave).toBeCalledWith([checkID1, checkID2], targetID);
expect(onSave).toBeCalledWith([checkID1, checkID2], targetID, targetName);
expect(onUpdateCatalog).toBeCalled();
expect(onClear).toBeCalled();
});
Expand Down
17 changes: 12 additions & 5 deletions assets/js/components/ClusterDetails/ChecksSelection.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/* eslint-disable react/no-array-index-key */

import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';

import { EOS_LOADING_ANIMATED } from 'eos-icons-react';

import { remove, uniq, toggle, groupBy } from '@lib/lists';
import { getCatalog } from '@state/selectors/catalog';
import { updateCatalog } from '@state/actions/catalog';
import { checksSelected } from '@state/actions/cluster';
import { checksSelected } from '@state/clusters';
import { executionRequested } from '@state/actions/lastExecutions';

import CatalogContainer from '@components/ChecksCatalog/CatalogContainer';
Expand Down Expand Up @@ -101,6 +101,15 @@ function ChecksSelection({ clusterId, cluster }) {
}
}, [loading]);

const saveSelection = useCallback(() =>
dispatch(
checksSelected({
checks: selectedChecks,
clusterID: clusterId,
})
)
);

return (
<div className="bg-white rounded p-3">
<CatalogContainer
Expand Down Expand Up @@ -148,9 +157,7 @@ function ChecksSelection({ clusterId, cluster }) {
<div className="place-items-end flex">
<button
className="flex justify-center items-center bg-jungle-green-500 hover:opacity-75 text-white font-bold py-2 px-4 rounded"
onClick={() =>
dispatch(checksSelected(selectedChecks, clusterId))
}
onClick={saveSelection}
type="button"
data-testid="save-selection-button"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ describe('ClusterDetails ChecksSelection component', () => {
},
},
{
type: 'CHECKS_SELECTED',
type: 'CLUSTER_CHECKS_SELECTED',
payload: {
checks: selectedChecks,
clusterID: cluster.id,
Expand Down
13 changes: 7 additions & 6 deletions assets/js/components/HostDetails/HostChecksSelection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ function HostChecksSelection({
catalogError,
catalogLoading,
onUpdateCatalog,
onSaveSelection,
isSavingSelection,
}) {
return (
<div className="w-full px-2 sm:px-0">
Expand All @@ -25,20 +27,19 @@ function HostChecksSelection({
<HostInfoBox provider={provider} agentVersion={agentVersion} />
<ChecksSelection
targetID={hostID}
targetName={hostName}
catalog={catalog}
catalogError={catalogError}
loading={catalogLoading}
selected={selectedChecks}
onSave={(_selectedChecks, _hostID) => {
// TODO: dispatch check selection for a host
}}
onSave={(newSelectedChecks, targetID, targetName) =>
onSaveSelection(newSelectedChecks, targetID, targetName)
}
onUpdateCatalog={() => onUpdateCatalog()}
onClear={() => {
// TODO
}}
saving={false}
error={null}
success={false}
saving={isSavingSelection}
/>
</div>
);
Expand Down
33 changes: 25 additions & 8 deletions assets/js/components/HostDetails/HostSettingsPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { useParams } from 'react-router-dom';

import LoadingBox from '@components/LoadingBox';

import { checksSelected } from '@state/hostChecksSelection';
import { updateCatalog } from '@state/actions/catalog';
import { getCatalog } from '@state/selectors/catalog';
import { getHost } from '@state/selectors';
import { getCheckSelection } from '@state/selectors/hostChecksSelection';
import HostChecksSelection from './HostChecksSelection';

function HostSettingsPage() {
Expand All @@ -21,16 +23,36 @@ function HostSettingsPage() {
loading: catalogLoading,
} = useSelector(getCatalog());

const { saving } = useSelector(getCheckSelection());

if (!host) {
return <LoadingBox text="Loading..." />;
}

const {
hostname: hostName,
provider,
agent_version: agentVersion,
selected_checks: selectedChecks,
} = host;

const refreshCatalog = () =>
dispatch(
updateCatalog({
provider: host.provider,
target_type: 'host',
})
);

const saveSelection = (selection, targetID, targetName) =>
dispatch(
checksSelected({
hostID: targetID,
hostName: targetName,
checks: selection,
})
);

return (
<HostChecksSelection
hostID={hostID}
Expand All @@ -41,14 +63,9 @@ function HostSettingsPage() {
catalog={catalog}
catalogError={catalogError}
catalogLoading={catalogLoading}
onUpdateCatalog={() =>
dispatch(
updateCatalog({
provider: host.provider,
target_type: 'host',
})
)
}
onUpdateCatalog={refreshCatalog}
isSavingSelection={saving}
onSaveSelection={saveSelection}
/>
);
}
Expand Down
1 change: 1 addition & 0 deletions assets/js/lib/test-utils/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const defaultInitialState = {
),
},
clusterChecksSelection: {},
hostChecksSelection: {},
catalog: { loading: false, data: [], error: null },
};

Expand Down
6 changes: 0 additions & 6 deletions assets/js/state/actions/cluster.js

This file was deleted.

5 changes: 4 additions & 1 deletion assets/js/state/clusters.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createSlice } from '@reduxjs/toolkit';
import { createAction, createSlice } from '@reduxjs/toolkit';

const initialState = {
loading: false,
Expand Down Expand Up @@ -103,6 +103,9 @@ export const clustersListSlice = createSlice({

export const CLUSTER_DEREGISTERED = 'CLUSTER_DEREGISTERED';

export const CLUSTER_CHECKS_SELECTED = 'CLUSTER_CHECKS_SELECTED';
export const checksSelected = createAction(CLUSTER_CHECKS_SELECTED);

export const {
setClusters,
appendCluster,
Expand Down
26 changes: 26 additions & 0 deletions assets/js/state/hostChecksSelection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { createAction, createSlice } from '@reduxjs/toolkit';

const initialState = {
saving: false,
};

export const hostChecksSelectionSlice = createSlice({
name: 'hostChecksSelection',
initialState,
reducers: {
startSavingChecksSelection: (state) => {
state.saving = true;
},
stopSavingChecksSelection: (state) => {
state.saving = false;
},
},
});

export const HOST_CHECKS_SELECTED = 'HOST_CHECKS_SELECTED';
export const checksSelected = createAction(HOST_CHECKS_SELECTED);

export const { startSavingChecksSelection, stopSavingChecksSelection } =
hostChecksSelectionSlice.actions;

export default hostChecksSelectionSlice.reducer;
38 changes: 38 additions & 0 deletions assets/js/state/hostChecksSelection.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import hostChecksSelectionReducer, {
startSavingChecksSelection,
stopSavingChecksSelection,
} from '@state/hostChecksSelection';

describe('Checks Selection reducer', () => {
it('should mark a check selection as saving', () => {
const initialState = {
saving: false,
};

const action = startSavingChecksSelection();

const expectedState = {
saving: true,
};

expect(hostChecksSelectionReducer(initialState, action)).toEqual(
expectedState
);
});

it('should mark a check selection as completed', () => {
const initialState = {
saving: true,
};

const action = stopSavingChecksSelection();

const expectedState = {
saving: false,
};

expect(hostChecksSelectionReducer(initialState, action)).toEqual(
expectedState
);
});
});
9 changes: 9 additions & 0 deletions assets/js/state/hosts.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ export const hostsListSlice = createSlice({
return host;
});
},
updateSelectedChecks: (state, action) => {
state.hosts = state.hosts.map((host) => {
if (host.id === action.payload.hostID) {
host.selected_checks = action.payload.checks;
}
return host;
});
},
setHeartbeatPassing: (state, action) => {
state.hosts = state.hosts.map((host) => {
if (host.id === action.payload.id) {
Expand Down Expand Up @@ -127,6 +135,7 @@ export const {
updateHost,
addTagToHost,
removeTagFromHost,
updateSelectedChecks,
setHeartbeatPassing,
setHeartbeatCritical,
setHostListDeregisterable,
Expand Down
25 changes: 25 additions & 0 deletions assets/js/state/hosts.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { faker } from '@faker-js/faker';
import hostsReducer, {
removeHost,
setHostListDeregisterable,
setHostNotDeregisterable,
setHostDeregistering,
setHostNotDeregistering,
updateSelectedChecks,
} from '@state/hosts';
import { hostFactory } from '@lib/test-utils/factories';

Expand Down Expand Up @@ -83,4 +85,27 @@ describe('Hosts reducer', () => {

expect(hostsReducer(initialState, action)).toEqual(expectedState);
});

it('should update the check selection for a host', () => {
const initialCheckSelection = [
faker.datatype.uuid(),
faker.datatype.uuid(),
];
const host1 = hostFactory.build({ selected_checks: initialCheckSelection });
const host2 = hostFactory.build();
const initialState = { hosts: [host1, host2] };

const newChecksSelection = [faker.datatype.uuid(), faker.datatype.uuid()];

const action = updateSelectedChecks({
hostID: host1.id,
checks: newChecksSelection,
});

const expectedState = {
hosts: [{ ...host1, selected_checks: newChecksSelection }, host2],
};

expect(hostsReducer(initialState, action)).toEqual(expectedState);
});
});
2 changes: 2 additions & 0 deletions assets/js/state/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import sapSystemsHealthSummaryReducer from './healthSummary';
import hostsListReducer from './hosts';
import clustersListReducer from './clusters';
import clusterChecksSelectionReducer from './clusterChecksSelection';
import hostChecksSelectionReducer from './hostChecksSelection';
import checksResultsFiltersReducer from './checksResultsFilters';
import sapSystemListReducer from './sapSystems';
import databasesListReducer from './databases';
Expand All @@ -23,6 +24,7 @@ export const store = configureStore({
hostsList: hostsListReducer,
clustersList: clustersListReducer,
clusterChecksSelection: clusterChecksSelectionReducer,
hostChecksSelection: hostChecksSelectionReducer,
checksResultsFilters: checksResultsFiltersReducer,
sapSystemsList: sapSystemListReducer,
databasesList: databasesListReducer,
Expand Down
Loading

0 comments on commit 73f91dc

Please sign in to comment.