foreman_rh_cloud 12.0.0 → 12.1.1

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -4
  3. data/app/controllers/concerns/inventory_upload/report_actions.rb +1 -1
  4. data/app/controllers/foreman_inventory_upload/accounts_controller.rb +2 -0
  5. data/app/controllers/foreman_inventory_upload/uploads_settings_controller.rb +2 -0
  6. data/app/controllers/insights_cloud/api/machine_telemetries_controller.rb +26 -22
  7. data/app/services/foreman_rh_cloud/cloud_request.rb +13 -3
  8. data/app/services/foreman_rh_cloud/cloud_request_forwarder.rb +17 -3
  9. data/app/services/foreman_rh_cloud/rules_ingester.rb +12 -1
  10. data/config/routes.rb +4 -0
  11. data/lib/foreman_inventory_upload/generators/queries.rb +2 -0
  12. data/lib/foreman_inventory_upload/generators/slice.rb +83 -42
  13. data/lib/foreman_inventory_upload.rb +5 -0
  14. data/lib/foreman_rh_cloud/engine.rb +15 -3
  15. data/lib/foreman_rh_cloud/version.rb +1 -1
  16. data/lib/foreman_rh_cloud.rb +2 -0
  17. data/package.json +1 -1
  18. data/test/controllers/insights_cloud/api/machine_telemetries_controller_test.rb +35 -2
  19. data/test/unit/slice_generator_test.rb +212 -4
  20. data/webpack/ForemanInventoryUpload/Components/Dashboard/Dashboard.js +5 -1
  21. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/Dashboard.test.js.snap +2 -1
  22. data/webpack/ForemanInventoryUpload/Components/InventorySettings/AdvancedSetting/AdvancedSettingsConstants.js +27 -8
  23. data/webpack/ForemanInventoryUpload/Components/InventorySettings/AdvancedSetting/index.js +11 -3
  24. data/webpack/ForemanInventoryUpload/Components/InventorySettings/InventorySettings.js +71 -10
  25. data/webpack/ForemanInventoryUpload/Components/InventorySettings/InventorySettingsSelectors.js +13 -1
  26. data/webpack/ForemanInventoryUpload/Components/InventorySettings/MinimalInventoryDropdown.js +107 -0
  27. data/webpack/ForemanInventoryUpload/Components/NavContainer/NavContainer.fixtures.js +9 -2
  28. data/webpack/ForemanInventoryUpload/Components/NavContainer/NavContainer.js +8 -0
  29. data/webpack/ForemanInventoryUpload/Components/NavContainer/__tests__/NavContainer.test.js +53 -9
  30. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/PageDescription/PageDescription.js +72 -58
  31. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/PageDescription/__tests__/PageDescription.test.js +61 -6
  32. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/ToolbarButtons.js +18 -6
  33. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/__tests__/ToolbarButtons.test.js +54 -6
  34. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/ReportGenerate.fixtures.js +2 -0
  35. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/ReportGenerate.js +8 -0
  36. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/__tests__/__snapshots__/ReportGenerate.test.js.snap +4 -2
  37. data/webpack/ForemanInventoryUpload/Components/ReportUpload/ReportUpload.js +1 -8
  38. data/webpack/ForemanInventoryUpload/Components/ReportUpload/__tests__/__snapshots__/ReportUpload.test.js.snap +4 -2
  39. data/webpack/ForemanInventoryUpload/Components/TabHeader/TabHeader.js +47 -28
  40. data/webpack/ForemanInventoryUpload/Components/TabHeader/__tests__/TabHeader.test.js +46 -8
  41. data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/InventoryAutoUpload.js +2 -7
  42. data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/__tests__/__snapshots__/InventoryAutoUpload.test.js.snap +1 -6
  43. data/webpack/common/Switcher/SwitcherPF4.js +11 -1
  44. metadata +4 -11
  45. data/webpack/ForemanInventoryUpload/Components/InventorySettings/__tests__/InventorySettings.test.js +0 -13
  46. data/webpack/ForemanInventoryUpload/Components/InventorySettings/__tests__/__snapshots__/InventorySettings.test.js.snap +0 -31
  47. data/webpack/ForemanInventoryUpload/Components/NavContainer/__tests__/__snapshots__/NavContainer.test.js.snap +0 -89
  48. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/PageDescription/__tests__/__snapshots__/PageDescription.test.js.snap +0 -62
  49. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/__tests__/__snapshots__/ToolbarButtons.test.js.snap +0 -10
  50. data/webpack/ForemanInventoryUpload/Components/TabHeader/__tests__/__snapshots__/TabHeader.test.js.snap +0 -43
  51. data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/components/AdvancedSettings/AdvancedSettings.js +0 -72
  52. data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/components/AdvancedSettings/index.js +0 -23
