From 65910ac706714b753ef6a4a35fd5d35171bbbf88 Mon Sep 17 00:00:00 2001 From: Lukas Jezek Date: Thu, 16 Jul 2026 10:45:05 +0000 Subject: [PATCH 1/3] Fixes #39532 - All hosts - add action - bulk add capsule --- ...osts_bulk_actions_controller_extensions.rb | 78 +++++ config/routes.rb | 2 + lib/foreman_openscap/engine.rb | 4 +- .../BulkChangeOpenscapProxyModal.js | 275 ++++++++++++++++++ .../HostsIndex/ChangeOpenscapProxyAction.js | 63 ++++ .../OpenscapRemediationWizard/constants.js | 7 +- webpack/global_index.js | 23 +- 7 files changed, 448 insertions(+), 4 deletions(-) create mode 100644 app/controllers/concerns/foreman_openscap/api/v2/hosts_bulk_actions_controller_extensions.rb create mode 100644 webpack/components/HostsIndex/BulkActions/changeOpenscapProxy/BulkChangeOpenscapProxyModal.js create mode 100644 webpack/components/HostsIndex/ChangeOpenscapProxyAction.js diff --git a/app/controllers/concerns/foreman_openscap/api/v2/hosts_bulk_actions_controller_extensions.rb b/app/controllers/concerns/foreman_openscap/api/v2/hosts_bulk_actions_controller_extensions.rb new file mode 100644 index 000000000..3775742ba --- /dev/null +++ b/app/controllers/concerns/foreman_openscap/api/v2/hosts_bulk_actions_controller_extensions.rb @@ -0,0 +1,78 @@ +module ForemanOpenscap + module Api + module V2 + module HostsBulkActionsControllerExtensions + extend ActiveSupport::Concern + + included do + before_action :find_editable_hosts, only: [:change_openscap_proxy] + end + + def change_openscap_proxy + openscap_proxy_id = params[:openscap_proxy_id] + + if openscap_proxy_id.blank? + return render json: { + error: { + message: _("No OpenSCAP Capsule selected."), + }, + }, status: :unprocessable_entity + end + + smart_proxy = begin + ::SmartProxy.find(openscap_proxy_id) + rescue ActiveRecord::RecordNotFound + nil + end + + if smart_proxy.nil? + return render json: { + error: { + message: _("OpenSCAP Capsule with id %s not found") % openscap_proxy_id, + }, + }, status: :unprocessable_entity + end + + unless smart_proxy.has_feature?('Openscap') + return render json: { + error: { + message: _("The selected capsule does not have the OpenSCAP feature enabled."), + }, + }, status: :unprocessable_entity + end + + failed_hosts = [] + @hosts.each do |host| + host.openscap_proxy = smart_proxy + failed_hosts << host unless host.save + end + + if failed_hosts.empty? + process_response(true, { + message: _("OpenSCAP Capsule set to %s") % smart_proxy.name, + }) + else + render_error(:bulk_hosts_error, status: :unprocessable_entity, + locals: { + message: n_("Failed to assign OpenSCAP Capsule to %s host", + "Failed to assign OpenSCAP Capsule to %s hosts", + failed_hosts.count) % failed_hosts.count, + failed_host_ids: failed_hosts.map(&:id), + }) + end + end + + private + + def action_permission + case params[:action] + when 'change_openscap_proxy' + 'edit' + else + super + end + end + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index e9f5ca4f5..dee9d38fa 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -103,6 +103,8 @@ end end end + + match 'hosts/bulk/change_openscap_proxy', :to => 'hosts_bulk_actions#change_openscap_proxy', :via => [:put] end end end diff --git a/lib/foreman_openscap/engine.rb b/lib/foreman_openscap/engine.rb index 7ade8dd68..dc070c240 100644 --- a/lib/foreman_openscap/engine.rb +++ b/lib/foreman_openscap/engine.rb @@ -93,7 +93,8 @@ class Engine < ::Rails::Engine :resource_type => 'ForemanOpenscap::ScapContent' permission :edit_hosts, { :hosts => %i[openscap_proxy_changed select_multiple_openscap_proxy - update_multiple_openscap_proxy] }, + update_multiple_openscap_proxy], + 'api/v2/hosts_bulk_actions' => [:change_openscap_proxy] }, :resource_type => "Host" permission :view_hosts, { 'api/v2/hosts' => [:policies_enc] }, :resource_type => 'Host' permission :edit_hostgroups, { :hostgroups => [:openscap_proxy_changed] }, :resource_type => "Hostgroup" @@ -195,6 +196,7 @@ class Engine < ::Rails::Engine # Include concerns in this config.to_prepare block config.to_prepare do ::Api::V2::HostsController.send(:include, ForemanOpenscap::Api::V2::HostsControllerExtensions) + ::Api::V2::HostsBulkActionsController.send(:include, ForemanOpenscap::Api::V2::HostsBulkActionsControllerExtensions) ::Host::Managed.send(:include, ForemanOpenscap::OpenscapProxyExtensions) ::Host::Managed.send(:include, ForemanOpenscap::OpenscapProxyCoreExtensions) ::Host::Managed.send(:prepend, ForemanOpenscap::HostExtensions) diff --git a/webpack/components/HostsIndex/BulkActions/changeOpenscapProxy/BulkChangeOpenscapProxyModal.js b/webpack/components/HostsIndex/BulkActions/changeOpenscapProxy/BulkChangeOpenscapProxyModal.js new file mode 100644 index 000000000..eb8876784 --- /dev/null +++ b/webpack/components/HostsIndex/BulkActions/changeOpenscapProxy/BulkChangeOpenscapProxyModal.js @@ -0,0 +1,275 @@ +import React, { useState, useEffect } from 'react'; +import PropTypes from 'prop-types'; +import { useDispatch, useSelector } from 'react-redux'; +import { + Modal, + Button, + Grid, + GridItem, + Form, + FormGroup, + Select, + Stack, + StackItem, + SelectOption, + SelectList, + MenuToggle, +} from '@patternfly/react-core'; +import { foremanUrl } from 'foremanReact/common/helpers'; +import { APIActions } from 'foremanReact/redux/API'; +import { sprintf, translate as __ } from 'foremanReact/common/I18n'; +import { STATUS } from 'foremanReact/constants'; +import { + selectAPIStatus, + selectAPIResponse, +} from 'foremanReact/redux/API/APISelectors'; +import { + BULK_CHANGE_OPENSCAP_PROXY_KEY, + HOSTS_API_PATH, + HOSTS_API_REQUEST_KEY, + OPENSCAP_PROXIES_KEY, +} from '../../../OpenscapRemediationWizard/constants'; + +const buildBulkRequestBody = ({ + fetchBulkParams, + organizationId, + locationId, + ...params +}) => ({ + included: { + search: fetchBulkParams(), + }, + ...(organizationId != null ? { organization_id: organizationId } : {}), + ...(locationId != null ? { location_id: locationId } : {}), + ...params, +}); + +const fetchOpenscapProxies = () => + APIActions.get({ + key: OPENSCAP_PROXIES_KEY, + url: foremanUrl( + '/api/smart_proxies?search=feature%3DOpenscap&per_page=all' + ), + }); + +const BulkChangeOpenscapProxyModal = ({ + isOpen, + closeModal, + selectAllHostsMode, + selectedCount, + fetchBulkParams, + organizationId, + locationId, +}) => { + const dispatch = useDispatch(); + const [proxyId, setProxyId] = useState(''); + const [proxySelectOpen, setProxySelectOpen] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); + + useEffect(() => { + if (isOpen) { + dispatch(fetchOpenscapProxies()); + } else { + setIsSubmitting(false); + } + }, [dispatch, isOpen]); + + const proxies = useSelector(state => + selectAPIResponse(state, OPENSCAP_PROXIES_KEY) + ); + const proxyStatus = useSelector(state => + selectAPIStatus(state, OPENSCAP_PROXIES_KEY) + ); + + const getProxyLabel = id => { + const proxy = proxies?.results?.find( + p => p.id.toString() === id.toString() + ); + return proxy?.name || id; + }; + + const handleProxySelect = (_event, selection) => { + setProxyId(selection); + setProxySelectOpen(false); + }; + + const handleToggleClick = () => { + setProxySelectOpen(!proxySelectOpen); + }; + + const handleModalClose = () => { + setProxyId(''); + setProxySelectOpen(false); + setIsSubmitting(false); + closeModal(); + }; + + const handleSuccess = () => { + dispatch( + APIActions.get({ + key: HOSTS_API_REQUEST_KEY, + url: foremanUrl(HOSTS_API_PATH), + }) + ); + handleModalClose(); + }; + + const handleError = () => { + setIsSubmitting(false); + handleModalClose(); + }; + + const handleConfirm = () => { + const requestBody = buildBulkRequestBody({ + fetchBulkParams, + organizationId, + locationId, + openscap_proxy_id: proxyId, + }); + + setIsSubmitting(true); + dispatch( + APIActions.put({ + key: BULK_CHANGE_OPENSCAP_PROXY_KEY, + url: foremanUrl('/api/v2/hosts/bulk/change_openscap_proxy'), + handleSuccess, + successToast: response => response.data.message, + handleError, + errorToast: error => + error?.response?.data?.error?.message || __('Error'), + params: requestBody, + }) + ); + }; + + const descriptionText = selectAllHostsMode ? ( + <> + {__('Assign OpenSCAP capsule for ')} + {__('ALL selected hosts')} +
+ {__( + '. This will change previous capsule assignments on the selected hosts.' + )} + + ) : ( + <> + {__('Assign OpenSCAP capsule for ')} + {sprintf(__('%s selected hosts.'), selectedCount)} +
+ {__( + 'This will change previous capsule assignments on the selected hosts.' + )} + + ); + + const modalActions = [ + , + , + ]; + + return ( + + + {descriptionText} + {proxyStatus === STATUS.RESOLVED && proxies?.results?.length > 0 && ( + + + +
+ + + +
+
+
+
+ )} + {proxyStatus === STATUS.RESOLVED && + (!proxies?.results || proxies.results.length === 0) && + __( + 'No OpenSCAP Proxies available. Please configure a Smart Proxy with the OpenSCAP feature.' + )} +
+
+ ); +}; + +BulkChangeOpenscapProxyModal.propTypes = { + isOpen: PropTypes.bool, + closeModal: PropTypes.func, + fetchBulkParams: PropTypes.func.isRequired, + selectedCount: PropTypes.number.isRequired, + selectAllHostsMode: PropTypes.bool.isRequired, + organizationId: PropTypes.number, + locationId: PropTypes.number, +}; + +BulkChangeOpenscapProxyModal.defaultProps = { + isOpen: false, + closeModal: () => {}, + organizationId: undefined, + locationId: undefined, +}; + +export default BulkChangeOpenscapProxyModal; diff --git a/webpack/components/HostsIndex/ChangeOpenscapProxyAction.js b/webpack/components/HostsIndex/ChangeOpenscapProxyAction.js new file mode 100644 index 000000000..9396807bc --- /dev/null +++ b/webpack/components/HostsIndex/ChangeOpenscapProxyAction.js @@ -0,0 +1,63 @@ +import React, { useContext } from 'react'; +import PropTypes from 'prop-types'; +import { MenuItem } from '@patternfly/react-core'; +import { translate as __ } from 'foremanReact/common/I18n'; +import { + openBulkModal, + useBulkModalOpen, +} from 'foremanReact/common/BulkModalStateHelper'; +import { ForemanActionsBarContext } from 'foremanReact/components/HostDetails/ActionsBar'; +import BulkChangeOpenscapProxyModal from './BulkActions/changeOpenscapProxy/BulkChangeOpenscapProxyModal'; +import { CHANGE_OPENSCAP_MODAL_ID } from '../OpenscapRemediationWizard/constants'; + +export const ChangeOpenscapProxyMenuItem = ({ selectedCount }) => { + const openModal = () => openBulkModal(CHANGE_OPENSCAP_MODAL_ID, true); + + return ( + + {__('OpenSCAP Capsule')} + + ); +}; + +ChangeOpenscapProxyMenuItem.propTypes = { + selectedCount: PropTypes.number, +}; + +ChangeOpenscapProxyMenuItem.defaultProps = { + selectedCount: 0, +}; + +const BulkChangeOpenscapProxyModalScene = () => { + const { + selectAllHostsMode = false, + selectedCount = 0, + fetchBulkParams, + organizationId, + locationId, + } = useContext(ForemanActionsBarContext) || {}; + + const { isOpen, close: closeModal } = useBulkModalOpen( + CHANGE_OPENSCAP_MODAL_ID + ); + + return ( + + ); +}; + +export default BulkChangeOpenscapProxyModalScene; diff --git a/webpack/components/OpenscapRemediationWizard/constants.js b/webpack/components/OpenscapRemediationWizard/constants.js index b8f745bde..7e6f34d33 100644 --- a/webpack/components/OpenscapRemediationWizard/constants.js +++ b/webpack/components/OpenscapRemediationWizard/constants.js @@ -1,7 +1,5 @@ import { translate as __ } from 'foremanReact/common/I18n'; -export const OPENSCAP_REMEDIATION_MODAL_ID = 'openscapRemediationModal'; -export const HOSTS_PATH = '/hosts'; export const FAIL_RULE_SEARCH = 'fails_xccdf_rule'; export const HOSTS_API_PATH = '/api/hosts'; @@ -21,3 +19,8 @@ export const WIZARD_TITLES = { reviewRemediation: __('Review remediation'), finish: __('Done'), }; + +export const BULK_CHANGE_OPENSCAP_PROXY_KEY = 'BULK_CHANGE_OPENSCAP_PROXY'; +export const OPENSCAP_PROXIES_KEY = 'OPENSCAP_PROXIES_KEY'; + +export const CHANGE_OPENSCAP_MODAL_ID = 'BULK_CHANGE_OPENSCAP_PROXY_MODAL'; diff --git a/webpack/global_index.js b/webpack/global_index.js index d62e51fa3..35979f093 100644 --- a/webpack/global_index.js +++ b/webpack/global_index.js @@ -1,10 +1,31 @@ import React from 'react'; import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill'; import HostKebabItems from './components/HostExtentions/HostKebabItems'; +import BulkChangeOpenscapProxyModalScene, { + ChangeOpenscapProxyMenuItem, +} from './components/HostsIndex/ChangeOpenscapProxyAction'; + +const HOST_DETAILS_KEBAB_WEIGHT = 400; +const HOST_ASSOCIATIONS_WEIGHT = 1212; +const BULK_MODAL_WEIGHT = 100; addGlobalFill( 'host-details-kebab', `openscap-kebab-items`, , - 400 + HOST_DETAILS_KEBAB_WEIGHT +); + +addGlobalFill( + '_host-associations', + 'openscap-change-proxy-menu-item', + , + HOST_ASSOCIATIONS_WEIGHT +); + +addGlobalFill( + '_all-hosts-modals', + 'BulkChangeOpenscapProxyModal', + , + BULK_MODAL_WEIGHT ); From 95930d3b1c23f0d80550ecb90bcb3141dbc91e39 Mon Sep 17 00:00:00 2001 From: Lukas Jezek Date: Thu, 16 Jul 2026 16:40:24 +0000 Subject: [PATCH 2/3] Review + added tests --- ...osts_bulk_actions_controller_extensions.rb | 12 +- .../v2/hosts_bulk_actions_controller_test.rb | 98 ++++++++++ .../BulkChangeOpenscapProxyModal.js | 122 ++++-------- .../BulkChangeOpenscapProxyModal.test.js | 160 ++++++++++++++++ .../ChangeOpenscapProxyAction.test.js | 174 ++++++++++++++++++ 5 files changed, 476 insertions(+), 90 deletions(-) create mode 100644 test/functional/api/v2/hosts_bulk_actions_controller_test.rb create mode 100644 webpack/components/HostsIndex/BulkActions/changeOpenscapProxy/__tests__/BulkChangeOpenscapProxyModal.test.js create mode 100644 webpack/components/HostsIndex/__tests__/ChangeOpenscapProxyAction.test.js diff --git a/app/controllers/concerns/foreman_openscap/api/v2/hosts_bulk_actions_controller_extensions.rb b/app/controllers/concerns/foreman_openscap/api/v2/hosts_bulk_actions_controller_extensions.rb index 3775742ba..d543d4bc9 100644 --- a/app/controllers/concerns/foreman_openscap/api/v2/hosts_bulk_actions_controller_extensions.rb +++ b/app/controllers/concerns/foreman_openscap/api/v2/hosts_bulk_actions_controller_extensions.rb @@ -14,7 +14,7 @@ def change_openscap_proxy if openscap_proxy_id.blank? return render json: { error: { - message: _("No OpenSCAP Capsule selected."), + message: _("No OpenSCAP Proxy selected."), }, }, status: :unprocessable_entity end @@ -28,7 +28,7 @@ def change_openscap_proxy if smart_proxy.nil? return render json: { error: { - message: _("OpenSCAP Capsule with id %s not found") % openscap_proxy_id, + message: _("OpenSCAP proxy with id %s not found") % openscap_proxy_id, }, }, status: :unprocessable_entity end @@ -36,7 +36,7 @@ def change_openscap_proxy unless smart_proxy.has_feature?('Openscap') return render json: { error: { - message: _("The selected capsule does not have the OpenSCAP feature enabled."), + message: _("The selected proxy does not have the OpenSCAP feature enabled."), }, }, status: :unprocessable_entity end @@ -49,13 +49,13 @@ def change_openscap_proxy if failed_hosts.empty? process_response(true, { - message: _("OpenSCAP Capsule set to %s") % smart_proxy.name, + message: _("OpenSCAP Proxy set to %s") % smart_proxy.name, }) else render_error(:bulk_hosts_error, status: :unprocessable_entity, locals: { - message: n_("Failed to assign OpenSCAP Capsule to %s host", - "Failed to assign OpenSCAP Capsule to %s hosts", + message: n_("Failed to assign OpenSCAP Proxy to %s host", + "Failed to assign OpenSCAP Proxy to %s hosts", failed_hosts.count) % failed_hosts.count, failed_host_ids: failed_hosts.map(&:id), }) diff --git a/test/functional/api/v2/hosts_bulk_actions_controller_test.rb b/test/functional/api/v2/hosts_bulk_actions_controller_test.rb new file mode 100644 index 000000000..bc3b71898 --- /dev/null +++ b/test/functional/api/v2/hosts_bulk_actions_controller_test.rb @@ -0,0 +1,98 @@ +require 'test_plugin_helper' + +class Api::V2::HostsBulkActionsControllerTest < ActionController::TestCase + def setup + as_admin do + @organization = FactoryBot.create(:organization) + @location = FactoryBot.create(:location) + @proxy = FactoryBot.create(:openscap_proxy, + :organizations => [@organization], + :locations => [@location]) + @host1 = FactoryBot.create(:host, :managed, + :organization => @organization, + :location => @location) + @host2 = FactoryBot.create(:host, :managed, + :organization => @organization, + :location => @location) + @host_ids = [@host1.id, @host2.id] + end + end + + def valid_bulk_params(host_ids = @host_ids) + { + :organization_id => @organization.id, + :location_id => @location.id, + :included => { + :ids => host_ids, + }, + :excluded => { + :ids => [], + }, + } + end + + test "should assign openscap proxy to selected hosts" do + put :change_openscap_proxy, + params: valid_bulk_params.merge(:openscap_proxy_id => @proxy.id), + session: set_session_user + + assert_response :success + response = ActiveSupport::JSON.decode(@response.body) + assert_match(/OpenSCAP Capsule set to/, response['message']) + assert_includes response['message'], @proxy.name + + [@host1, @host2].each do |host| + host.reload + assert_equal @proxy.id, host.openscap_proxy_id + end + end + + test "should require openscap_proxy_id" do + put :change_openscap_proxy, + params: valid_bulk_params, + session: set_session_user + + assert_response :unprocessable_entity + response = ActiveSupport::JSON.decode(@response.body) + assert_match(/No OpenSCAP Capsule selected/, response['error']['message']) + end + + test "should return error when proxy is not found" do + put :change_openscap_proxy, + params: valid_bulk_params.merge(:openscap_proxy_id => 0), + session: set_session_user + + assert_response :unprocessable_entity + response = ActiveSupport::JSON.decode(@response.body) + assert_match(/not found/, response['error']['message']) + end + + test "should return error when proxy lacks Openscap feature" do + other_proxy = FactoryBot.create(:smart_proxy, + :organizations => [@organization], + :locations => [@location]) + openscap_feature = Feature.find_by(:name => 'Openscap') + other_proxy.features.delete(openscap_feature) if openscap_feature + refute other_proxy.reload.has_feature?('Openscap') + + put :change_openscap_proxy, + params: valid_bulk_params.merge(:openscap_proxy_id => other_proxy.id), + session: set_session_user + + assert_response :unprocessable_entity + response = ActiveSupport::JSON.decode(@response.body) + assert_match(/OpenSCAP feature/, response['error']['message']) + end + + test "should assign openscap proxy for a single host" do + put :change_openscap_proxy, + params: valid_bulk_params([@host1.id]).merge(:openscap_proxy_id => @proxy.id), + session: set_session_user + + assert_response :success + @host1.reload + assert_equal @proxy.id, @host1.openscap_proxy_id + @host2.reload + assert_nil @host2.openscap_proxy_id + end +end diff --git a/webpack/components/HostsIndex/BulkActions/changeOpenscapProxy/BulkChangeOpenscapProxyModal.js b/webpack/components/HostsIndex/BulkActions/changeOpenscapProxy/BulkChangeOpenscapProxyModal.js index eb8876784..1d02856c4 100644 --- a/webpack/components/HostsIndex/BulkActions/changeOpenscapProxy/BulkChangeOpenscapProxyModal.js +++ b/webpack/components/HostsIndex/BulkActions/changeOpenscapProxy/BulkChangeOpenscapProxyModal.js @@ -8,13 +8,11 @@ import { GridItem, Form, FormGroup, - Select, Stack, StackItem, - SelectOption, - SelectList, - MenuToggle, } from '@patternfly/react-core'; +// eslint-disable-next-line import/no-unresolved +import { SimpleDropdown } from '@patternfly/react-templates'; import { foremanUrl } from 'foremanReact/common/helpers'; import { APIActions } from 'foremanReact/redux/API'; import { sprintf, translate as __ } from 'foremanReact/common/I18n'; @@ -23,6 +21,7 @@ import { selectAPIStatus, selectAPIResponse, } from 'foremanReact/redux/API/APISelectors'; +import { buildBulkRequestBody } from 'foremanReact/components/HostsIndex/BulkActions/helpers'; import { BULK_CHANGE_OPENSCAP_PROXY_KEY, HOSTS_API_PATH, @@ -30,20 +29,6 @@ import { OPENSCAP_PROXIES_KEY, } from '../../../OpenscapRemediationWizard/constants'; -const buildBulkRequestBody = ({ - fetchBulkParams, - organizationId, - locationId, - ...params -}) => ({ - included: { - search: fetchBulkParams(), - }, - ...(organizationId != null ? { organization_id: organizationId } : {}), - ...(locationId != null ? { location_id: locationId } : {}), - ...params, -}); - const fetchOpenscapProxies = () => APIActions.get({ key: OPENSCAP_PROXIES_KEY, @@ -63,7 +48,6 @@ const BulkChangeOpenscapProxyModal = ({ }) => { const dispatch = useDispatch(); const [proxyId, setProxyId] = useState(''); - const [proxySelectOpen, setProxySelectOpen] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); useEffect(() => { @@ -81,25 +65,8 @@ const BulkChangeOpenscapProxyModal = ({ selectAPIStatus(state, OPENSCAP_PROXIES_KEY) ); - const getProxyLabel = id => { - const proxy = proxies?.results?.find( - p => p.id.toString() === id.toString() - ); - return proxy?.name || id; - }; - - const handleProxySelect = (_event, selection) => { - setProxyId(selection); - setProxySelectOpen(false); - }; - - const handleToggleClick = () => { - setProxySelectOpen(!proxySelectOpen); - }; - const handleModalClose = () => { setProxyId(''); - setProxySelectOpen(false); setIsSubmitting(false); closeModal(); }; @@ -144,28 +111,38 @@ const BulkChangeOpenscapProxyModal = ({ const descriptionText = selectAllHostsMode ? ( <> - {__('Assign OpenSCAP capsule for ')} - {__('ALL selected hosts')} + {__('Assign OpenSCAP proxy for ')} + {__('ALL selected hosts.')}
- {__( - '. This will change previous capsule assignments on the selected hosts.' - )} + {__('This will change previous proxy assignments on the selected hosts.')} ) : ( <> - {__('Assign OpenSCAP capsule for ')} + {__('Assign OpenSCAP proxy for ')} {sprintf(__('%s selected hosts.'), selectedCount)}
- {__( - 'This will change previous capsule assignments on the selected hosts.' - )} + {__('This will change previous proxy assignments on the selected hosts.')} ); + const getProxyLabel = id => { + const proxy = proxies?.results?.find( + p => p.id.toString() === id.toString() + ); + return proxy?.name || id; + }; + + const proxyItems = + proxies?.results?.map(proxy => ({ + value: proxy.id.toString(), + content: proxy.name, + onClick: () => setProxyId(proxy.id.toString()), + })) || []; + const modalActions = [