foreman_puppet 10.1.3 → 11.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/concerns/foreman_puppet/extensions/hosts_controller_extensions.rb +1 -1
- data/app/controllers/foreman_puppet/api/v2/hosts_bulk_actions_controller.rb +75 -1
- data/app/models/concerns/foreman_puppet/extensions/host.rb +2 -2
- data/app/models/concerns/foreman_puppet/extensions/host_common.rb +35 -0
- data/app/models/concerns/foreman_puppet/extensions/hostgroup.rb +10 -3
- data/app/models/foreman_puppet/config_group.rb +6 -0
- data/app/models/foreman_puppet/puppetclass.rb +14 -0
- data/app/services/concerns/foreman_puppet/extensions/bulk_hosts_manager.rb +18 -0
- data/config/api_routes.rb +1 -0
- data/db/seeds.d/111_puppet_proxy_feature.rb +1 -1
- data/lib/foreman_puppet/register.rb +2 -1
- data/lib/foreman_puppet/version.rb +1 -1
- data/test/controllers/foreman_puppet/api/v2/hosts_bulk_actions_controller_test.rb +93 -0
- data/test/models/foreman_puppet/host_test.rb +66 -0
- data/test/models/foreman_puppet/hostgroup_test.rb +32 -0
- data/test/services/foreman_puppet/bulk_hosts_manager_test.rb +45 -0
- data/webpack/__mocks__/foremanReact/redux/API/index.js +8 -0
- data/webpack/global_index.js +18 -1
- data/webpack/src/Extends/Hosts/ActionsBar/index.js +20 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkChangeProxyCommon/__tests__/actions.test.js +0 -7
- data/webpack/src/Extends/Hosts/BulkActions/BulkChangeProxyCommon/index.js +9 -13
- data/webpack/src/Extends/Hosts/BulkActions/BulkChangePuppetCAProxy/__tests__/index.test.js +3 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkChangePuppetCAProxy/index.js +2 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkChangePuppetEnvironment/BulkChangePuppetEnvironmentModal.js +239 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkChangePuppetEnvironment/__tests__/BulkChangePuppetEnvironmentModal.test.js +155 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkChangePuppetEnvironment/__tests__/actions.test.js +49 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkChangePuppetEnvironment/__tests__/index.test.js +66 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkChangePuppetEnvironment/actions.js +37 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkChangePuppetEnvironment/index.js +30 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkChangePuppetProxy/__tests__/index.test.js +160 -44
- data/webpack/src/Extends/Hosts/BulkActions/BulkChangePuppetProxy/index.js +2 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkRemoveProxyCommon/__tests__/actions.test.js +0 -6
- data/webpack/src/Extends/Hosts/BulkActions/BulkRemoveProxyCommon/index.js +8 -13
- data/webpack/src/Extends/Hosts/BulkActions/BulkRemovePuppetCAProxy/__tests__/index.test.js +138 -38
- data/webpack/src/Extends/Hosts/BulkActions/BulkRemovePuppetCAProxy/index.js +2 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkRemovePuppetEnvironment/BulkRemovePuppetEnvironmentModal.js +148 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkRemovePuppetEnvironment/__tests__/BulkRemovePuppetEnvironmentModal.test.js +80 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkRemovePuppetEnvironment/__tests__/index.test.js +66 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkRemovePuppetEnvironment/index.js +30 -0
- data/webpack/src/Extends/Hosts/BulkActions/BulkRemovePuppetProxy/__tests__/index.test.js +125 -33
- data/webpack/src/Extends/Hosts/BulkActions/BulkRemovePuppetProxy/index.js +2 -0
- data/webpack/src/Router/routes.fixtures.js +13 -0
- data/webpack/src/Router/routes.test.js +68 -8
- data/webpack/src/foreman_puppet_host_form.test.js +3 -1
- data/webpack/test_setup.js +1 -0
- metadata +14 -3
- data/webpack/src/Router/__snapshots__/routes.test.js.snap +0 -3
|
@@ -24,6 +24,26 @@ const HostActionsBar = () => {
|
|
|
24
24
|
<Menu ouiaId="content-flyout-menu" onSelect={() => setMenuOpen(false)}>
|
|
25
25
|
<MenuContent>
|
|
26
26
|
<MenuList>
|
|
27
|
+
<MenuItem
|
|
28
|
+
itemId="bulk-change-puppet-environment-menu-item"
|
|
29
|
+
key="bulk-change-puppet-environment-menu-item"
|
|
30
|
+
onClick={() =>
|
|
31
|
+
handleOpenBulkModal('bulk-change-puppet-environment')
|
|
32
|
+
}
|
|
33
|
+
isDisabled={selectedCount === 0}
|
|
34
|
+
>
|
|
35
|
+
{__('Change Puppet environment')}
|
|
36
|
+
</MenuItem>
|
|
37
|
+
<MenuItem
|
|
38
|
+
itemId="bulk-remove-puppet-environment-menu-item"
|
|
39
|
+
key="bulk-remove-puppet-environment-menu-item"
|
|
40
|
+
onClick={() =>
|
|
41
|
+
handleOpenBulkModal('bulk-remove-puppet-environment')
|
|
42
|
+
}
|
|
43
|
+
isDisabled={selectedCount === 0}
|
|
44
|
+
>
|
|
45
|
+
{__('Remove Puppet environment')}
|
|
46
|
+
</MenuItem>
|
|
27
47
|
<MenuItem
|
|
28
48
|
itemId="bulk-change-puppet-proxy-menu-item"
|
|
29
49
|
key="bulk-change-puppet-proxy-menu-item"
|
|
@@ -9,13 +9,6 @@ import {
|
|
|
9
9
|
BULK_CHANGE_PUPPET_PROXY_KEY,
|
|
10
10
|
} from '../actions';
|
|
11
11
|
|
|
12
|
-
jest.mock('foremanReact/redux/API', () => ({
|
|
13
|
-
APIActions: {
|
|
14
|
-
get: jest.fn(),
|
|
15
|
-
put: jest.fn(),
|
|
16
|
-
},
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
12
|
describe('BulkChangeProxyCommon actions', () => {
|
|
20
13
|
const smartProxiesUrl = foremanUrl('/api/smart_proxies');
|
|
21
14
|
const bulkChangeUrl = foremanUrl('/api/v2/hosts/bulk/change_puppet_proxy');
|
|
@@ -15,17 +15,11 @@ import {
|
|
|
15
15
|
} from '@patternfly/react-core';
|
|
16
16
|
import { addToast } from 'foremanReact/components/ToastsList/slice';
|
|
17
17
|
import { sprintf, translate as __ } from 'foremanReact/common/I18n';
|
|
18
|
-
import { foremanUrl } from 'foremanReact/common/helpers';
|
|
19
|
-
import { APIActions } from 'foremanReact/redux/API';
|
|
20
18
|
import { STATUS } from 'foremanReact/constants';
|
|
21
19
|
import {
|
|
22
20
|
selectAPIStatus,
|
|
23
21
|
selectAPIResponse,
|
|
24
22
|
} from 'foremanReact/redux/API/APISelectors';
|
|
25
|
-
import {
|
|
26
|
-
HOSTS_API_PATH,
|
|
27
|
-
API_REQUEST_KEY,
|
|
28
|
-
} from 'foremanReact/routes/Hosts/constants';
|
|
29
23
|
import {
|
|
30
24
|
fetchSmartProxies,
|
|
31
25
|
SMART_PROXY_KEY,
|
|
@@ -47,6 +41,7 @@ const BulkChangeProxyCommon = ({
|
|
|
47
41
|
allHostsMessage,
|
|
48
42
|
someHostsMessage,
|
|
49
43
|
isCAProxy,
|
|
44
|
+
onSuccess: onSuccessCallback,
|
|
50
45
|
}) => {
|
|
51
46
|
const dispatch = useDispatch();
|
|
52
47
|
const [smartProxyId, setSmartProxyId] = useState('');
|
|
@@ -82,6 +77,7 @@ const BulkChangeProxyCommon = ({
|
|
|
82
77
|
|
|
83
78
|
const toggle = toggleRef => (
|
|
84
79
|
<MenuToggle
|
|
80
|
+
ouiaId="bulk-change-proxy-common-select-toggle"
|
|
85
81
|
ref={toggleRef}
|
|
86
82
|
onClick={onToggleClick}
|
|
87
83
|
isExpanded={smartProxySelectOpen}
|
|
@@ -112,13 +108,11 @@ const BulkChangeProxyCommon = ({
|
|
|
112
108
|
message: response.data.message,
|
|
113
109
|
})
|
|
114
110
|
);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
121
|
-
handleModalClose();
|
|
111
|
+
try {
|
|
112
|
+
if (onSuccessCallback) onSuccessCallback();
|
|
113
|
+
} finally {
|
|
114
|
+
handleModalClose();
|
|
115
|
+
}
|
|
122
116
|
};
|
|
123
117
|
|
|
124
118
|
const handleConfirm = () => {
|
|
@@ -242,11 +236,13 @@ BulkChangeProxyCommon.propTypes = {
|
|
|
242
236
|
allHostsMessage: PropTypes.string.isRequired,
|
|
243
237
|
someHostsMessage: PropTypes.string.isRequired,
|
|
244
238
|
isCAProxy: PropTypes.bool.isRequired,
|
|
239
|
+
onSuccess: PropTypes.func,
|
|
245
240
|
};
|
|
246
241
|
|
|
247
242
|
BulkChangeProxyCommon.defaultProps = {
|
|
248
243
|
isOpen: false,
|
|
249
244
|
closeModal: () => {},
|
|
245
|
+
onSuccess: undefined,
|
|
250
246
|
};
|
|
251
247
|
|
|
252
248
|
export default BulkChangeProxyCommon;
|
|
@@ -18,11 +18,13 @@ jest.mock('../../BulkChangeProxyCommon', () => ({
|
|
|
18
18
|
|
|
19
19
|
describe('BulkChangePuppetCAProxyScene', () => {
|
|
20
20
|
const fetchBulkParams = jest.fn();
|
|
21
|
+
const refreshTableData = jest.fn();
|
|
21
22
|
const contextValue = {
|
|
22
23
|
selectAllHostsMode: false,
|
|
23
24
|
selectedCount: 2,
|
|
24
25
|
selectedResults: [1, 2],
|
|
25
26
|
fetchBulkParams,
|
|
27
|
+
refreshTableData,
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
beforeEach(() => {
|
|
@@ -51,6 +53,7 @@ describe('BulkChangePuppetCAProxyScene', () => {
|
|
|
51
53
|
selectAllHostsMode: false,
|
|
52
54
|
isOpen: true,
|
|
53
55
|
closeModal: expect.any(Function),
|
|
56
|
+
onSuccess: refreshTableData,
|
|
54
57
|
selectMessage: 'Select a Puppet CA Proxy',
|
|
55
58
|
handleErrorMessage: 'Failed to change Puppet CA Proxy',
|
|
56
59
|
changeMessage: 'Change Puppet CA Proxy',
|
|
@@ -11,6 +11,7 @@ const BulkChangePuppetCAProxyScene = () => {
|
|
|
11
11
|
selectedCount,
|
|
12
12
|
selectedResults,
|
|
13
13
|
fetchBulkParams,
|
|
14
|
+
refreshTableData,
|
|
14
15
|
} = useContext(ForemanActionsBarContext);
|
|
15
16
|
const { isOpen, close: closeModal } = useBulkModalOpen(
|
|
16
17
|
'bulk-change-puppet-ca-proxy'
|
|
@@ -24,6 +25,7 @@ const BulkChangePuppetCAProxyScene = () => {
|
|
|
24
25
|
selectAllHostsMode={selectAllHostsMode}
|
|
25
26
|
isOpen={isOpen}
|
|
26
27
|
closeModal={closeModal}
|
|
28
|
+
onSuccess={refreshTableData}
|
|
27
29
|
selectMessage={__('Select a Puppet CA Proxy')}
|
|
28
30
|
handleErrorMessage={__('Failed to change Puppet CA Proxy')}
|
|
29
31
|
changeMessage={__('Change Puppet CA Proxy')}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { FormattedMessage } from 'react-intl';
|
|
4
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
5
|
+
import {
|
|
6
|
+
Modal,
|
|
7
|
+
Button,
|
|
8
|
+
TextContent,
|
|
9
|
+
Text,
|
|
10
|
+
Select,
|
|
11
|
+
SelectOption,
|
|
12
|
+
SelectList,
|
|
13
|
+
MenuToggle,
|
|
14
|
+
} from '@patternfly/react-core';
|
|
15
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
16
|
+
import { addToast } from 'foremanReact/components/ToastsList/slice';
|
|
17
|
+
import { useForemanOrganization } from 'foremanReact/Root/Context/ForemanContext';
|
|
18
|
+
import {
|
|
19
|
+
selectAPIStatus,
|
|
20
|
+
selectAPIResponse,
|
|
21
|
+
} from 'foremanReact/redux/API/APISelectors';
|
|
22
|
+
import { STATUS } from 'foremanReact/constants';
|
|
23
|
+
import { bulkActionErrorToastParams } from '../toastHelpers';
|
|
24
|
+
import {
|
|
25
|
+
fetchEnvironments,
|
|
26
|
+
bulkChangePuppetEnvironment,
|
|
27
|
+
PUPPET_ENVIRONMENTS_KEY,
|
|
28
|
+
BULK_CHANGE_PUPPET_ENVIRONMENT_KEY,
|
|
29
|
+
INHERIT_ENVIRONMENT,
|
|
30
|
+
} from './actions';
|
|
31
|
+
|
|
32
|
+
const BulkChangePuppetEnvironmentModal = ({
|
|
33
|
+
isOpen,
|
|
34
|
+
closeModal,
|
|
35
|
+
selectAllHostsMode,
|
|
36
|
+
selectedCount,
|
|
37
|
+
fetchBulkParams,
|
|
38
|
+
onSuccess: onSuccessCallback,
|
|
39
|
+
}) => {
|
|
40
|
+
const dispatch = useDispatch();
|
|
41
|
+
const [environmentId, setEnvironmentId] = useState(null);
|
|
42
|
+
const [environmentSelectOpen, setEnvironmentSelectOpen] = useState(false);
|
|
43
|
+
const currentOrganization = useForemanOrganization();
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
dispatch(fetchEnvironments(currentOrganization?.id));
|
|
47
|
+
}, [dispatch, currentOrganization?.id]);
|
|
48
|
+
|
|
49
|
+
const environments = useSelector(state =>
|
|
50
|
+
selectAPIResponse(state, PUPPET_ENVIRONMENTS_KEY)
|
|
51
|
+
);
|
|
52
|
+
const environmentsStatus = useSelector(state =>
|
|
53
|
+
selectAPIStatus(state, PUPPET_ENVIRONMENTS_KEY)
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const onToggleClick = () => {
|
|
57
|
+
setEnvironmentSelectOpen(!environmentSelectOpen);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const handleEnvironmentSelect = (event, selection) => {
|
|
61
|
+
setEnvironmentId(selection);
|
|
62
|
+
setEnvironmentSelectOpen(false);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const getEnvironmentLabel = value => {
|
|
66
|
+
if (value === INHERIT_ENVIRONMENT) return __('*Inherit from host group*');
|
|
67
|
+
|
|
68
|
+
const selectedEnvironment = environments?.results?.find(
|
|
69
|
+
environment => `${environment.id}` === value
|
|
70
|
+
);
|
|
71
|
+
return selectedEnvironment?.name || __('Select an Environment');
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const toggle = toggleRef => (
|
|
75
|
+
<MenuToggle
|
|
76
|
+
ouiaId="bulk-change-env-common-select-toggle"
|
|
77
|
+
ref={toggleRef}
|
|
78
|
+
onClick={onToggleClick}
|
|
79
|
+
isExpanded={environmentSelectOpen}
|
|
80
|
+
style={{ width: '500px' }}
|
|
81
|
+
>
|
|
82
|
+
{environmentId
|
|
83
|
+
? getEnvironmentLabel(environmentId)
|
|
84
|
+
: __('Select an Environment')}
|
|
85
|
+
</MenuToggle>
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const handleModalClose = () => {
|
|
89
|
+
setEnvironmentId(null);
|
|
90
|
+
closeModal();
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const handleError = response => {
|
|
94
|
+
handleModalClose();
|
|
95
|
+
dispatch(
|
|
96
|
+
addToast(
|
|
97
|
+
bulkActionErrorToastParams(
|
|
98
|
+
response,
|
|
99
|
+
__('Failed to change Puppet Environment'),
|
|
100
|
+
BULK_CHANGE_PUPPET_ENVIRONMENT_KEY
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const handleSuccess = response => {
|
|
107
|
+
dispatch(
|
|
108
|
+
addToast({
|
|
109
|
+
type: 'success',
|
|
110
|
+
message: response.data.message,
|
|
111
|
+
})
|
|
112
|
+
);
|
|
113
|
+
try {
|
|
114
|
+
if (onSuccessCallback) onSuccessCallback();
|
|
115
|
+
} finally {
|
|
116
|
+
handleModalClose();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const handleConfirm = () => {
|
|
121
|
+
const requestBody = {
|
|
122
|
+
included: {
|
|
123
|
+
search: fetchBulkParams(),
|
|
124
|
+
},
|
|
125
|
+
environment_id: environmentId,
|
|
126
|
+
organization_id: currentOrganization?.id,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
dispatch(
|
|
130
|
+
bulkChangePuppetEnvironment(requestBody, handleSuccess, handleError)
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const modalActions = [
|
|
135
|
+
<Button
|
|
136
|
+
key="add"
|
|
137
|
+
ouiaId="bulk-change-environment-modal-add-button"
|
|
138
|
+
variant="primary"
|
|
139
|
+
onClick={handleConfirm}
|
|
140
|
+
isDisabled={environmentId === null}
|
|
141
|
+
isLoading={environmentsStatus === STATUS.PENDING}
|
|
142
|
+
>
|
|
143
|
+
{__('Change Puppet Environment')}
|
|
144
|
+
</Button>,
|
|
145
|
+
<Button
|
|
146
|
+
key="cancel"
|
|
147
|
+
ouiaId="bulk-change-environment-modal-cancel-button"
|
|
148
|
+
variant="link"
|
|
149
|
+
onClick={handleModalClose}
|
|
150
|
+
>
|
|
151
|
+
{__('Cancel')}
|
|
152
|
+
</Button>,
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<Modal
|
|
157
|
+
isOpen={isOpen}
|
|
158
|
+
onClose={handleModalClose}
|
|
159
|
+
onEscapePress={handleModalClose}
|
|
160
|
+
title={__('Change Puppet Environment')}
|
|
161
|
+
width="50%"
|
|
162
|
+
position="top"
|
|
163
|
+
actions={modalActions}
|
|
164
|
+
id="bulk-change-puppet-environment"
|
|
165
|
+
key="bulk-change-puppet-environment"
|
|
166
|
+
ouiaId="bulk-change-puppet-environment"
|
|
167
|
+
>
|
|
168
|
+
<TextContent>
|
|
169
|
+
<Text ouiaId="bulk-change-puppet-environment-options">
|
|
170
|
+
{selectAllHostsMode ? (
|
|
171
|
+
<FormattedMessage
|
|
172
|
+
id="bulk-change-puppet-environment-warning-message-all"
|
|
173
|
+
defaultMessage={__(
|
|
174
|
+
'Changing the Puppet environment will affect {boldCount} selected hosts. Some hosts may already have been associated with the selected environment.'
|
|
175
|
+
)}
|
|
176
|
+
values={{
|
|
177
|
+
boldCount: <strong>{__('All')}</strong>,
|
|
178
|
+
}}
|
|
179
|
+
/>
|
|
180
|
+
) : (
|
|
181
|
+
<FormattedMessage
|
|
182
|
+
id="bulk-change-puppet-environment-warning-message"
|
|
183
|
+
defaultMessage={__(
|
|
184
|
+
'Changing the Puppet environment will affect {boldCount} selected {count, plural, one {host} other {hosts}}. Some hosts may already have been associated with the selected environment.'
|
|
185
|
+
)}
|
|
186
|
+
values={{
|
|
187
|
+
count: selectedCount,
|
|
188
|
+
boldCount: <strong>{selectedCount}</strong>,
|
|
189
|
+
}}
|
|
190
|
+
/>
|
|
191
|
+
)}
|
|
192
|
+
</Text>
|
|
193
|
+
</TextContent>
|
|
194
|
+
{environmentsStatus === STATUS.RESOLVED && (
|
|
195
|
+
<Select
|
|
196
|
+
id="bulk-change-puppet-environment-select"
|
|
197
|
+
isOpen={environmentSelectOpen}
|
|
198
|
+
selected={environmentId}
|
|
199
|
+
onSelect={handleEnvironmentSelect}
|
|
200
|
+
onOpenChange={isSelectOpen => setEnvironmentSelectOpen(isSelectOpen)}
|
|
201
|
+
toggle={toggle}
|
|
202
|
+
shouldFocusToggleOnSelect
|
|
203
|
+
ouiaId="bulk-change-puppet-environment-select"
|
|
204
|
+
>
|
|
205
|
+
<SelectList>
|
|
206
|
+
<SelectOption value={INHERIT_ENVIRONMENT}>
|
|
207
|
+
{__('*Inherit from host group*')}
|
|
208
|
+
</SelectOption>
|
|
209
|
+
{environments?.results?.map(environment => (
|
|
210
|
+
<SelectOption
|
|
211
|
+
key={`${environment.id}`}
|
|
212
|
+
value={`${environment.id}`}
|
|
213
|
+
>
|
|
214
|
+
{environment.name}
|
|
215
|
+
</SelectOption>
|
|
216
|
+
))}
|
|
217
|
+
</SelectList>
|
|
218
|
+
</Select>
|
|
219
|
+
)}
|
|
220
|
+
</Modal>
|
|
221
|
+
);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
BulkChangePuppetEnvironmentModal.propTypes = {
|
|
225
|
+
isOpen: PropTypes.bool,
|
|
226
|
+
closeModal: PropTypes.func,
|
|
227
|
+
fetchBulkParams: PropTypes.func.isRequired,
|
|
228
|
+
onSuccess: PropTypes.func,
|
|
229
|
+
selectedCount: PropTypes.number.isRequired,
|
|
230
|
+
selectAllHostsMode: PropTypes.bool.isRequired,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
BulkChangePuppetEnvironmentModal.defaultProps = {
|
|
234
|
+
isOpen: false,
|
|
235
|
+
closeModal: () => {},
|
|
236
|
+
onSuccess: undefined,
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
export default BulkChangePuppetEnvironmentModal;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import { IntlProvider } from 'react-intl';
|
|
5
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
6
|
+
|
|
7
|
+
import BulkChangePuppetEnvironmentModal from '../BulkChangePuppetEnvironmentModal';
|
|
8
|
+
import { fetchEnvironments, bulkChangePuppetEnvironment } from '../actions';
|
|
9
|
+
import {
|
|
10
|
+
selectAPIResponse,
|
|
11
|
+
selectAPIStatus,
|
|
12
|
+
} from 'foremanReact/redux/API/APISelectors';
|
|
13
|
+
|
|
14
|
+
jest.mock('@patternfly/react-core', () => {
|
|
15
|
+
const React = require('react');
|
|
16
|
+
const actual = jest.requireActual('@patternfly/react-core');
|
|
17
|
+
|
|
18
|
+
const MenuToggle = React.forwardRef(({ children, onClick }, ref) => (
|
|
19
|
+
<button type="button" ref={ref} onClick={onClick}>
|
|
20
|
+
{children}
|
|
21
|
+
</button>
|
|
22
|
+
));
|
|
23
|
+
|
|
24
|
+
const SelectOption = ({ children, value, onSelect }) => (
|
|
25
|
+
<button type="button" onClick={event => onSelect(event, value)}>
|
|
26
|
+
{children}
|
|
27
|
+
</button>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const SelectList = ({ children, onSelect }) => (
|
|
31
|
+
<div>
|
|
32
|
+
{React.Children.map(children, child =>
|
|
33
|
+
React.isValidElement(child)
|
|
34
|
+
? React.cloneElement(child, { onSelect })
|
|
35
|
+
: child
|
|
36
|
+
)}
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const Select = ({ children, isOpen, toggle, onSelect }) => (
|
|
41
|
+
<div>
|
|
42
|
+
{toggle()}
|
|
43
|
+
{isOpen &&
|
|
44
|
+
React.Children.map(children, child =>
|
|
45
|
+
React.isValidElement(child)
|
|
46
|
+
? React.cloneElement(child, { onSelect })
|
|
47
|
+
: child
|
|
48
|
+
)}
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
...actual,
|
|
54
|
+
MenuToggle,
|
|
55
|
+
Select,
|
|
56
|
+
SelectList,
|
|
57
|
+
SelectOption,
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
jest.mock('react-redux', () => ({
|
|
62
|
+
...jest.requireActual('react-redux'),
|
|
63
|
+
useDispatch: jest.fn(),
|
|
64
|
+
useSelector: jest.fn(),
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
jest.mock('foremanReact/Root/Context/ForemanContext', () => ({
|
|
68
|
+
useForemanOrganization: jest.fn(() => ({ id: 23 })),
|
|
69
|
+
}));
|
|
70
|
+
|
|
71
|
+
jest.mock('foremanReact/redux/API/APISelectors', () => ({
|
|
72
|
+
selectAPIResponse: jest.fn(),
|
|
73
|
+
selectAPIStatus: jest.fn(),
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
jest.mock('../actions', () => ({
|
|
77
|
+
fetchEnvironments: jest.fn(() => ({ type: 'FETCH_ENVIRONMENTS' })),
|
|
78
|
+
bulkChangePuppetEnvironment: jest.fn(() => ({
|
|
79
|
+
type: 'BULK_CHANGE_PUPPET_ENVIRONMENT',
|
|
80
|
+
})),
|
|
81
|
+
PUPPET_ENVIRONMENTS_KEY: 'PUPPET_ENVIRONMENTS_KEY',
|
|
82
|
+
BULK_CHANGE_PUPPET_ENVIRONMENT_KEY: 'BULK_CHANGE_PUPPET_ENVIRONMENT',
|
|
83
|
+
INHERIT_ENVIRONMENT: 'inherit',
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
describe('BulkChangePuppetEnvironmentModal', () => {
|
|
87
|
+
const dispatch = jest.fn();
|
|
88
|
+
const fetchBulkParams = jest.fn(
|
|
89
|
+
() => 'organization = "Default Organization"'
|
|
90
|
+
);
|
|
91
|
+
const environmentsResponse = {
|
|
92
|
+
results: [{ id: 1, name: 'production' }],
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const renderComponent = () =>
|
|
96
|
+
render(
|
|
97
|
+
<IntlProvider locale="en">
|
|
98
|
+
<BulkChangePuppetEnvironmentModal
|
|
99
|
+
isOpen
|
|
100
|
+
closeModal={jest.fn()}
|
|
101
|
+
fetchBulkParams={fetchBulkParams}
|
|
102
|
+
selectedCount={2}
|
|
103
|
+
selectAllHostsMode={false}
|
|
104
|
+
/>
|
|
105
|
+
</IntlProvider>
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
beforeEach(() => {
|
|
109
|
+
jest.clearAllMocks();
|
|
110
|
+
useDispatch.mockReturnValue(dispatch);
|
|
111
|
+
selectAPIResponse.mockReturnValue(environmentsResponse);
|
|
112
|
+
selectAPIStatus.mockReturnValue('RESOLVED');
|
|
113
|
+
useSelector.mockImplementation(selector => selector({}));
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('renders the environment options', () => {
|
|
117
|
+
renderComponent();
|
|
118
|
+
|
|
119
|
+
expect(fetchEnvironments).toHaveBeenCalledWith(23);
|
|
120
|
+
expect(
|
|
121
|
+
screen.getAllByText('Change Puppet Environment')[0]
|
|
122
|
+
).toBeInTheDocument();
|
|
123
|
+
expect(
|
|
124
|
+
screen.getByRole('button', { name: 'Select an Environment' })
|
|
125
|
+
).toBeInTheDocument();
|
|
126
|
+
expect(
|
|
127
|
+
screen.getByText(/Changing the Puppet environment will affect/)
|
|
128
|
+
).toBeInTheDocument();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('submits organization scope with the bulk change request', () => {
|
|
132
|
+
renderComponent();
|
|
133
|
+
|
|
134
|
+
fireEvent.click(
|
|
135
|
+
screen.getByRole('button', { name: 'Select an Environment' })
|
|
136
|
+
);
|
|
137
|
+
expect(screen.getByText('*Inherit from host group*')).toBeInTheDocument();
|
|
138
|
+
fireEvent.click(screen.getByText('production'));
|
|
139
|
+
fireEvent.click(
|
|
140
|
+
screen.getByRole('button', { name: 'Change Puppet Environment' })
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
expect(bulkChangePuppetEnvironment).toHaveBeenCalledWith(
|
|
144
|
+
{
|
|
145
|
+
included: {
|
|
146
|
+
search: 'organization = "Default Organization"',
|
|
147
|
+
},
|
|
148
|
+
environment_id: '1',
|
|
149
|
+
organization_id: 23,
|
|
150
|
+
},
|
|
151
|
+
expect.any(Function),
|
|
152
|
+
expect.any(Function)
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
});
|
data/webpack/src/Extends/Hosts/BulkActions/BulkChangePuppetEnvironment/__tests__/actions.test.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { APIActions } from 'foremanReact/redux/API';
|
|
2
|
+
import {
|
|
3
|
+
fetchEnvironments,
|
|
4
|
+
PUPPET_ENVIRONMENTS_KEY,
|
|
5
|
+
bulkChangePuppetEnvironment,
|
|
6
|
+
BULK_CHANGE_PUPPET_ENVIRONMENT_KEY,
|
|
7
|
+
} from '../actions';
|
|
8
|
+
|
|
9
|
+
jest.mock('foremanReact/redux/API', () => ({
|
|
10
|
+
APIActions: {
|
|
11
|
+
get: jest.fn(),
|
|
12
|
+
put: jest.fn(),
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
describe('BulkChangePuppetEnvironment actions', () => {
|
|
17
|
+
const environmentsUrl = '/foreman_puppet/api/v2/environments';
|
|
18
|
+
const bulkChangeUrl = '/api/v2/hosts/bulk/change_puppet_environment';
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
jest.clearAllMocks();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('fetches environments', () => {
|
|
25
|
+
fetchEnvironments(23);
|
|
26
|
+
|
|
27
|
+
expect(APIActions.get).toHaveBeenCalledWith({
|
|
28
|
+
key: PUPPET_ENVIRONMENTS_KEY,
|
|
29
|
+
url: environmentsUrl,
|
|
30
|
+
params: { per_page: 'all', organization_id: 23 },
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('calls bulk change puppet environment endpoint', () => {
|
|
35
|
+
const params = { included: { ids: [1] }, environment_id: '1' };
|
|
36
|
+
const handleSuccess = jest.fn();
|
|
37
|
+
const handleError = jest.fn();
|
|
38
|
+
|
|
39
|
+
bulkChangePuppetEnvironment(params, handleSuccess, handleError);
|
|
40
|
+
|
|
41
|
+
expect(APIActions.put).toHaveBeenCalledWith({
|
|
42
|
+
key: BULK_CHANGE_PUPPET_ENVIRONMENT_KEY,
|
|
43
|
+
url: bulkChangeUrl,
|
|
44
|
+
handleSuccess,
|
|
45
|
+
handleError,
|
|
46
|
+
params,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
data/webpack/src/Extends/Hosts/BulkActions/BulkChangePuppetEnvironment/__tests__/index.test.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer, { act } from 'react-test-renderer';
|
|
3
|
+
|
|
4
|
+
import { useBulkModalOpen } from 'foremanReact/common/BulkModalStateHelper';
|
|
5
|
+
import { ForemanActionsBarContext } from 'foremanReact/components/HostDetails/ActionsBar';
|
|
6
|
+
|
|
7
|
+
import BulkChangePuppetEnvironmentScene from '../index';
|
|
8
|
+
import BulkChangePuppetEnvironmentModal from '../BulkChangePuppetEnvironmentModal';
|
|
9
|
+
|
|
10
|
+
jest.mock('foremanReact/common/BulkModalStateHelper', () => ({
|
|
11
|
+
useBulkModalOpen: jest.fn(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
jest.mock('../BulkChangePuppetEnvironmentModal', () => ({
|
|
15
|
+
__esModule: true,
|
|
16
|
+
default: jest.fn(() => null),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
describe('BulkChangePuppetEnvironmentScene', () => {
|
|
20
|
+
const fetchBulkParams = jest.fn();
|
|
21
|
+
const refreshTableData = jest.fn();
|
|
22
|
+
const contextValue = {
|
|
23
|
+
selectAllHostsMode: false,
|
|
24
|
+
selectedCount: 2,
|
|
25
|
+
selectedResults: [1, 2],
|
|
26
|
+
fetchBulkParams,
|
|
27
|
+
refreshTableData,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
jest.clearAllMocks();
|
|
32
|
+
useBulkModalOpen.mockReturnValue({
|
|
33
|
+
isOpen: true,
|
|
34
|
+
close: jest.fn(),
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('opens with bulk modal state and passes expected props', () => {
|
|
39
|
+
let component;
|
|
40
|
+
act(() => {
|
|
41
|
+
component = renderer.create(
|
|
42
|
+
<ForemanActionsBarContext.Provider value={contextValue}>
|
|
43
|
+
<BulkChangePuppetEnvironmentScene />
|
|
44
|
+
</ForemanActionsBarContext.Provider>
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const componentType =
|
|
49
|
+
BulkChangePuppetEnvironmentModal.default ||
|
|
50
|
+
BulkChangePuppetEnvironmentModal;
|
|
51
|
+
const { props } = component.root.findByType(componentType);
|
|
52
|
+
|
|
53
|
+
expect(props).toEqual(
|
|
54
|
+
expect.objectContaining({
|
|
55
|
+
fetchBulkParams,
|
|
56
|
+
selectedCount: 2,
|
|
57
|
+
selectAllHostsMode: false,
|
|
58
|
+
isOpen: true,
|
|
59
|
+
closeModal: expect.any(Function),
|
|
60
|
+
onSuccess: refreshTableData,
|
|
61
|
+
})
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
component.unmount();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { APIActions } from 'foremanReact/redux/API';
|
|
2
|
+
import { foremanUrl } from 'foremanReact/common/helpers';
|
|
3
|
+
|
|
4
|
+
export const PUPPET_ENVIRONMENTS_KEY = 'PUPPET_ENVIRONMENTS_KEY';
|
|
5
|
+
export const BULK_CHANGE_PUPPET_ENVIRONMENT_KEY =
|
|
6
|
+
'BULK_CHANGE_PUPPET_ENVIRONMENT';
|
|
7
|
+
|
|
8
|
+
export const INHERIT_ENVIRONMENT = 'inherit';
|
|
9
|
+
|
|
10
|
+
export const fetchEnvironments = organizationId => {
|
|
11
|
+
const url = foremanUrl('/foreman_puppet/api/v2/environments');
|
|
12
|
+
return APIActions.get({
|
|
13
|
+
key: PUPPET_ENVIRONMENTS_KEY,
|
|
14
|
+
url,
|
|
15
|
+
params: {
|
|
16
|
+
per_page: 'all',
|
|
17
|
+
...(organizationId ? { organization_id: organizationId } : {}),
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const bulkChangePuppetEnvironment = (
|
|
23
|
+
params,
|
|
24
|
+
handleSuccess,
|
|
25
|
+
handleError
|
|
26
|
+
) => {
|
|
27
|
+
const url = foremanUrl('/api/v2/hosts/bulk/change_puppet_environment');
|
|
28
|
+
return APIActions.put({
|
|
29
|
+
key: BULK_CHANGE_PUPPET_ENVIRONMENT_KEY,
|
|
30
|
+
url,
|
|
31
|
+
handleSuccess,
|
|
32
|
+
handleError,
|
|
33
|
+
params,
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default fetchEnvironments;
|