diff --git a/app/controllers/api/v2/hosts_bulk_actions_controller.rb b/app/controllers/api/v2/hosts_bulk_actions_controller.rb index bed35b6f1c..0c7bce1830 100644 --- a/app/controllers/api/v2/hosts_bulk_actions_controller.rb +++ b/app/controllers/api/v2/hosts_bulk_actions_controller.rb @@ -9,7 +9,6 @@ class HostsBulkActionsController < V2::BaseController before_action :validate_power_action, :only => [:change_power_state] def_param_group :bulk_host_ids do - param :organization_id, :number, :required => true, :desc => N_("ID of the organization") param :included, Hash, :desc => N_("Hosts to include in the action"), :required => true, :action_aware => true do param :search, String, :required => false, :desc => N_("Search string describing which hosts to perform the action on") param :ids, Array, :required => false, :desc => N_("List of host ids to perform the action on") diff --git a/app/controllers/concerns/api/v2/bulk_hosts_extension.rb b/app/controllers/concerns/api/v2/bulk_hosts_extension.rb index b6d720964d..49e53b2b77 100644 --- a/app/controllers/concerns/api/v2/bulk_hosts_extension.rb +++ b/app/controllers/concerns/api/v2/bulk_hosts_extension.rb @@ -1,9 +1,10 @@ module Api::V2::BulkHostsExtension extend ActiveSupport::Concern - def bulk_hosts_relation(permission, org) + def bulk_hosts_relation(permission, org, location) relation = ::Host::Managed.authorized(permission) relation = relation.where(organization: org) if org + relation = relation.where(location: location) if location relation end @@ -18,7 +19,8 @@ def find_bulk_hosts(permission, bulk_params, restrict_to = nil) end find_organization - @hosts = bulk_hosts_relation(permission, @organization) + find_location + @hosts = bulk_hosts_relation(permission, @organization, @location) if bulk_params[:included][:ids].present? @hosts = @hosts.where(id: bulk_params[:included][:ids]) @@ -42,4 +44,8 @@ def find_bulk_hosts(permission, bulk_params, restrict_to = nil) def find_organization @organization ||= Organization.find_by_id(params[:organization_id]) end + + def find_location + @location ||= Location.find_by_id(params[:location_id]) + end end diff --git a/test/controllers/api/v2/hosts_bulk_actions_controller_test.rb b/test/controllers/api/v2/hosts_bulk_actions_controller_test.rb index 0312d16308..f03af11fd5 100644 --- a/test/controllers/api/v2/hosts_bulk_actions_controller_test.rb +++ b/test/controllers/api/v2/hosts_bulk_actions_controller_test.rb @@ -17,6 +17,7 @@ def setup def valid_bulk_params(host_ids = @host_ids) { :organization_id => @organization.id, + :location_id => @location.id, :included => { :ids => host_ids, }, @@ -107,6 +108,33 @@ def valid_power_params(host_ids = @host_ids, action = 'start') end end + test "should scope searched hosts by organization and location" do + other_location = FactoryBot.create(:location) + other_host = FactoryBot.create(:host, :managed, :organization => @organization, :location => other_location) + + put :change_owner, params: { + :organization_id => @organization.id, + :location_id => @location.id, + :included => { + :search => 'name ~ *', + }, + :excluded => { + :ids => [], + }, + :owner_id => @user.id_and_type, + } + + assert_response :success + + [@host1, @host2, @host3].each do |host| + host.reload + assert_equal @user.id_and_type, host.is_owned_by + end + + other_host.reload + refute_equal @user.id_and_type, other_host.is_owned_by + end + context "change_power_state" do test "successfully changes power state for all hosts" do Host.any_instance.stubs(:supports_power?).returns(true) diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/__tests__/bulkDelete.test.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/__tests__/bulkDelete.test.js index 5c76648968..f132aaffae 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/__tests__/bulkDelete.test.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/__tests__/bulkDelete.test.js @@ -37,12 +37,16 @@ describe('bulkDeleteHosts', () => { }); const bulkParams = 'id ^ (1,2,3)'; + const organizationId = 1; + const locationId = 2; const selectedCount = 3; const destroyVmOnHostDelete = true; it('dispatches openConfirmModal with correct parameters', () => { bulkDeleteHosts({ bulkParams, + organizationId, + locationId, selectedCount, destroyVmOnHostDelete, })(dispatch); @@ -61,6 +65,8 @@ describe('bulkDeleteHosts', () => { it('calls visit with /new/hosts when onDeleteSuccess is not provided', () => { bulkDeleteHosts({ bulkParams, + organizationId, + locationId, selectedCount, destroyVmOnHostDelete, })(dispatch); @@ -70,8 +76,16 @@ describe('bulkDeleteHosts', () => { expect(APIActions.delete).toHaveBeenCalledTimes(1); const deleteParams = APIActions.delete.mock.calls[0][0]; - - expect(deleteParams.url).toBe(`/api/v2/hosts/bulk?search=${bulkParams}`); + const requestUrl = new URL(deleteParams.url, 'https://example.test'); + + expect(requestUrl.pathname).toBe('/api/v2/hosts/bulk'); + expect(requestUrl.searchParams.get('search')).toBe(bulkParams); + expect(requestUrl.searchParams.get('organization_id')).toBe( + String(organizationId) + ); + expect(requestUrl.searchParams.get('location_id')).toBe( + String(locationId) + ); expect(deleteParams.key).toBe('BULK-HOSTS-DELETE'); expect(typeof deleteParams.successToast).toBe('function'); expect(typeof deleteParams.errorToast).toBe('function'); @@ -86,6 +100,8 @@ describe('bulkDeleteHosts', () => { bulkDeleteHosts({ bulkParams, + organizationId, + locationId, selectedCount, destroyVmOnHostDelete, onDeleteSuccess, @@ -106,6 +122,8 @@ describe('bulkDeleteHosts', () => { it('returns formatted success message with host count', () => { bulkDeleteHosts({ bulkParams, + organizationId, + locationId, selectedCount, destroyVmOnHostDelete, })(dispatch); @@ -124,6 +142,8 @@ describe('bulkDeleteHosts', () => { it('returns error message from response', () => { bulkDeleteHosts({ bulkParams, + organizationId, + locationId, selectedCount, destroyVmOnHostDelete, })(dispatch); diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/assignTaxonomy/BulkAssignTaxonomyModal.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/assignTaxonomy/BulkAssignTaxonomyModal.js index 5bc8273709..3ae3450866 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/assignTaxonomy/BulkAssignTaxonomyModal.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/assignTaxonomy/BulkAssignTaxonomyModal.js @@ -38,6 +38,7 @@ import { API_REQUEST_KEY, } from '../../../../routes/Hosts/constants'; import TaxonomySelect from './TaxonomySelect'; +import { buildBulkRequestBody } from '../helpers'; export const BulkAssignOrganizationModal = props => ( @@ -52,6 +53,8 @@ const BulkAssignTaxonomyModal = ({ selectAllHostsMode, selectedCount, fetchBulkParams, + organizationId, + locationId, modalType, }) => { const org = modalType === MODAL_TYPES.ORGANIZATION; @@ -136,13 +139,13 @@ const BulkAssignTaxonomyModal = ({ }; const handleSave = () => { - const requestBody = { - included: { - search: fetchBulkParams(), - }, + const requestBody = buildBulkRequestBody({ + fetchBulkParams, + organizationId, + locationId, id: taxId, mismatch_setting: fixRadioChecked, - }; + }); org ? dispatch( @@ -243,10 +246,14 @@ BulkAssignTaxonomyModal.propTypes = { selectedCount: PropTypes.number.isRequired, selectAllHostsMode: PropTypes.bool.isRequired, fetchBulkParams: PropTypes.func.isRequired, + organizationId: PropTypes.number, + locationId: PropTypes.number, modalType: PropTypes.string.isRequired, }; BulkAssignTaxonomyModal.defaultProps = { isOpen: false, closeModal: () => {}, + organizationId: undefined, + locationId: undefined, }; diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/assignTaxonomy/index.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/assignTaxonomy/index.js index 61d2fdfc5d..4375e6b447 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/assignTaxonomy/index.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/assignTaxonomy/index.js @@ -7,15 +7,21 @@ import { } from './BulkAssignTaxonomyModal'; export const BulkAssignOrganizationModalScene = ({ isOpen, closeModal }) => { - const { selectAllHostsMode, selectedCount, fetchBulkParams } = useContext( - ForemanActionsBarContext - ); + const { + selectAllHostsMode, + selectedCount, + fetchBulkParams, + organizationId, + locationId, + } = useContext(ForemanActionsBarContext); return ( @@ -23,15 +29,21 @@ export const BulkAssignOrganizationModalScene = ({ isOpen, closeModal }) => { }; export const BulkAssignLocationModalScene = ({ isOpen, closeModal }) => { - const { selectAllHostsMode, selectedCount, fetchBulkParams } = useContext( - ForemanActionsBarContext - ); + const { + selectAllHostsMode, + selectedCount, + fetchBulkParams, + organizationId, + locationId, + } = useContext(ForemanActionsBarContext); return ( diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/buildHosts/BulkBuildHostModal.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/buildHosts/BulkBuildHostModal.js index e492e0b0fc..eea9df3c7e 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/buildHosts/BulkBuildHostModal.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/buildHosts/BulkBuildHostModal.js @@ -12,7 +12,7 @@ import { } from '@patternfly/react-core'; import { addToast } from '../../../ToastsList/slice'; import { translate as __ } from '../../../../common/I18n'; -import { failedHostsToastParams } from '../helpers'; +import { buildBulkRequestBody, failedHostsToastParams } from '../helpers'; import { STATUS } from '../../../../constants'; import { selectAPIStatus } from '../../../../redux/API/APISelectors'; import { bulkBuildHosts, HOST_BUILD_KEY } from './actions'; @@ -22,6 +22,8 @@ const BulkBuildHostModal = ({ closeModal, selectedCount, fetchBulkParams, + organizationId, + locationId, }) => { const dispatch = useDispatch(); const [buildRadioChecked, setBuildRadioChecked] = useState(true); @@ -44,13 +46,13 @@ const BulkBuildHostModal = ({ ); }; const handleSave = () => { - const requestBody = { - included: { - search: fetchBulkParams(), - }, + const requestBody = buildBulkRequestBody({ + fetchBulkParams, + organizationId, + locationId, reboot: rebootChecked, rebuild_configuration: !buildRadioChecked, - }; + }); dispatch(bulkBuildHosts(requestBody, handleModalClose, handleError)); }; @@ -156,11 +158,15 @@ BulkBuildHostModal.propTypes = { closeModal: PropTypes.func, selectedCount: PropTypes.number.isRequired, fetchBulkParams: PropTypes.func.isRequired, + organizationId: PropTypes.number, + locationId: PropTypes.number, }; BulkBuildHostModal.defaultProps = { isOpen: false, closeModal: () => {}, + organizationId: undefined, + locationId: undefined, }; export default BulkBuildHostModal; diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/buildHosts/index.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/buildHosts/index.js index f7e413862e..b143c30973 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/buildHosts/index.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/buildHosts/index.js @@ -4,14 +4,19 @@ import { ForemanActionsBarContext } from '../../../../components/HostDetails/Act import BulkBuildHostModal from './BulkBuildHostModal'; const BulkBuildHostModalScene = ({ isOpen, closeModal }) => { - const { selectedCount, fetchBulkParams } = useContext( - ForemanActionsBarContext - ); + const { + selectedCount, + fetchBulkParams, + organizationId, + locationId, + } = useContext(ForemanActionsBarContext); return ( diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/bulkDelete.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/bulkDelete.js index 6ab889bd29..5c12e9d7f7 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/bulkDelete.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/bulkDelete.js @@ -4,17 +4,24 @@ import { visit, foremanUrl } from '../../../common/helpers'; import { sprintf, translate as __ } from '../../../common/I18n'; import { openConfirmModal } from '../../ConfirmModal'; import { APIActions } from '../../../redux/API'; +import { bulkActionTaxonomyParams } from './helpers'; import './bulkDeleteModal.scss'; export const bulkDeleteHosts = ({ bulkParams, + organizationId, + locationId, selectedCount, destroyVmOnHostDelete, onDeleteSuccess, }) => dispatch => { const successToast = () => sprintf(__('%s hosts deleted'), selectedCount); const errorToast = ({ message }) => message; - const url = foremanUrl(`/api/v2/hosts/bulk?search=${bulkParams}`); + const queryParams = new URLSearchParams({ + search: bulkParams, + ...bulkActionTaxonomyParams({ organizationId, locationId }), + }); + const url = foremanUrl(`/api/v2/hosts/bulk?${queryParams.toString()}`); // TODO: Replace with a checkbox instead of a global setting for cascade host destroy const cascadeMessage = () => diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/changeOwner/BulkChangeOwnerModal.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/changeOwner/BulkChangeOwnerModal.js index 34e7d9b456..d48e9d9e0c 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/changeOwner/BulkChangeOwnerModal.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/changeOwner/BulkChangeOwnerModal.js @@ -34,7 +34,7 @@ import { HOSTS_API_PATH, API_REQUEST_KEY, } from '../../../../routes/Hosts/constants'; -import { failedHostsToastParams } from '../helpers'; +import { buildBulkRequestBody, failedHostsToastParams } from '../helpers'; const BulkChangeOwnerModal = ({ isOpen, @@ -42,6 +42,8 @@ const BulkChangeOwnerModal = ({ selectAllHostsMode, selectedCount, fetchBulkParams, + organizationId, + locationId, }) => { const dispatch = useDispatch(); const [ownerId, setOwnerId] = useState(''); @@ -131,12 +133,12 @@ const BulkChangeOwnerModal = ({ }; const handleConfirm = () => { - const requestBody = { - included: { - search: fetchBulkParams(), - }, + const requestBody = buildBulkRequestBody({ + fetchBulkParams, + organizationId, + locationId, owner_id: ownerId, - }; + }); dispatch(bulkChangeOwner(requestBody, handleSuccess, handleError)); }; @@ -250,11 +252,15 @@ BulkChangeOwnerModal.propTypes = { fetchBulkParams: PropTypes.func.isRequired, selectedCount: PropTypes.number.isRequired, selectAllHostsMode: PropTypes.bool.isRequired, + organizationId: PropTypes.number, + locationId: PropTypes.number, }; BulkChangeOwnerModal.defaultProps = { isOpen: false, closeModal: () => {}, + organizationId: undefined, + locationId: undefined, }; export default BulkChangeOwnerModal; diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/changeOwner/index.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/changeOwner/index.js index fd554d8662..454e56ca50 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/changeOwner/index.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/changeOwner/index.js @@ -9,6 +9,8 @@ const BulkChangeOwnerModalScene = ({ isOpen, closeModal }) => { selectedCount, selectedResults, fetchBulkParams, + organizationId, + locationId, } = useContext(ForemanActionsBarContext); return ( { selectedCount={selectedCount} selectedResults={selectedResults} fetchBulkParams={fetchBulkParams} + organizationId={organizationId} + locationId={locationId} isOpen={isOpen} closeModal={closeModal} /> diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/disassociate/BulkDisassociateModal.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/disassociate/BulkDisassociateModal.js index 17a51be23d..d8b7ec3300 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/disassociate/BulkDisassociateModal.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/disassociate/BulkDisassociateModal.js @@ -18,7 +18,7 @@ import { HOSTS_API_PATH, API_REQUEST_KEY, } from '../../../../routes/Hosts/constants'; -import { failedHostsToastParams } from '../helpers'; +import { buildBulkRequestBody, failedHostsToastParams } from '../helpers'; const BulkDisassociateModal = ({ isOpen, @@ -27,6 +27,8 @@ const BulkDisassociateModal = ({ selectedCount, selectedResults, fetchBulkParams, + organizationId, + locationId, }) => { const dispatch = useDispatch(); const hostsWithComputeResource = selectedResults?.filter( @@ -91,11 +93,12 @@ const BulkDisassociateModal = ({ const queryString = selectedResultsEmpty ? fetchBulkParams() : `id ^ (${hostsWithComputeResource.map(h => h.id).join(',')})`; - const requestBody = { - included: { - search: queryString, - }, - }; + const requestBody = buildBulkRequestBody({ + fetchBulkParams, + organizationId, + locationId, + includedSearch: queryString, + }); dispatch(bulkDisassociate(requestBody, handleSuccess, handleError)); }; @@ -188,12 +191,16 @@ BulkDisassociateModal.propTypes = { fetchBulkParams: PropTypes.func.isRequired, selectedCount: PropTypes.number.isRequired, selectAllHostsMode: PropTypes.bool.isRequired, + organizationId: PropTypes.number, + locationId: PropTypes.number, }; BulkDisassociateModal.defaultProps = { isOpen: false, closeModal: () => {}, selectedResults: [], + organizationId: undefined, + locationId: undefined, }; export default BulkDisassociateModal; diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/disassociate/index.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/disassociate/index.js index 45438eb4f2..169eb72f36 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/disassociate/index.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/disassociate/index.js @@ -9,6 +9,8 @@ const BulkDisassociateModalScene = ({ isOpen, closeModal }) => { selectedCount, selectedResults, fetchBulkParams, + organizationId, + locationId, } = useContext(ForemanActionsBarContext); return ( { selectedCount={selectedCount} selectedResults={selectedResults} fetchBulkParams={fetchBulkParams} + organizationId={organizationId} + locationId={locationId} isOpen={isOpen} closeModal={closeModal} /> diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/helpers.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/helpers.js index 675c0c5bfb..e51c1ba8cc 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/helpers.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/helpers.js @@ -8,6 +8,28 @@ export const searchLink = ({ query, message, baseUrl }) => ({ href: urlWithSearch(baseUrl, query), }); +export const bulkActionTaxonomyParams = ({ + organizationId, + locationId, +} = {}) => ({ + ...(organizationId != null ? { organization_id: organizationId } : {}), + ...(locationId != null ? { location_id: locationId } : {}), +}); + +export const buildBulkRequestBody = ({ + fetchBulkParams, + organizationId, + locationId, + includedSearch, + ...params +}) => ({ + included: { + search: includedSearch || fetchBulkParams(), + }, + ...bulkActionTaxonomyParams({ organizationId, locationId }), + ...params, +}); + export const failedHostsToastParams = ({ message, failed_host_ids: failedHostIds, diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/powerState/BulkPowerStateModal.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/powerState/BulkPowerStateModal.js index a21ab5bfcd..8cb93e5b10 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/powerState/BulkPowerStateModal.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/powerState/BulkPowerStateModal.js @@ -19,7 +19,7 @@ import './BulkPowerStateModal.scss'; import { HostsPowerRefreshContext } from '../../HostsPowerRefreshContext'; import { POWER_STATES, BULK_POWER_STATE_KEY } from './constants'; import { bulkChangePowerState } from './actions'; -import { failedHostsToastParams } from '../helpers'; +import { buildBulkRequestBody, failedHostsToastParams } from '../helpers'; import { addToast } from '../../../ToastsList/slice'; import { HOSTS_API_PATH, @@ -31,6 +31,8 @@ import { APIActions } from '../../../../redux/API'; const BulkPowerStateModal = ({ selectedHostsCount, fetchBulkParams, + organizationId, + locationId, isOpen, closeModal, }) => { @@ -102,12 +104,12 @@ const BulkPowerStateModal = ({ const handleSubmit = () => { setIsLoading(true); - const payload = { - included: { - search: fetchBulkParams(), - }, + const payload = buildBulkRequestBody({ + fetchBulkParams, + organizationId, + locationId, power: selectedPowerState, - }; + }); dispatch(bulkChangePowerState(payload, handleSuccess, handleError)); }; @@ -206,12 +208,16 @@ const BulkPowerStateModal = ({ BulkPowerStateModal.propTypes = { selectedHostsCount: PropTypes.number, fetchBulkParams: PropTypes.func.isRequired, + organizationId: PropTypes.number, + locationId: PropTypes.number, isOpen: PropTypes.bool, closeModal: PropTypes.func, }; BulkPowerStateModal.defaultProps = { selectedHostsCount: 0, + organizationId: undefined, + locationId: undefined, isOpen: false, closeModal: () => {}, }; diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/powerState/index.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/powerState/index.js index 80dcf1ba13..f67e800a9e 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/powerState/index.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/powerState/index.js @@ -4,13 +4,18 @@ import { ForemanActionsBarContext } from '../../../../components/HostDetails/Act import BulkPowerStateModal from './BulkPowerStateModal'; const BulkPowerStateModalScene = ({ isOpen, closeModal }) => { - const { fetchBulkParams, selectedCount = 0 } = useContext( - ForemanActionsBarContext - ); + const { + fetchBulkParams, + selectedCount = 0, + organizationId, + locationId, + } = useContext(ForemanActionsBarContext); return ( diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/reassignHostGroup/BulkReassignHostgroupModal.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/reassignHostGroup/BulkReassignHostgroupModal.js index 308e4ee60f..3e6c051a85 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/reassignHostGroup/BulkReassignHostgroupModal.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/reassignHostGroup/BulkReassignHostgroupModal.js @@ -11,7 +11,7 @@ import { } from '@patternfly/react-core'; import { addToast } from '../../../ToastsList/slice'; import { translate as __ } from '../../../../common/I18n'; -import { failedHostsToastParams } from '../helpers'; +import { buildBulkRequestBody, failedHostsToastParams } from '../helpers'; import { STATUS } from '../../../../constants'; import { selectAPIStatus, @@ -77,6 +77,8 @@ const BulkReassignHostgroupModal = ({ closeModal, selectedCount, fetchBulkParams, + organizationId, + locationId, }) => { const dispatch = useDispatch(); const [hostgroupId, setHostgroupId] = useState(''); @@ -151,12 +153,12 @@ const BulkReassignHostgroupModal = ({ handleModalClose(); }; const handleSave = () => { - const requestBody = { - included: { - search: fetchBulkParams(), - }, + const requestBody = buildBulkRequestBody({ + fetchBulkParams, + organizationId, + locationId, hostgroup_id: hostgroupId, - }; + }); dispatch(bulkReassignHostgroups(requestBody, handleSuccess, handleError)); }; @@ -285,11 +287,15 @@ BulkReassignHostgroupModal.propTypes = { closeModal: PropTypes.func, selectedCount: PropTypes.number.isRequired, fetchBulkParams: PropTypes.func.isRequired, + organizationId: PropTypes.number, + locationId: PropTypes.number, }; BulkReassignHostgroupModal.defaultProps = { isOpen: false, closeModal: () => {}, + organizationId: undefined, + locationId: undefined, }; export default BulkReassignHostgroupModal; diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/reassignHostGroup/index.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/reassignHostGroup/index.js index 4712eaad4d..d7a4669a73 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/reassignHostGroup/index.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/reassignHostGroup/index.js @@ -4,14 +4,19 @@ import { ForemanActionsBarContext } from '../../../../components/HostDetails/Act import BulkReassignHostgroupModal from './BulkReassignHostgroupModal'; const BulkReassignHostgroupModalScene = ({ isOpen, closeModal }) => { - const { selectedCount, fetchBulkParams } = useContext( - ForemanActionsBarContext - ); + const { + selectedCount, + fetchBulkParams, + organizationId, + locationId, + } = useContext(ForemanActionsBarContext); return ( diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/index.js b/webpack/assets/javascripts/react_app/components/HostsIndex/index.js index 83c173ce6a..f8b82efd1e 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/index.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/index.js @@ -45,6 +45,8 @@ import { useForemanSettings, useForemanHostsPageUrl, useForemanContext, + useForemanOrganization, + useForemanLocation, } from '../../Root/Context/ForemanContext'; import { bulkDeleteHosts } from './BulkActions/bulkDelete'; import { @@ -114,6 +116,8 @@ const HostsIndex = () => { syncWithOptions: true, }); const contextData = useForemanContext(); + const currentOrganization = useForemanOrganization(); + const currentLocation = useForemanLocation(); const { response: { @@ -240,6 +244,8 @@ const HostsIndex = () => { dispatch( bulkDeleteHosts({ bulkParams, + organizationId: currentOrganization?.id, + locationId: currentLocation?.id, selectedCount, destroyVmOnHostDelete, onDeleteSuccess: () => { @@ -589,6 +595,8 @@ const HostsIndex = () => { selectedCount, selectedResults, fetchBulkParams, + organizationId: currentOrganization?.id, + locationId: currentLocation?.id, }} > ({ })), useForemanHostsPageUrl: jest.fn(() => '/hosts'), useForemanContext: jest.fn(() => ({})), + useForemanOrganization: jest.fn(() => undefined), + useForemanLocation: jest.fn(() => undefined), })); jest.mock('../common/Slot', () => ({