Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patches for packages alternative approach #3005

Merged
merged 2 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 27 additions & 18 deletions assets/js/common/UpgradablePackagesList/UpgradablePackagesList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { noop } from 'lodash';

import Button from '@common/Button';
import Table, { createStringSortingPredicate } from '@common/Table';
import { EOS_LOADING_ANIMATED } from 'eos-icons-react';

const upgradablePackagesDefault = [];

function UpgradablePackagesList({
upgradablePackages = upgradablePackagesDefault,
patchesLoading,
onPatchClick = noop,
onLoad = noop,
}) {
const [sortDirection, setSortDirection] = useState('asc');

Expand All @@ -29,7 +30,6 @@ function UpgradablePackagesList({
const config = {
pagination: true,
usePadding: false,
onPageChange: onLoad,
columns: [
{
title: 'Installed Packages',
Expand All @@ -47,22 +47,31 @@ function UpgradablePackagesList({
{
title: 'Related Patches',
key: 'patches',
render: (content, { to_package_id }) => (
<div>
{content &&
content.map(({ advisory }) => (
<div key={`${to_package_id}-${advisory}`}>
<Button
type="link"
size="fit"
onClick={() => onPatchClick(advisory)}
>
{advisory}
</Button>
</div>
))}
</div>
),
render: (content, { to_package_id }) => {
if (patchesLoading) {
return (
<div>
<EOS_LOADING_ANIMATED />
</div>
);
}
return (
<div>
{content &&
content.map(({ advisory }) => (
<div key={`${to_package_id}-${advisory}`}>
<Button
type="link"
size="fit"
onClick={() => onPatchClick(advisory)}
>
{advisory}
</Button>
</div>
))}
</div>
);
},
},
],
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export default {
},
description: 'List of upgradable packages',
},
patchesLoading: {
description: 'Are patches loading?',
},
},
};

Expand All @@ -25,3 +28,11 @@ export const Default = {
upgradablePackages: upgradablePackageFactory.buildList(4),
},
};

export const PatchesLoading = {
args: {
hostname: 'Example Host',
patchesLoading: true,
upgradablePackages: upgradablePackageFactory.buildList(4),
},
};
4 changes: 2 additions & 2 deletions assets/js/lib/api/softwareUpdates.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { networkClient } from '@lib/network';
export const getSoftwareUpdates = (hostID) =>
networkClient.get(`/hosts/${hostID}/software_updates`);

export const getPatchesForPackages = (packageIDs) =>
export const getPatchesForPackages = (hostID) =>
networkClient.get(`/software_updates/packages`, {
params: { package_ids: packageIDs },
params: { host_id: hostID },
});

export const getAdvisoryErrata = (advisoryName) =>
Expand Down
2 changes: 2 additions & 0 deletions assets/js/pages/UpgradablePackagesPage/UpgradablePackages.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { containsSubstring } from '@lib/filter';
export default function UpgradablePackages({
hostName,
upgradablePackages,
patchesLoading,
onPatchClick = noop,
onLoad = noop,
}) {
Expand Down Expand Up @@ -41,6 +42,7 @@ export default function UpgradablePackages({
</div>
</div>
<UpgradablePackagesList
patchesLoading={patchesLoading}
upgradablePackages={displayedPackages}
onPatchClick={onPatchClick}
onLoad={onLoad}
Expand Down
25 changes: 15 additions & 10 deletions assets/js/pages/UpgradablePackagesPage/UpgradablePackagesPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { useSelector, useDispatch } from 'react-redux';
import { get } from 'lodash';

import { getHost } from '@state/selectors/host';
import { getUpgradablePackages } from '@state/selectors/softwareUpdates';
import {
getUpgradablePackages,
getPatchesLoading,
} from '@state/selectors/softwareUpdates';
import {
fetchSoftwareUpdates,
fetchUpgradablePackagesPatches,
Expand All @@ -30,24 +33,26 @@ function UpgradablePackagesPage() {
getUpgradablePackages(state, hostID)
);

const patchesLoading = useSelector((state) =>
getPatchesLoading(state, hostID)
);

useEffect(() => {
if (upgradablePackages.length > 0) {
dispatch(fetchUpgradablePackagesPatches({ hostID }));
}
}, [upgradablePackages.length]);

return (
<>
<BackButton url={`/hosts/${hostID}`}>Back to Host Details</BackButton>
<UpgradablePackages
hostName={hostname}
upgradablePackages={upgradablePackages}
patchesLoading={patchesLoading}
onPatchClick={(advisoryID) =>
navigate(`/hosts/${hostID}/patches/${advisoryID}`)
}
onLoad={(items) => {
if (items.length) {
const packageIDs = items.map(
({ to_package_id: packageID }) => packageID
);

dispatch(fetchUpgradablePackagesPatches({ hostID, packageIDs }));
}
}}
/>
</>
);
Expand Down
34 changes: 14 additions & 20 deletions assets/js/state/sagas/softwareUpdates.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { get, chunk } from 'lodash';
import { all, put, call, takeEvery } from 'redux-saga/effects';
import { get } from 'lodash';
import { put, call, takeEvery } from 'redux-saga/effects';
import {
getSoftwareUpdates,
getPatchesForPackages,
Expand Down Expand Up @@ -43,27 +43,21 @@ export function* fetchSoftwareUpdates({ payload: hostID }) {
}
}

export function* fetchUpgradablePackagesPatches({
payload: { hostID, packageIDs },
}) {
const chunks = chunk(packageIDs, 50);

const effects = chunks.map((packageIDsChunk) =>
call(getPatchesForPackages, packageIDsChunk)
);

export function* fetchUpgradablePackagesPatches({ payload: { hostID } }) {
try {
const responses = yield all(effects);
const patches = responses
.map(({ data: { patches: patchesForChunk } }) =>
patchesForChunk.map((patch) => ({
const {
data: { patches },
} = yield call(getPatchesForPackages, hostID);

yield put(
setPatchesForPackages({
hostID,
patches: patches.map((patch) => ({
...patch,
package_id: Number(patch.package_id),
}))
)
.flat();

yield put(setPatchesForPackages({ hostID, patches }));
})),
})
);
yield put(setSettingsConfigured());
} catch (error) {
const errorCode = get(error, ['response', 'status']);
Expand Down
31 changes: 0 additions & 31 deletions assets/js/state/sagas/softwareUpdates.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,37 +168,6 @@ describe('Software Updates saga', () => {
]);
});

it('chunks 600 unique package IDs', async () => {
const axiosMock = new MockAdapter(networkClient);
const hostID = faker.string.uuid();
const packageIDs = Array.from(new Array(160)).map(() =>
faker.number.int()
);
const patches = patchForPackageFactory.buildList(3);
const response = {
patches: [{ package_id: packageIDs[0], patches }],
};

axiosMock.onGet(`/api/v1/software_updates/packages`).reply(200, response);

const dispatched = await recordSaga(fetchUpgradablePackagesPatches, {
payload: { hostID, packageIDs },
});

expect(dispatched).toEqual([
setPatchesForPackages({
hostID,
patches: [
...response.patches,
...response.patches,
...response.patches,
...response.patches,
],
}),
setSettingsConfigured(),
]);
});

it('should set settings not configured when 422 with relevant error message', async () => {
const axiosMock = new MockAdapter(networkClient);
const hostID = faker.string.uuid();
Expand Down
5 changes: 5 additions & 0 deletions assets/js/state/selectors/softwareUpdates.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ export const getSoftwareUpdatesLoading = createSelector(
(softwareUpdates) => get(softwareUpdates, ['loading'], false)
);

export const getPatchesLoading = createSelector(
[(state, id) => getSoftwareUpdatesForHost(id)(state)],
(softwareUpdates) => get(softwareUpdates, ['loadingPatches'], false)
);

export const getSoftwareUpdatesErrors = createSelector(
[(state, id) => getSoftwareUpdatesForHost(id)(state)],
(softwareUpdates) => get(softwareUpdates, ['errors'], [])
Expand Down
7 changes: 6 additions & 1 deletion assets/js/state/softwareUpdates.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const initialState = {

const initialHostState = {
loading: false,
loadingPatches: false,
errors: [],
};

Expand All @@ -24,7 +25,7 @@ export const softwareUpdatesSlice = createSlice({
startLoadingSoftwareUpdates: (state, { payload: { hostID } }) => {
state.softwareUpdates = {
...state.softwareUpdates,
[hostID]: { ...initialHostState, loading: true },
[hostID]: { ...initialHostState, loading: true, loadingPatches: true },
};
},
setSoftwareUpdates: (
Expand Down Expand Up @@ -71,6 +72,10 @@ export const softwareUpdatesSlice = createSlice({
};
});

if (state.softwareUpdates[hostID]) {
state.softwareUpdates[hostID].loadingPatches = false;
}

if (!isEmpty(packages)) {
state.softwareUpdates[hostID].upgradable_packages = newPackages;
}
Expand Down
17 changes: 14 additions & 3 deletions assets/js/state/softwareUpdates.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ describe('SoftwareUpdates reducer', () => {
const action = startLoadingSoftwareUpdates({ hostID });

const expectedState = {
softwareUpdates: { [hostID]: { loading: true, errors: [] } },
softwareUpdates: {
[hostID]: { loading: true, loadingPatches: true, errors: [] },
},
};

expect(softwareUpdatesReducer(initialState, action)).toEqual(expectedState);
Expand All @@ -34,6 +36,7 @@ describe('SoftwareUpdates reducer', () => {
relevant_patches: [],
upgradable_packages: [],
loading: false,
loadingPatches: false,
errors: [],
},
[host2]: {
Expand Down Expand Up @@ -64,6 +67,7 @@ describe('SoftwareUpdates reducer', () => {
},
],
loading: false,
loadingPatches: false,
errors: [],
},
},
Expand Down Expand Up @@ -116,10 +120,12 @@ describe('SoftwareUpdates reducer', () => {
relevant_patches: [],
upgradable_packages: [],
loading: false,
loadingPatches: false,
errors: [],
},
[host2]: {
...newSoftwareUpdates,
loadingPatches: false,
loading: false,
errors: [],
},
Expand Down Expand Up @@ -170,7 +176,7 @@ describe('SoftwareUpdates reducer', () => {
errors: [],
loading: false,
},
[host2]: { errors: [], loading: false },
[host2]: { errors: [], loading: false, loadingPatches: false },
},
});
});
Expand Down Expand Up @@ -230,7 +236,12 @@ describe('SoftwareUpdates reducer', () => {

expect(actual).toEqual({
softwareUpdates: {
[host1]: { ...initialState[host1], loading: false, errors },
[host1]: {
...initialState[host1],
loading: false,
loadingPatches: false,
errors,
},
},
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,14 @@ defmodule Trento.Infrastructure.SoftwareUpdates.MockSuma do
{:ok,
[
%{
name: "kernel",
name: "elixir",
version: "6.9.7",
release: "2",
arch_label: "x86_64",
epoch: "0"
},
%{
name: "systemd",
version: "6.9.7",
release: "2",
arch_label: "x86_64",
Expand Down
Loading
Loading