Skip to content

Commit

Permalink
Fixes #36789 - Add actions to force applicability refresh (#10757)
Browse files Browse the repository at this point in the history
* Fixes #36789 - Add actions to force applicability refresh
* Refs #36789 - consume context from Foreman
  so I can get rid of the stupid menu when you
  click it
* Refs #36789 - respect permissions
  • Loading branch information
jeremylenz authored Oct 12, 2023
1 parent 27ce2b7 commit a578284
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 37 deletions.
49 changes: 45 additions & 4 deletions webpack/components/extensions/HostDetails/ActionsBar/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,44 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { DropdownItem } from '@patternfly/react-core';
import { CubeIcon, UndoIcon } from '@patternfly/react-icons';
import React, { useContext } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { DropdownItem, DropdownSeparator } from '@patternfly/react-core';
import { CubeIcon, UndoIcon, RedoIcon } from '@patternfly/react-icons';

import { translate as __ } from 'foremanReact/common/I18n';
import { HOST_DETAILS_KEY } from 'foremanReact/components/HostDetails/consts';
import { ForemanActionsBarContext } from 'foremanReact/components/HostDetails/ActionsBar';
import { foremanUrl } from 'foremanReact/common/helpers';

import { selectHostDetails } from '../HostDetailsSelectors';
import { useRexJobPolling } from '../Tabs/RemoteExecutionHooks';
import { runSubmanRepos } from '../Cards/ContentViewDetailsCard/HostContentViewActions';
import { hasRequiredPermissions as can, userPermissionsFromHostDetails } from '../hostDetailsHelpers';