@@ -35,6 +35,12 @@ class SliceGeneratorTest < ActiveSupport::TestCase
35
35
  ForemanInventoryUpload::Generators::Queries.instance_variable_set(:@fact_names, nil)
36
36
  end
37
37
 
38
+ def create_fact_values(host, facts)
39
+ facts.each do |fact_name, value|
40
+ FactoryBot.create(:fact_value, fact_name: fact_names[fact_name], value: value, host: host)
41
+ end
42
+ end
43
+
38
44
  def interesting_facts
39
45
  [
40
46
  'dmi::system::uuid',
@@ -52,6 +58,8 @@ class SliceGeneratorTest < ActiveSupport::TestCase
52
58
  'distribution::version',
53
59
  'distribution::id',
54
60
  'virt::is_guest',
61
+ 'hypervisor::type',
62
+ 'hypervisor::version',
55
63
  'dmi::system::manufacturer',
56
64
  'dmi::system::product_name',
57
65
  'dmi::chassis::asset_tag',
@@ -98,10 +106,210 @@ class SliceGeneratorTest < ActiveSupport::TestCase
98
106
  assert_equal 'test_nic1', actual_nic['name']
99
107
  end
100
108
 
109
+ test 'generates a report with minimal data collection' do
110
+ Setting[:insights_minimal_data_collection] = true
111
+ create_fact_values(@host,
112
+ 'dmi::system::uuid' => 'D30B0B42-7824-2635-C62D-491394DE43F7',
113
+ 'dmi::bios::vendor' => 'SeaBios',
114
+ 'dmi::bios::version' => '10',
115
+ 'cpu::cpu_socket(s)' => '2',
116
+ 'cpu::cpu(s)' => '4',
117
+ 'cpu::core(s)_per_socket' => '1',
118
+ 'memory::memtotal' => '1024',
119
+ 'insights_id' => '00000000-0073-0400-0000-000000000000')
120
+
121
+ batch = Host.where(id: @host.id).in_batches.first
122
+ generator = create_generator(batch)
123
+
124
+ json_str = generator.render
125
+ actual = JSON.parse(json_str.join("\n"))
126
+
127
+ assert_equal '00000000-0000-0000-0000-000000000000', actual['report_slice_id']
128
+ assert_not_nil(actual_host = actual['hosts'].first)
129
+ assert_equal 1, generator.hosts_count
130
+ assert_equal '1234', actual_host['account']
131
+ assert_not_nil(actual_system_profile = actual_host['system_profile'])
132
+ assert_not_nil actual_host['subscription_manager_id']
133
+ assert_equal 'D30B0B42-7824-2635-C62D-491394DE43F7', actual_host['bios_uuid']
134
+ assert_equal '00000000-0073-0400-0000-000000000000', actual_host['insights_id']
135
+ assert_equal 4, actual_system_profile['number_of_cpus']
136
+ assert_equal 2, actual_system_profile['number_of_sockets']
137
+ assert_equal 1_048_576, actual_system_profile['system_memory_bytes']
138
+ assert_equal 1, actual_system_profile['cores_per_socket']
139
+ assert_equal 'SeaBios', actual_host['bios_vendor']
140
+ assert_equal '10', actual_host['bios_version']
141
+ assert_equal '2', actual_host['cpu_socket(s)']
142
+ # Assert exclusion of non-minimal data collection fact
143
+ assert_nil actual_host['ip_addresses']
144
+ assert_nil actual_host['mac_addresses']
145
+ assert_nil actual_host['fqdn']
146
+ end
147
+
148
+ test 'generates a report with minimal data collection for a hypervisor' do
149
+ Setting[:insights_minimal_data_collection] = true
150
+ create_fact_values(@host,
151
+ 'dmi::system::uuid' => 'D30B0B42-7824-2635-C62D-491394DE43F7',
152
+ 'hypervisor::type' => 'VMware',
153
+ 'hypervisor::version' => '6.7',
154
+ 'cpu::cpu_socket(s)' => '2',
155
+ 'cpu::cpu(s)' => '4',
156
+ 'cpu::core(s)_per_socket' => '1',
157
+ 'memory::memtotal' => '1024')
158
+
159
+ @host.subscription_facet.hypervisor = true
160
+ @host.subscription_facet.save!
161
+
162
+ batch = Host.where(id: @host.id).in_batches.first
163
+ generator = create_generator(batch)
164
+
165
+ json_str = generator.render
166
+ actual = JSON.parse(json_str.join("\n"))
167
+
168
+ assert_equal '00000000-0000-0000-0000-000000000000', actual['report_slice_id']
169
+ assert_not_nil(actual_host = actual['hosts'].first)
170
+ assert_not_nil(actual_system_profile = actual_host['system_profile'])
171
+ assert_equal 'D30B0B42-7824-2635-C62D-491394DE43F7', actual_host['bios_uuid']
172
+ assert_equal 'VMware', actual_host['hypervisor_type']
173
+ assert_equal '6.7', actual_host['hypervisor_version']
174
+ assert_equal '2', actual_host['cpu_socket(s)']
175
+ assert_equal 4, actual_system_profile['number_of_cpus']
176
+ assert_equal 2, actual_system_profile['number_of_sockets']
177
+ assert_equal 1_048_576, actual_system_profile['system_memory_bytes']
178
+ assert_equal 1, actual_system_profile['cores_per_socket']
179
+ assert_equal 1, generator.hosts_count
180
+ # Assert exclusion of non-minimal data collection facts
181
+ assert_nil actual_host['ip_addresses']
182
+ assert_nil actual_host['mac_addresses']
183
+ assert_nil actual_host['fqdn']
184
+ assert true, @host.subscription_facet.hypervisor?
185
+ end
186
+
187
+ test 'packages are excluded in the report with minimal data collection' do
188
+ Setting[:exclude_installed_packages] = false
189
+ Setting[:insights_minimal_data_collection] = true
190
+
191
+ installed_package = ::Katello::InstalledPackage.create(name: 'test-package', nvrea: 'test-package-1.0.x86_64', nvra: 'test-package-1.0.x86_64')
192
+ another_host = FactoryBot.create(
193
+ :host,
194
+ :with_subscription,
195
+ :with_content,
196
+ content_view: @host.content_views.first,
197
+ lifecycle_environment: @host.lifecycle_environments.first,
198
+ organization: @host.organization,
199
+ installed_packages: [installed_package]
200
+ )
201
+ create_fact_values(another_host,
202
+ 'dmi::system::uuid' => 'D30B0B42-7824-2635-C62D-491394DE43F7',
203
+ 'dmi::bios::vendor' => 'SeaBios',
204
+ 'dmi::bios::version' => '10',
205
+ 'cpu::cpu_socket(s)' => '2',
206
+ 'cpu::cpu(s)' => '4',
207
+ 'cpu::core(s)_per_socket' => '1',
208
+ 'memory::memtotal' => '1024')
209
+
210
+ batch = Host.where(id: another_host.id).in_batches.first
211
+ generator = create_generator(batch)
212
+
213
+ json_str = generator.render
214
+ actual = JSON.parse(json_str.join("\n"))
215
+
216
+ assert_equal '00000000-0000-0000-0000-000000000000', actual['report_slice_id']
217
+ assert_not_nil(actual_host = actual['hosts'].first)
218
+ assert_not_nil(actual_host['system_profile'])
219
+ assert_not_nil(actual_system_profile = actual_host['system_profile'])
220
+ assert_not_nil actual_host['subscription_manager_id']
221
+ assert_equal 'D30B0B42-7824-2635-C62D-491394DE43F7', actual_host['bios_uuid']
222
+ assert_equal 4, actual_system_profile['number_of_cpus']
223
+ assert_equal 2, actual_system_profile['number_of_sockets']
224
+ assert_equal 1_048_576, actual_system_profile['system_memory_bytes']
225
+ assert_equal 1, actual_system_profile['cores_per_socket']
226
+ assert_equal 'SeaBios', actual_host['bios_vendor']
227
+ assert_equal '10', actual_host['bios_version']
228
+ assert_equal '2', actual_host['cpu_socket(s)']
229
+ assert_nil actual_host['installed_packages']
230
+ end
231
+
232
+ test 'generates a report with minimal data collection with ip setting overridden' do
233
+ Setting[:insights_minimal_data_collection] = true
234
+ Setting[:obfuscate_inventory_ips] = false
235
+ create_fact_values(@host,
236
+ 'dmi::system::uuid' => 'D30B0B42-7824-2635-C62D-491394DE43F7',
237
+ 'dmi::bios::vendor' => 'SeaBios',
238
+ 'dmi::bios::version' => '10',
239
+ 'cpu::cpu_socket(s)' => '2',
240
+ 'cpu::cpu(s)' => '4',
241
+ 'cpu::core(s)_per_socket' => '1',
242
+ 'memory::memtotal' => '1024')
243
+
244
+ batch = Host.where(id: @host.id).in_batches.first
245
+ generator = create_generator(batch)
246
+
247
+ json_str = generator.render
248
+ actual = JSON.parse(json_str.join("\n"))
249
+
250
+ assert_equal '00000000-0000-0000-0000-000000000000', actual['report_slice_id']
251
+ assert_not_nil(actual_host = actual['hosts'].first)
252
+ assert_equal 1, generator.hosts_count
253
+ assert_not_nil(actual_host['system_profile'])
254
+ assert_not_nil(actual_system_profile = actual_host['system_profile'])
255
+ assert_not_nil actual_host['subscription_manager_id']
256
+ assert_equal 'D30B0B42-7824-2635-C62D-491394DE43F7', actual_host['bios_uuid']
257
+ assert_equal 4, actual_system_profile['number_of_cpus']
258
+ assert_equal 2, actual_system_profile['number_of_sockets']
259
+ assert_equal 1_048_576, actual_system_profile['system_memory_bytes']
260
+ assert_equal 1, actual_system_profile['cores_per_socket']
261
+ assert_equal 'SeaBios', actual_host['bios_vendor']
262
+ assert_equal '10', actual_host['bios_version']
263
+ assert_equal '2', actual_host['cpu_socket(s)']
264
+ # Assert exclusion of non-minimal data collection fact
265
+ assert_nil actual_host['ip_addresses']
266
+ assert_nil actual_host['mac_addresses']
267
+ assert_nil actual_host['fqdn']
268
+ end
269
+
270
+ test 'generates a report with minimal data collection with fqdn setting overridden' do
271
+ Setting[:insights_minimal_data_collection] = true
272
+ Setting[:obfuscate_inventory_hostnames] = false
273
+ create_fact_values(@host,
274
+ 'dmi::system::uuid' => 'D30B0B42-7824-2635-C62D-491394DE43F7',
275
+ 'dmi::bios::vendor' => 'SeaBios',
276
+ 'dmi::bios::version' => '10',
277
+ 'cpu::cpu_socket(s)' => '2',
278
+ 'cpu::cpu(s)' => '4',
279
+ 'cpu::core(s)_per_socket' => '1',
280
+ 'memory::memtotal' => '1024')
281
+
282
+ batch = Host.where(id: @host.id).in_batches.first
283
+ generator = create_generator(batch)
284
+
285
+ json_str = generator.render
286
+ actual = JSON.parse(json_str.join("\n"))
287
+
288
+ assert_equal '00000000-0000-0000-0000-000000000000', actual['report_slice_id']
289
+ assert_not_nil(actual_host = actual['hosts'].first)
290
+ assert_not_nil(actual_host['system_profile'])
291
+ assert_not_nil(actual_system_profile = actual_host['system_profile'])
292
+ assert_not_nil actual_host['subscription_manager_id']
293
+ assert_equal 'D30B0B42-7824-2635-C62D-491394DE43F7', actual_host['bios_uuid']
294
+ assert_equal 4, actual_system_profile['number_of_cpus']
295
+ assert_equal 2, actual_system_profile['number_of_sockets']
296
+ assert_equal 1_048_576, actual_system_profile['system_memory_bytes']
297
+ assert_equal 1, actual_system_profile['cores_per_socket']
298
+ assert_equal 'SeaBios', actual_host['bios_vendor']
299
+ assert_equal '10', actual_host['bios_version']
300
+ assert_equal '2', actual_host['cpu_socket(s)']
301
+ # Assert exclusion of non-minimal data collection fact
302
+ assert_nil actual_host['ip_addresses']
303
+ assert_nil actual_host['mac_addresses']
304
+ assert_nil actual_host['fqdn']
305
+ assert_equal 1, generator.hosts_count
306
+ end
307
+
101
308
  test 'hosts report fields should be present if fact exist' do
102
- FactoryBot.create(:fact_value, fact_name: fact_names['cpu::cpu(s)'], value: '4', host: @host)
103
- FactoryBot.create(:fact_value, fact_name: fact_names['cpu::cpu_socket(s)'], value: '2', host: @host)
104
- FactoryBot.create(:fact_value, fact_name: fact_names['cpu::core(s)_per_socket'], value: '1', host: @host)
309
+ create_fact_values(@host,
310
+ 'cpu::cpu(s)' => '4',
311
+ 'cpu::cpu_socket(s)' => '2',
312
+ 'cpu::core(s)_per_socket' => '1')
105
313
 
106
314
  batch = Host.where(id: @host.id).in_batches.first
107
315
  generator = create_generator(batch)
@@ -704,7 +912,7 @@ class SliceGeneratorTest < ActiveSupport::TestCase
704
912
  end
705
913
 
706
914
  test 'passes valid bios_uuid field' do
707
- FactoryBot.create(:fact_value, fact_name: fact_names['dmi::system::uuid'], value: 'D30B0B42-7824-2635-C62D-491394DE43F7', host: @host)
915
+ create_fact_values(@host, 'dmi::system::uuid' => 'D30B0B42-7824-2635-C62D-491394DE43F7')
708
916
 
709
917
  batch = Host.where(id: @host.id).in_batches.first
710
918
  generator = create_generator(batch)
@@ -49,6 +49,7 @@ class Dashboard extends React.Component {
49
49
  showFullScreen,
50
50
  activeTab,
51
51
  } = this.props;
52
+ const downloadButtonDisabled = () => account.report_file_paths.length === 0;
52
53
  return (
53
54
  <NavContainer
54
55
  items={[
@@ -60,6 +61,8 @@ class Dashboard extends React.Component {
60
61
  ...generating,
61
62
  restartProcess: this.handleRestart,
62
63
  exitCode: account.generate_report_status,
64
+ downloadReports: this.handleDownload,
65
+ downloadButtonDisabled,
63
66
  toggleFullScreen: this.handleToggleFullScreen,
64
67
  },
65
68
  onClick: () => this.handleTabChange('generating'),
@@ -70,7 +73,6 @@ class Dashboard extends React.Component {
70
73
  component: ReportUpload,
71
74
  props: {
72
75
  ...uploading,
73
- downloadReports: this.handleDownload,
74
76
  exitCode: account.upload_report_status,
75
77
  toggleFullScreen: this.handleToggleFullScreen,
76
78
  },
@@ -117,6 +119,7 @@ Dashboard.propTypes = {
117
119
  account: PropTypes.shape({
118
120
  generate_report_status: PropTypes.string,
119
121
  upload_report_status: PropTypes.string,
122
+ report_file_paths: PropTypes.arrayOf(PropTypes.string),
120
123
  }),
121
124
  showFullScreen: PropTypes.bool,
122
125
  toggleFullScreen: PropTypes.func,
@@ -136,6 +139,7 @@ Dashboard.defaultProps = {
136
139
  account: {
137
140
  generate_report_status: 'unknown',
138
141
  upload_report_status: 'unknown',
142
+ report_file_paths: [],
139
143
  },
140
144
  showFullScreen: false,
141
145
  toggleFullScreen: noop,
@@ -10,6 +10,8 @@ exports[`Dashboard rendering with props 1`] = `
10
10
  "name": "Generating",
11
11
  "onClick": [Function],
12
12
  "props": Object {
13
+ "downloadButtonDisabled": [Function],
14
+ "downloadReports": [Function],
13
15
  "exitCode": "unknown",
14
16
  "restartProcess": [Function],
15
17
  "toggleFullScreen": [Function],
@@ -21,7 +23,6 @@ exports[`Dashboard rendering with props 1`] = `
21
23
  "name": "Uploading",
22
24
  "onClick": [Function],
23
25
  "props": Object {
24
- "downloadReports": [Function],
25
26
  "exitCode": "unknown",
26
27
  "toggleFullScreen": [Function],
27
28
  },
@@ -8,6 +8,27 @@ export const settingsDict = {
8
8
  'Enable automatic upload of your hosts inventory to the Red Hat cloud'
9
9
  ),
10
10
  },
11
+ allowAutoInsightsMismatchDelete: {
12
+ name: 'allow_auto_insights_mismatch_delete',
13
+ label: __('Automatic mismatch deletion'),
14
+ tooltip: __(
15
+ 'Enable automatic deletion of mismatched host records from the Red Hat cloud'
16
+ ),
17
+ },
18
+ };
19
+
20
+ // we don't use this anywhere, but leaving the comment just so you can, like, see
21
+ // export const parentSettingsDict = {
22
+ // insightsMinimalDataCollection: {
23
+ // name: 'insights_minimal_data_collection',
24
+ // label: __('Minimal data collection'),
25
+ // tooltip: __(
26
+ // 'Only send the minimum required data to Red Hat cloud, and obfuscate wherever possible'
27
+ // ),
28
+ // },
29
+ // };
30
+
31
+ export const childSettingsDict = {
11
32
  hostObfuscationEnabled: {
12
33
  name: 'obfuscate_inventory_hostnames',
13
34
  label: __('Obfuscate host names'),
@@ -20,16 +41,14 @@ export const settingsDict = {
20
41
  },
21
42
  excludePackagesEnabled: {
22
43
  name: 'exclude_installed_packages',
23
- label: __('Exclude installed Packages'),
44
+ label: __('Exclude installed packages'),
24
45
  tooltip: __(
25
46
  'Exclude installed packages from being uploaded to the Red Hat cloud'
26
47
  ),
27
48
  },
28
- allowAutoInsightsMismatchDelete: {
29
- name: 'allow_auto_insights_mismatch_delete',
30
- label: __('Automatic mismatch deletion'),
31
- tooltip: __(
32
- 'Enable automatic deletion of mismatched host records from the Red Hat cloud'
33
- ),
34
- },
49
+ };
50
+
51
+ export const allSettingsDict = {
52
+ ...settingsDict,
53
+ ...childSettingsDict,
35
54
  };
@@ -4,9 +4,8 @@ import { useSelector, useDispatch } from 'react-redux';
4
4
  import { selectSettings } from '../InventorySettingsSelectors';
5
5
  import { handleToggle } from './AdvancedSettingActions';
6
6
  import SwitcherPF4 from '../../../../common/Switcher/SwitcherPF4';
7
- import { settingsDict } from './AdvancedSettingsConstants';
8
7
 
9
- const AdvancedSetting = ({ setting }) => {
8
+ const AdvancedSetting = ({ setting, settingsDict, isLocked, lockedValue }) => {
10
9
  const settingValue = useSelector(store => selectSettings(store)[setting]);
11
10
  const dispatch = useDispatch();
12
11
  const onToggle = () =>
@@ -16,7 +15,8 @@ const AdvancedSetting = ({ setting }) => {
16
15
  id={settingsDict[setting].name}
17
16
  label={settingsDict[setting].label}
18
17
  tooltip={settingsDict[setting].tooltip}
19
- isChecked={settingValue}
18
+ isChecked={isLocked ? lockedValue : settingValue}
19
+ isDisabled={isLocked}
20
20
  onChange={onToggle}
21
21
  />
22
22
  );
@@ -24,6 +24,14 @@ const AdvancedSetting = ({ setting }) => {
24
24
 
25
25
  AdvancedSetting.propTypes = {
26
26
  setting: PropTypes.string.isRequired,
27
+ settingsDict: PropTypes.shape({}).isRequired,
28
+ isLocked: PropTypes.bool,
29
+ lockedValue: PropTypes.bool,
30
+ };
31
+
32
+ AdvancedSetting.defaultProps = {
33
+ isLocked: false,
34
+ lockedValue: true,
27
35
  };
28
36
 
29
37
  export default AdvancedSetting;
@@ -1,18 +1,79 @@
1
- import React from 'react';
1
+ import React, { useState } from 'react';
2
+ import { useSelector } from 'react-redux';
2
3
  import { translate as __ } from 'foremanReact/common/I18n';
4
+ import { Flex, FlexItem } from '@patternfly/react-core';
3
5
 
4
6
  import AdvancedSetting from './AdvancedSetting';
5
- import { settingsDict } from './AdvancedSetting/AdvancedSettingsConstants';
7
+ import {
8
+ childSettingsDict,
9
+ settingsDict,
10
+ } from './AdvancedSetting/AdvancedSettingsConstants';
11
+ import {
12
+ selectSubscriptionConnectionEnabled,
13
+ selectInsightsMinimalDataCollection,
14
+ } from './InventorySettingsSelectors'; // Make sure this path is correct!
15
+ import MinimalInventoryDropdown from './MinimalInventoryDropdown';
6
16
 
7
17
  import './InventorySettings.scss';
8
18
 
9
- const InventorySettings = () => (
10
- <div className="inventory-settings">
11
- <h3>{__('Settings')}</h3>
12
- {Object.keys(settingsDict).map(key => (
13
- <AdvancedSetting setting={key} key={key} />
14
- ))}
15
- </div>
16
- );
19
+ const InventorySettings = () => {
20
+ const subscriptionConnectionEnabled = useSelector(
21
+ state => selectSubscriptionConnectionEnabled(state) // Added (state) =>
22
+ );
23
+ const insightsMinimalDataCollection = useSelector(
24
+ state => selectInsightsMinimalDataCollection(state) // Added (state) =>
25
+ );
26
+ const [chosenValue, setChosenValue] = useState(null);
27
+ const settingKeys = new Set(Object.keys(settingsDict));
28
+
29
+ if (!subscriptionConnectionEnabled) {
30
+ settingKeys.delete('autoUploadEnabled');
31
+ }
32
+
33
+ return (
34
+ <div className="inventory-settings">
35
+ <h3>{__('Settings')}</h3>
36
+ {[...settingKeys].map(key => {
37
+ const isChildSetting = Object.hasOwnProperty.call(
38
+ childSettingsDict,
39
+ key
40
+ );
41
+ return (
42
+ <AdvancedSetting
43
+ key={key}
44
+ setting={key}
45
+ settingsDict={isChildSetting ? childSettingsDict : settingsDict}
46
+ isLocked={
47
+ isChildSetting &&
48
+ (chosenValue === 'minimal' || insightsMinimalDataCollection)
49
+ }
50
+ />
51
+ );
52
+ })}
53
+ <MinimalInventoryDropdown setChosenValue={setChosenValue} />
54
+ {Object.keys(childSettingsDict).length > 0 && ( // Only render this section if there are child settings
55
+ <div style={{ marginTop: '1.5em' }}>
56
+ <Flex>
57
+ <FlexItem>
58
+ <span style={{ width: '6em' }} />
59
+ </FlexItem>
60
+ <FlexItem>
61
+ {Object.keys(childSettingsDict).map(key => (
62
+ <AdvancedSetting
63
+ key={key}
64
+ setting={key}
65
+ settingsDict={childSettingsDict}
66
+ isLocked={
67
+ chosenValue === 'minimal' || insightsMinimalDataCollection
68
+ }
69
+ />
70
+ ))}
71
+ </FlexItem>
72
+ </Flex>
73
+ </div>
74
+ )}
75
+ </div>
76
+ );
77
+ };
17
78
 
18
79
  export default InventorySettings;
@@ -1,12 +1,21 @@
1
- import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';
1
+ import {
2
+ selectAPIResponse,
3
+ selectAPIStatus,
4
+ } from 'foremanReact/redux/API/APISelectors';
2
5
  import { INVENTORY_SETTINGS } from './InventorySettingsConstants';
3
6
 
4
7
  export const selectSettings = state =>
5
8
  selectAPIResponse(state, INVENTORY_SETTINGS);
6
9
 
10
+ export const selectSettingsStatus = state =>
11
+ selectAPIStatus(state, INVENTORY_SETTINGS);
12
+
7
13
  export const selectAutoUploadEnabled = state =>
8
14
  selectSettings(state).autoUploadEnabled;
9
15
 
16
+ export const selectSubscriptionConnectionEnabled = state =>
17
+ selectSettings(state).subscriptionConnectionEnabled;
18
+
10
19
  export const selectHostObfuscationEnabled = state =>
11
20
  selectSettings(state).hostObfuscationEnabled;
12
21
 
@@ -18,3 +27,6 @@ export const selectExcludePackages = state =>
18
27
 
19
28
  export const selectMismatchDelete = state =>
20
29
  selectSettings(state).allowAutoInsightsMismatchDelete;
30
+
31
+ export const selectInsightsMinimalDataCollection = state =>
32
+ selectSettings(state).insightsMinimalDataCollection;
@@ -0,0 +1,107 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useDispatch, useSelector } from 'react-redux';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
+ import {
6
+ Dropdown,
7
+ DropdownItem,
8
+ DropdownList,
9
+ MenuToggle,
10
+ } from '@patternfly/react-core';
11
+
12
+ import { childSettingsDict } from './AdvancedSetting/AdvancedSettingsConstants';
13
+ import { setSetting } from './InventorySettingsActions';
14
+
15
+ import { selectInsightsMinimalDataCollection } from './InventorySettingsSelectors';
16
+
17
+ const MinimalInventoryDropdown = ({ setChosenValue }) => {
18
+ const dispatch = useDispatch();
19
+ const [isOpen, setIsOpen] = useState(false);
20
+ const dropdownValues = {
21
+ minimal: {
22
+ title: __('Minimal data collection'),
23
+ description: __(
24
+ 'Only send the minimum required data to Red Hat cloud, obfuscation settings are disabled'
25
+ ),
26
+ },
27
+ optional: {
28
+ title: __('Optional data collection'),
29
+ description: __(
30
+ 'Send additional data to enhance Insights services, as per the settings'
31
+ ),
32
+ },
33
+ };
34
+ const valueToBoolean = {
35
+ minimal: true,
36
+ optional: false,
37
+ };
38
+ const currentSettingBoolean = useSelector(
39
+ selectInsightsMinimalDataCollection
40
+ );
41
+ const currentDropdownValue = currentSettingBoolean ? 'minimal' : 'optional';
42
+ const onToggleClick = () => {
43
+ setIsOpen(!isOpen);
44
+ };
45
+ const onSelect = (_event, value) => {
46
+ setIsOpen(false);
47
+ setChosenValue(value);
48
+
49
+ dispatch(
50
+ setSetting({
51
+ setting: 'insights_minimal_data_collection',
52
+ value: valueToBoolean[value],
53
+ })
54
+ );
55
+
56
+ if (value === 'minimal') {
57
+ // If user wants to move to minimal data collection, turn on all related settings.
58
+ // These will be overridden by insights_minimal_data_collection anyway, but this takes care of the visuals.
59
+ Object.values(childSettingsDict).forEach(setting => {
60
+ dispatch(
61
+ setSetting({
62
+ setting: setting.name,
63
+ value: true,
64
+ })
65
+ );
66
+ });
67
+ }
68
+ };
69
+ return (
70
+ <Dropdown
71
+ isOpen={isOpen}
72
+ onSelect={onSelect}
73
+ onOpenChange={val => setIsOpen(val)}
74
+ toggle={toggleRef => (
75
+ <MenuToggle
76
+ ref={toggleRef}
77
+ isFullWidth
78
+ onClick={onToggleClick}
79
+ isExpanded={isOpen}
80
+ >
81
+ {dropdownValues[currentDropdownValue].title}
82
+ </MenuToggle>
83
+ )}
84
+ shouldFocusToggleOnSelect
85
+ >
86
+ <div style={{ maxWidth: '28em' }}>
87
+ <DropdownList>
88
+ {Object.entries(dropdownValues).map(([value, item]) => (
89
+ <DropdownItem
90
+ value={value}
91
+ key={value}
92
+ description={item.description}
93
+ >
94
+ {item.title}
95
+ </DropdownItem>
96
+ ))}
97
+ </DropdownList>
98
+ </div>
99
+ </Dropdown>
100
+ );
101
+ };
102
+
103
+ MinimalInventoryDropdown.propTypes = {
104
+ setChosenValue: PropTypes.func.isRequired,
105
+ };
106
+
107
+ export default MinimalInventoryDropdown;
@@ -1,12 +1,19 @@
1
1
  import React from 'react';
2
2
  import { noop } from 'foremanReact/common/helpers';
3
+ import { translate as __ } from 'foremanReact/common/I18n';
3
4
 
4
5
  export const props = {
5
6
  items: [
6
7
  {
7
8
  icon: 'some-icon',
8
- name: 'some-name',
9
- component: () => <p>test</p>,
9
+ name: __('Generating'),
10
+ component: () => <p>test1</p>,
11
+ onClick: noop,
12
+ },
13
+ {
14
+ icon: 'other-icon',
15
+ name: __('Uploading'),
16
+ component: () => <p>test2</p>,
10
17
  onClick: noop,
11
18
  },
12
19
  ],
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { useSelector } from 'react-redux';
2
3
  import PropTypes from 'prop-types';
3
4
  import {
4
5
  TabContainer,
@@ -9,8 +10,10 @@ import {
9
10
  Icon,
10
11
  noop,
11
12
  } from 'patternfly-react';
13
+ import { translate as __ } from 'foremanReact/common/I18n';
12
14
  import './navContainer.scss';
13
15
  import FullScreenModal from '../FullScreenModal';
16
+ import { selectSubscriptionConnectionEnabled } from '../InventorySettings/InventorySettingsSelectors';
14
17
 
15
18
  const NavContainer = ({
16
19
  items,
@@ -18,8 +21,13 @@ const NavContainer = ({
18
21
  toggleFullScreen,
19
22
  terminalProps,
20
23
  }) => {
24
+ const subscriptionConnectionEnabled = useSelector(
25
+ selectSubscriptionConnectionEnabled
26
+ );
21
27
  const navItems = items.map((item, index) => {
22
28
  const { name, icon, onClick } = item;
29
+ if (name === __('Uploading') && !subscriptionConnectionEnabled) return null;
30
+
23
31
  return (
24
32
  <NavItem
25
33
  key={index}