const HostActionsBar = () => {
const hostDetails = useSelector(selectHostDetails);
const dispatch = useDispatch();
const hostname = hostDetails?.name;
const { onKebabToggle } = useContext(ForemanActionsBarContext);

const recalculateApplicability = ['edit_hosts', 'create_job_invocations'];
const showRecalculate =
can(recalculateApplicability, userPermissionsFromHostDetails({ hostDetails }));

const refreshHostDetails = () => dispatch({
type: 'API_GET',
payload: {
key: HOST_DETAILS_KEY,
url: `/api/hosts/${hostname}`,
},
});

const {
triggerJobStart: triggerRecalculate,
} = useRexJobPolling(() => runSubmanRepos(hostname, refreshHostDetails));

const handleRefreshApplicabilityClick = () => {
triggerRecalculate();
onKebabToggle();
};

return (
<>
Expand All @@ -21,6 +50,18 @@ const HostActionsBar = () => {
>
{__('Legacy content host UI')}
</DropdownItem>
<DropdownSeparator key="separator" ouiaId="katello-separator" />
{showRecalculate && (
<DropdownItem
ouiaId="katello-refresh-applicability"
key="katello-refresh-applicability"
onClick={handleRefreshApplicabilityClick}
icon={<RedoIcon />}
>
{__('Refresh applicability')}
</DropdownItem>
)
}
<DropdownItem
ouiaId="katello-change-host-content-source"
key="katello-change-host-content-source"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from '@patternfly/react-table';
import { isEqual } from 'lodash';
import { translate as __ } from 'foremanReact/common/I18n';
import { HOST_DETAILS_KEY } from 'foremanReact/components/HostDetails/consts';
import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';
import IsoDate from 'foremanReact/components/common/dates/IsoDate';
import { urlBuilder } from 'foremanReact/common/urlHelpers';
Expand All @@ -27,7 +28,7 @@ import SelectableDropdown from '../../../../SelectableDropdown';
import { useSet, useBulkSelect, useUrlParams, useTableSort } from '../../../../../components/Table/TableHooks';
import TableWrapper from '../../../../../components/Table/TableWrapper';
import { ErrataType, ErrataSeverity, ErrataToggleGroupItem } from '../../../../../components/Errata';
import { getInstallableErrata, regenerateApplicability } from './HostErrataActions';
import { getInstallableErrata } from './HostErrataActions';
import ErratumExpansionDetail from './ErratumExpansionDetail';
import ErratumExpansionContents from './ErratumExpansionContents';
import { selectHostErrataStatus } from './HostErrataSelectors';
Expand All @@ -42,6 +43,7 @@ import { hasRequiredPermissions as can,
import SortableColumnHeaders from '../../../../Table/components/SortableColumnHeaders';
import { useRexJobPolling } from '../RemoteExecutionHooks';
import { errataStatusContemplation, friendlyErrataStatus } from '../../../../Errata/errataHelpers';
import { runSubmanRepos } from '../../Cards/ContentViewDetailsCard/HostContentViewActions';

const recalculateApplicability = ['edit_hosts'];
const invokeRexJobs = ['create_job_invocations'];
Expand Down Expand Up @@ -106,9 +108,21 @@ export const ErrataTab = () => {
setToggleGroupState(APPLICABLE);
};

const refreshHostDetails = () => dispatch({
type: 'API_GET',
payload: {
key: HOST_DETAILS_KEY,
url: `/api/hosts/${hostname}`,
},
});

const {
triggerJobStart: triggerRecalculate, lastCompletedJob: lastCompletedRecalculate,
} = useRexJobPolling(() => runSubmanRepos(hostname, refreshHostDetails));

const recalculateErrata = () => {
setIsBulkActionOpen(false);
dispatch(regenerateApplicability(hostId));
triggerRecalculate();
};

let resetFilters = resetFiltersOnly;
Expand All @@ -120,7 +134,7 @@ export const ErrataTab = () => {
emptyContentTitle = __('All up to date');
emptyContentBody = __('No action is needed because there are no applicable errata for this host.');
resetFilters = recalculateErrata;
secondaryActionTextOverride = __('Recalculate');
secondaryActionTextOverride = __('Refresh errata applicability');
break;
case 'Needed':
emptyContentTitle = __('No matching errata found');
Expand Down Expand Up @@ -286,7 +300,7 @@ export const ErrataTab = () => {
component="button"
onClick={recalculateErrata}
>
{__('Recalculate')}
{__('Refresh errata applicability')}
</DropdownItem>,
];

Expand Down Expand Up @@ -455,7 +469,7 @@ export const ErrataTab = () => {
additionalListeners={[
hostId, toggleGroupState, errataTypeSelected,
errataSeveritySelected, activeSortColumn, activeSortDirection,
lastCompletedApply, lastCompletedBulkApply]}
lastCompletedApply, lastCompletedBulkApply, lastCompletedRecalculate]}
fetchItems={fetchItems}
bookmarkController="katello_errata"
readOnlyBookmarks={readOnlyBookmarks}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { API_OPERATIONS, get, put } from 'foremanReact/redux/API';
import { API_OPERATIONS, get } from 'foremanReact/redux/API';
import { foremanApi } from '../../../../../services/api';
import { HOST_ERRATA_KEY, HOST_ERRATA_APPLICABILITY_KEY } from './HostErrataConstants';
import { errorToast } from '../../../../../scenes/Tasks/helpers';
import { HOST_ERRATA_KEY } from './HostErrataConstants';

export const getInstallableErrata = (hostId, params) => get({
type: API_OPERATIONS.GET,
Expand All @@ -10,23 +9,5 @@ export const getInstallableErrata = (hostId, params) => get({
params,
});

export const regenerateApplicability = (hostId, params) => put({
type: API_OPERATIONS.PUT,
key: HOST_ERRATA_APPLICABILITY_KEY,
url: foremanApi.getApiUrl(`/hosts/${hostId}/errata/applicability`),
// This endpoint doesn't return a task, so can't use renderTaskStartedToast
// also can't use successToast because we want the type to be 'info'
handleSuccess: () => {
window.tfm.toastNotifications.notify({
message: 'Regenerating errata applicability.',
type: 'info',
link: {
children: 'View related tasks',
href: '/foreman_tasks/tasks?search=action+~+applicability&page=1',
},
});
},
errorToast: error => errorToast(error),
params,
});
export default getInstallableErrata;

Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export const HOST_ERRATA_KEY = 'HOST_ERRATUM';
export const HOST_ERRATA_APPLICABILITY_KEY = 'HOST_ERRATA_APPLICABILITY';
export const HOST_ERRATA_APPLY_KEY = 'HOST_ERRATUM_APPLY';

export const ERRATA_TYPES = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useCallback, useState, useRef } from 'react';
import { useSelector } from 'react-redux';
import { useSelector, useDispatch } from 'react-redux';
import {
ActionList,
ActionListItem,
Expand All @@ -19,6 +19,7 @@ import {
import { TableVariant, Thead, Tbody, Tr, Th, Td, TableText } from '@patternfly/react-table';
import PropTypes from 'prop-types';
import { translate as __ } from 'foremanReact/common/I18n';
import { HOST_DETAILS_KEY } from 'foremanReact/components/HostDetails/consts';
import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';

import { urlBuilder } from 'foremanReact/common/urlHelpers';
Expand All @@ -43,6 +44,7 @@ import { hasRequiredPermissions as can,
userPermissionsFromHostDetails } from '../../hostDetailsHelpers';
import SortableColumnHeaders from '../../../../Table/components/SortableColumnHeaders';
import { useRexJobPolling } from '../RemoteExecutionHooks';
import { runSubmanRepos } from '../../Cards/ContentViewDetailsCard/HostContentViewActions';

const invokeRexJobs = ['create_job_invocations'];
const createBookmarks = ['create_bookmarks'];
Expand Down Expand Up @@ -199,6 +201,7 @@ export const PackagesTab = () => {
const { results, ...metadata } = response;
const { error: errorSearchBody } = metadata;
const status = useSelector(state => selectHostPackagesStatus(state));
const dispatch = useDispatch();
const {
selectOne,
isSelected,
Expand Down Expand Up @@ -271,6 +274,23 @@ export const PackagesTab = () => {
isPolling: isInstallInProgress,
} = useRexJobPolling(packageInstallAction, getHostDetails({ hostname }));

const refreshHostDetails = () => dispatch({
type: 'API_GET',
payload: {
key: HOST_DETAILS_KEY,
url: `/api/hosts/${hostname}`,
},
});

const {
triggerJobStart: triggerRecalculate, lastCompletedJob: lastCompletedRecalculate,
} = useRexJobPolling(() => runSubmanRepos(hostname, refreshHostDetails));

const handleRefreshApplicabilityClick = () => {
setIsBulkActionOpen(false);
triggerRecalculate();
};

const actionInProgress = (isRemoveInProgress || isUpgradeInProgress
|| isBulkRemoveInProgress || isBulkUpgradeInProgress || isInstallInProgress);
const disabledReason = __('A remote execution job is in progress.');
Expand Down Expand Up @@ -347,7 +367,7 @@ export const PackagesTab = () => {
</DropdownItem>,
];

const dropdownRemoveItems = [
const kebabItems = [
<DropdownItem
aria-label="bulk_remove"
ouiaId="bulk_remove"
Expand All @@ -368,6 +388,15 @@ export const PackagesTab = () => {
>
{__('Install packages')}
</DropdownItem>,
<DropdownItem
aria-label="refresh_applicability"
ouiaId="refresh_applicability"
key="refresh_applicability"
component="button"
onClick={handleRefreshApplicabilityClick}
>
{__('Refresh package applicability')}
</DropdownItem>,
];

const handlePackageStatusSelected = newStatus => setPackageStatusSelected((prevStatus) => {
Expand Down Expand Up @@ -408,7 +437,7 @@ export const PackagesTab = () => {
toggle={<KebabToggle aria-label="bulk_actions" onToggle={toggleBulkAction} />}
isOpen={isBulkActionOpen}
isPlain
dropdownItems={dropdownRemoveItems}
dropdownItems={kebabItems}
ouiaId="bulk_actions_dropdown"
/>
</ActionListItem>
Expand Down Expand Up @@ -463,7 +492,7 @@ export const PackagesTab = () => {
additionalListeners={[hostId, packageStatusSelected,
activeSortDirection, activeSortColumn, lastCompletedPackageUpgrade,
lastCompletedPackageRemove, lastCompletedBulkPackageRemove,
lastCompletedBulkPackageUpgrade, lastCompletedPackageInstall]}
lastCompletedBulkPackageUpgrade, lastCompletedPackageInstall, lastCompletedRecalculate]}
fetchItems={fetchItems}
bookmarkController="katello_host_installed_packages"
readOnlyBookmarks={readOnlyBookmarks}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const useRexJobPolling = (initialAction, successAction = null, failureAct
const tick = (resp) => {
const { data } = resp;
const { statusLabel, id, description } = propsToCamelCase(data);
setRexJobId(id);
if (!id) setRexJobId(id);
if (statusLabel && statusLabel !== 'running') {
stopRexJobPolling({ jobId: id, statusLabel });
if (statusLabel === 'succeeded') {
Expand Down

0 comments on commit a578284

Please sign in to comment.