foreman_rh_cloud 12.2.0 → 12.2.2

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/rh_cloud/advisor_engine_config_controller.rb +1 -1
  3. data/app/controllers/api/v2/rh_cloud/cloud_request_controller.rb +3 -0
  4. data/app/controllers/api/v2/rh_cloud/inventory_controller.rb +3 -0
  5. data/app/controllers/concerns/foreman_rh_cloud/iop_smart_proxy_access.rb +28 -0
  6. data/app/controllers/concerns/insights_cloud/package_profile_upload_extensions.rb +1 -1
  7. data/app/controllers/foreman_inventory_upload/uploads_controller.rb +3 -0
  8. data/app/controllers/foreman_rh_cloud/foreman_rh_cloud_controller.rb +22 -0
  9. data/app/controllers/insights_cloud/api/machine_telemetries_controller.rb +19 -5
  10. data/app/controllers/insights_cloud/ui_requests_controller.rb +11 -0
  11. data/app/services/foreman_rh_cloud/cert_auth.rb +1 -1
  12. data/app/services/foreman_rh_cloud/cloud_request.rb +1 -1
  13. data/app/services/foreman_rh_cloud/cloud_request_forwarder.rb +1 -1
  14. data/app/services/foreman_rh_cloud/hit_remediations_retriever.rb +27 -10
  15. data/app/services/foreman_rh_cloud/url_remediations_retriever.rb +1 -1
  16. data/app/views/api/v2/hosts/insights/base.rabl +2 -2
  17. data/app/views/api/v2/hosts/insights/single.rabl +1 -1
  18. data/config/routes.rb +8 -14
  19. data/lib/foreman_inventory_upload/async/generate_all_reports_job.rb +2 -2
  20. data/lib/foreman_inventory_upload/async/upload_report_job.rb +1 -1
  21. data/lib/foreman_inventory_upload/generators/slice.rb +24 -0
  22. data/lib/foreman_rh_cloud/engine.rb +8 -131
  23. data/lib/foreman_rh_cloud/plugin.rb +150 -0
  24. data/lib/foreman_rh_cloud/version.rb +1 -1
  25. data/lib/foreman_rh_cloud.rb +5 -6
  26. data/lib/insights_cloud/async/insights_scheduled_sync.rb +2 -2
  27. data/lib/inventory_sync/async/inventory_hosts_sync.rb +1 -1
  28. data/lib/inventory_sync/async/inventory_scheduled_sync.rb +2 -2
  29. data/lib/tasks/insights.rake +1 -1
  30. data/lib/tasks/rh_cloud_inventory.rake +20 -2
  31. data/package.json +1 -1
  32. data/test/controllers/insights_cloud/api/machine_telemetries_controller_test.rb +20 -3
  33. data/test/controllers/insights_cloud/ui_requests_controller_test.rb +23 -0
  34. data/test/controllers/insights_sync/settings_controller_test.rb +1 -1
  35. data/test/factories/inventory_upload_factories.rb +4 -112
  36. data/test/jobs/inventory_scheduled_sync_test.rb +3 -3
  37. data/test/test_plugin_helper.rb +8 -2
  38. data/test/unit/rh_cloud_http_proxy_test.rb +3 -3
  39. data/test/unit/services/foreman_rh_cloud/cloud_request_forwarder_test.rb +4 -1
  40. data/test/unit/slice_generator_test.rb +33 -0
  41. data/test/unit/tags_generator_test.rb +4 -1
  42. data/webpack/CVEsHostDetailsTab/CVEsHostDetailsTab.js +2 -1
  43. data/webpack/CVEsHostDetailsTab/CVEsHostDetailsTab.scss +3 -0
  44. data/webpack/CveDetailsPage/CveDetailsPage.js +1 -1
  45. data/webpack/CveDetailsPage/CveDetailsPage.test.js +1 -3
  46. data/webpack/ForemanColumnExtensions/index.js +20 -15
  47. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/PageDescription/PageDescription.js +23 -0
  48. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/PageDescription/__tests__/PageDescription.test.js +2 -0
  49. data/webpack/ForemanRhCloudPages.js +1 -0
  50. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationHelpers.js +26 -4
  51. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +85 -11
  52. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModalFooter.js +39 -5
  53. data/webpack/InsightsCloudSync/Components/RemediationModal/Resolutions.js +13 -0
  54. data/webpack/InsightsCloudSync/InsightsCloudSync.js +9 -7
  55. data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +16 -15
  56. data/webpack/InsightsVulnerability/InsightsVulnerabilityListPage.js +1 -1
  57. data/webpack/IopRecommendationDetails/IopRecommendationDetails.js +1 -11
  58. data/webpack/common/Hooks/ConfigHooks.js +1 -2
  59. data/webpack/common/styles.scss +7 -0
  60. metadata +6 -1
@@ -0,0 +1,3 @@
1
+ div.rh-cloud-insights-vulnerability-host-details-component {
2
+ padding: 24px;
3
+ }
@@ -10,7 +10,7 @@ const CveDetailsPage = () => {
10
10
 
11
11
  return (
12
12
  <ScalprumProvider {...providerOptions}>
13
- <div className="rh-cloud-cve-details-page">
13
+ <div className="rh-cloud-cve-details-page vulnerability">
14
14
  <ScalprumComponent scope={scope} module={module} cveId={cveId} />
15
15
  </div>
16
16
  </ScalprumProvider>
@@ -18,9 +18,7 @@ jest.mock('@scalprum/react-core', () => ({
18
18
  describe('CveDetailsPage component', () => {
19
19
  it('renders the container with correct class', () => {
20
20
  const { container } = render(<CveDetailsPage />);
21
- expect(
22
- container.querySelector('.rh-cloud-cve-details-page')
23
- ).toBeTruthy();
21
+ expect(container.querySelector('.rh-cloud-cve-details-page')).toBeTruthy();
24
22
  });
25
23
 
26
24
  it('passes cveId from URL params to ScalprumComponent', () => {
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
- import { ScalprumComponent, ScalprumProvider } from '@scalprum/react-core';
2
+ import PropTypes from 'prop-types';
3
+ import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
4
+ import { Link } from 'react-router-dom';
3
5
  import { translate as __ } from 'foremanReact/common/I18n';
4
6
  import { propsToCamelCase } from 'foremanReact/common/helpers';
5
7
  import { CVECountCell } from '../InsightsVulnerabilityHostIndexExtensions/CVECountCell';
6
- import { providerOptions } from '../common/ScalprumModule/ScalprumContext';
7
8
 
8
9
  const HostedRecommendationsCell = hostDetails => {
9
10
  const insightsAttributes = propsToCamelCase(
@@ -18,22 +19,26 @@ const HostedRecommendationsCell = hostDetails => {
18
19
  return <a href={hitsUrl}>{hitsCount}</a>;
19
20
  };
20
21
 
21
- const IopRecommendationsCell = hostDetails => {
22
- const scope = 'advisor';
23
- const module = './RecommendationsCellWrapped';
22
+ const IopRecommendationsCell = ({ hostDetails }) => {
23
+ // eslint-disable-next-line camelcase
24
+ const uuid = hostDetails?.insights_attributes?.uuid;
25
+ const { response } = useAPI(
26
+ uuid ? 'get' : null,
27
+ `/insights_cloud/api/insights/v1/system/${uuid}`,
28
+ { key: `HOST_RECS_COUNT_${uuid}` }
29
+ );
24
30
 
25
- return (
26
- <span className="rh-cloud-insights-recommendations-cell">
27
- <ScalprumComponent scope={scope} module={module} />
28
- </span>
31
+ const hits = response?.hits;
32
+ return hits === undefined ? (
33
+ '—'
34
+ ) : (
35
+ <Link to={`hosts/${hostDetails.name}#/Insights`}>{hits}</Link>
29
36
  );
30
37
  };
31
38
 
32
- const IopRecommendationsCellWrapped = hostDetails => (
33
- <ScalprumProvider {...providerOptions}>
34
- <IopRecommendationsCell hostDetails={hostDetails} />
35
- </ScalprumProvider>
36
- );
39
+ IopRecommendationsCell.propTypes = {
40
+ hostDetails: PropTypes.object.isRequired,
41
+ };
37
42
 
38
43
  const RecommendationsCell = hostDetails => {
39
44
  const insightsAttributes = propsToCamelCase(
@@ -42,7 +47,7 @@ const RecommendationsCell = hostDetails => {
42
47
  );
43
48
 
44
49
  return insightsAttributes.useLocalAdvisorEngine ? (
45
- <IopRecommendationsCellWrapped hostDetails={hostDetails} />
50
+ <IopRecommendationsCell hostDetails={hostDetails} />
46
51
  ) : (
47
52
  <HostedRecommendationsCell hostDetails={hostDetails} />
48
53
  );
@@ -3,6 +3,7 @@ import { Text } from '@patternfly/react-core';
3
3
  import { useSelector } from 'react-redux';
4
4
 
5
5
  import { translate as __ } from 'foremanReact/common/I18n';
6
+ import { getDocsURL } from 'foremanReact/common/helpers';
6
7
  import { FormattedMessage } from 'react-intl';
7
8
  import { selectSubscriptionConnectionEnabled } from '../../../InventorySettings/InventorySettingsSelectors';
8
9
 
@@ -53,6 +54,28 @@ export const PageDescription = () => {
53
54
  />
54
55
  </Text>
55
56
  )}
57
+ <Text ouiaId="text-minimal-data-collection">
58
+ <FormattedMessage
59
+ id="minimal-data-collection-message"
60
+ defaultMessage={__(
61
+ 'Learn more about {minimalDataCollectionSetting}.'
62
+ )}
63
+ values={{
64
+ minimalDataCollectionSetting: (
65
+ <a
66
+ href={getDocsURL(
67
+ 'Managing_Hosts',
68
+ 'setting-minimal-data-collection'
69
+ )}
70
+ target="_blank"
71
+ rel="noopener noreferrer"
72
+ >
73
+ {__('setting minimal data collection')}
74
+ </a>
75
+ ),
76
+ }}
77
+ />
78
+ </Text>
56
79
  <Text ouiaId="text-more-info-subscription">
57
80
  {__('For more information about the Subscriptions service, see:')}
58
81
  &nbsp;
@@ -15,6 +15,8 @@ jest.mock('react-intl', () => {
15
15
  }),
16
16
  };
17
17
  });
18
+ jest.mock('foremanReact/common/helpers', () => ({ getDocsURL: () => {} }));
19
+
18
20
  const middlewares = [thunk];
19
21
  const mockStore = configureMockStore(middlewares);
20
22
 
@@ -8,6 +8,7 @@ import InsightsCloudSync from './InsightsCloudSync';
8
8
  import IopRecommendationDetails from './IopRecommendationDetails/IopRecommendationDetails';
9
9
  import InsightsHostDetailsTab from './InsightsHostDetailsTab';
10
10
  import CveDetailsPage from './CveDetailsPage';
11
+ import './common/styles.scss';
11
12
 
12
13
  const pages = [
13
14
  { name: 'ForemanInventoryUpload', type: ForemanInventoryUpload },
@@ -3,7 +3,15 @@ import React from 'react';
3
3
  import { orderBy } from 'lodash';
4
4
  import Resolutions from './Resolutions';
5
5
 
6
- export const modifyRows = (remediations, setResolutions, setHostsIds) => {
6
+ export const getResolutionId = (selectedResolution, id) =>
7
+ `${id}_${selectedResolution}`;
8
+
9
+ export const modifyRows = (
10
+ remediations,
11
+ setResolutions,
12
+ setHostsIds,
13
+ isIop
14
+ ) => {
7
15
  if (remediations.length === 0) return [];
8
16
 
9
17
  const resolutionToSubmit = [];
@@ -15,9 +23,22 @@ export const modifyRows = (remediations, setResolutions, setHostsIds) => {
15
23
  ).map(({ id, host_id, hostname, title, resolutions, reboot }) => {
16
24
  hostsIdsToSubmit.add(host_id);
17
25
  const selectedResolution = resolutions[0]?.id;
26
+ /* eslint-disable spellcheck/spell-checker */
27
+
28
+ // For IoP: {
29
+ // hit_id: "c7c6727e-2966-4f7c-87f1-20ef14db7a2d",
30
+ // rule_id: "hardening_ssh_client_alive|OPENSSH_HARDENING_CLIENT_ALIVE",
31
+ // resolution_type: "less_secure",
32
+ // resolution_id:"hardening_ssh_client_alive|OPENSSH_HARDENING_CLIENT_ALIVE_less_secure",
33
+ // }
34
+ // for Hosted, hit_id and rule_id will be Foreman database IDs
35
+
36
+ /* eslint-enable spellcheck/spell-checker */
18
37
  resolutionToSubmit.push({
19
- hit_id: id,
20
- resolution_id: selectedResolution /** defaults to the first resolution if many */,
38
+ hit_id: isIop ? host_id : id,
39
+ rule_id: id,
40
+ resolution_type: selectedResolution /** defaults to the first resolution if many */,
41
+ resolution_id: getResolutionId(selectedResolution, id),
21
42
  });
22
43
  return {
23
44
  cells: [
@@ -25,10 +46,11 @@ export const modifyRows = (remediations, setResolutions, setHostsIds) => {
25
46
  title,
26
47
  <div>
27
48
  <Resolutions
28
- hit_id={id}
49
+ hit_id={isIop ? host_id : id}
29
50
  resolutions={resolutions}
30
51
  setResolutions={setResolutions}
31
52
  selectedResolution={selectedResolution}
53
+ isIop={isIop}
32
54
  />
33
55
  </div>,
34
56
  reboot,
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable react-hooks/exhaustive-deps */
2
2
  import React, { useEffect } from 'react';
3
+ import Immutable from 'seamless-immutable';
3
4
  import PropTypes from 'prop-types';
4
5
  import {
5
6
  Table,
@@ -7,7 +8,7 @@ import {
7
8
  TableBody,
8
9
  } from '@patternfly/react-table/deprecated';
9
10
  import { Modal, ModalVariant, Button } from '@patternfly/react-core';
10
- import { isEmpty } from 'lodash';
11
+ import { isEmpty, noop } from 'lodash';
11
12
  import { STATUS } from 'foremanReact/constants';
12
13
  import { translate as __ } from 'foremanReact/common/I18n';
13
14
  import { columns } from './RemediationTableConstants';
@@ -15,8 +16,48 @@ import { modifyRows } from './RemediationHelpers';
15
16
  import ModalFooter from './RemediationModalFooter';
16
17
  import TableEmptyState from '../../../common/table/EmptyState';
17
18
  import './RemediationModal.scss';
19
+ import { useAdvisorEngineConfig } from '../../../common/Hooks/ConfigHooks';
20
+
21
+ /* eslint-disable spellcheck/spell-checker */
22
+
23
+ // Sample iopData:
24
+ // const iopTestData = Immutable([
25
+ // {
26
+ // hostid: 'c7c6727e-2966-4f7c-87f1-20ef14db7a2d',
27
+ // host_name: 'advisor-test.local',
28
+ // rulename: 'hardening_cryptopol_krb5|NO_CPOL_KRB5',
29
+ // resolutions: [
30
+ // {
31
+ // description: 'Remove manual crypto-policies',
32
+ // id: 'fix',
33
+ // needs_reboot: true,
34
+ // resolution_risk: 1,
35
+ // },
36
+ // ],
37
+ // rebootable: true,
38
+ // description: 'Decreased security: krb5 crypto-policies overridden',
39
+ // },
40
+ // {
41
+ // hostid: 'c7c6727e-2966-4f7c-87f1-20ef14db7a2d',
42
+ // host_name: 'advisor-test.local',
43
+ // rulename: 'hardening_logging_auditd|HARDENING_LOGGING_5_AUDITD',
44
+ // resolutions: [
45
+ // {
46
+ // description: 'Install and enable auditd',
47
+ // id: 'fix',
48
+ // needs_reboot: false,
49
+ // resolution_risk: 1,
50
+ // },
51
+ // ],
52
+ // rebootable: false,
53
+ // description: 'Decreased security: auditd not running',
54
+ // },
55
+ // ]);
56
+
57
+ /* eslint-enable spellcheck/spell-checker */
18
58
 
19
59
  const RemediationModal = ({
60
+ iopData,
20
61
  selectedIds,
21
62
  fetchRemediations,
22
63
  remediations,
@@ -24,31 +65,49 @@ const RemediationModal = ({
24
65
  error,
25
66
  isAllSelected,
26
67
  query,
68
+ isDisabled,
27
69
  }) => {
28
- const [rows, setRows] = React.useState([]);
70
+ const iopRows = Immutable(iopData ?? []).map(recommendation => ({
71
+ id: recommendation.rulename,
72
+ host_id: recommendation.hostid,
73
+ hostname: recommendation.host_name,
74
+ title: recommendation.description,
75
+ resolutions: recommendation.resolutions ?? [],
76
+ reboot: recommendation.rebootable,
77
+ }));
78
+
29
79
  const [open, setOpen] = React.useState(false);
30
80
  const [resolutions, setResolutions] = React.useState([]);
31
81
  const [hostsIds, setHostsIds] = React.useState([]);
82
+ const [rows, setRows] = React.useState([]);
32
83
  const toggleModal = () => setOpen(prevValue => !prevValue);
33
84
 
85
+ const isIop = useAdvisorEngineConfig();
34
86
  useEffect(() => {
35
- if (open) fetchRemediations({ selectedIds, isAllSelected, query });
87
+ // only fetch for Hosted. IoP provides via props.
88
+ if (!isIop && open)
89
+ fetchRemediations({ selectedIds, isAllSelected, query });
36
90
  }, [open]);
37
91
 
38
92
  useEffect(() => {
39
- const modifiedRows =
40
- status === STATUS.PENDING
41
- ? []
42
- : modifyRows(remediations, setResolutions, setHostsIds);
93
+ let modifiedRows;
94
+ if (isIop) {
95
+ modifiedRows = modifyRows(iopRows, setResolutions, setHostsIds, true);
96
+ } else {
97
+ modifiedRows =
98
+ status === STATUS.PENDING
99
+ ? []
100
+ : modifyRows(remediations, setResolutions, setHostsIds, false);
101
+ }
43
102
  setRows(modifiedRows);
44
- }, [remediations, status]);
103
+ }, [remediations, status, iopData, isIop]);
45
104
 
46
105
  return (
47
106
  <React.Fragment>
48
107
  <Button
49
108
  ouiaId="button-remediate"
50
109
  variant="primary"
51
- isDisabled={isEmpty(selectedIds)}
110
+ isDisabled={isDisabled || isEmpty(selectedIds)}
52
111
  onClick={() => {
53
112
  toggleModal();
54
113
  }}
@@ -68,6 +127,7 @@ const RemediationModal = ({
68
127
  toggleModal={toggleModal}
69
128
  resolutions={resolutions}
70
129
  hostsIds={hostsIds}
130
+ isIop={isIop}
71
131
  />
72
132
  }
73
133
  >
@@ -92,22 +152,36 @@ const RemediationModal = ({
92
152
  };
93
153
 
94
154
  RemediationModal.propTypes = {
95
- selectedIds: PropTypes.object,
96
- fetchRemediations: PropTypes.func.isRequired,
155
+ iopData: PropTypes.arrayOf(
156
+ PropTypes.shape({
157
+ hostid: PropTypes.string,
158
+ host_name: PropTypes.string,
159
+ rulename: PropTypes.string,
160
+ resolutions: PropTypes.array,
161
+ rebootable: PropTypes.bool,
162
+ description: PropTypes.string,
163
+ })
164
+ ),
165
+ selectedIds: PropTypes.shape({}),
166
+ fetchRemediations: PropTypes.func,
97
167
  remediations: PropTypes.array,
98
168
  status: PropTypes.string,
99
169
  error: PropTypes.string,
100
170
  isAllSelected: PropTypes.bool,
101
171
  query: PropTypes.string,
172
+ isDisabled: PropTypes.bool,
102
173
  };
103
174
 
104
175
  RemediationModal.defaultProps = {
176
+ iopData: null,
105
177
  selectedIds: {},
178
+ fetchRemediations: noop,
106
179
  remediations: [],
107
180
  status: null,
108
181
  error: null,
109
182
  isAllSelected: false,
110
183
  query: null,
184
+ isDisabled: false,
111
185
  };
112
186
 
113
187
  export default RemediationModal;
@@ -2,18 +2,44 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { Button } from '@patternfly/react-core';
4
4
  import { translate as __ } from 'foremanReact/common/I18n';
5
+ import { useBulkSelect } from 'foremanReact/components/PF4/TableIndexPage/Table/TableHooks';
5
6
  import { JOB_INVOCATION_PATH } from './RemediationTableConstants';
6
7
 
7
- const ModalFooter = ({ toggleModal, resolutions, hostsIds }) => {
8
+ const ModalFooter = ({ toggleModal, resolutions, hostsIds, isIop }) => {
8
9
  let token = document.querySelector('meta[name="csrf-token"]');
9
10
  token = token?.content || '';
11
+
12
+ const [jobInProgress, setJobInProgress] = React.useState(false);
13
+ const formRef = React.useRef(null);
14
+
15
+ const { fetchBulkParams } = useBulkSelect({
16
+ initialArry: hostsIds,
17
+ idColumn: 'insights_uuid',
18
+ });
19
+
20
+ const handleSubmit = e => {
21
+ e.preventDefault();
22
+ setJobInProgress(true);
23
+
24
+ setTimeout(() => {
25
+ // eslint-disable-next-line no-unused-expressions
26
+ formRef.current?.submit?.();
27
+ }, 100);
28
+ };
10
29
  return (
11
- <form action={JOB_INVOCATION_PATH} method="post">
30
+ <form
31
+ action={JOB_INVOCATION_PATH}
32
+ method="post"
33
+ ref={formRef}
34
+ onSubmit={handleSubmit}
35
+ >
12
36
  <Button
13
37
  type="submit"
14
38
  ouiaId="button-confirm"
15
39
  key="confirm"
16
40
  variant="primary"
41
+ isDisabled={jobInProgress}
42
+ isLoading={jobInProgress}
17
43
  >
18
44
  {__('Remediate')}
19
45
  </Button>
@@ -32,9 +58,15 @@ const ModalFooter = ({ toggleModal, resolutions, hostsIds }) => {
32
58
  name="inputs[hit_remediation_pairs]"
33
59
  value={JSON.stringify(resolutions)}
34
60
  />
35
- {hostsIds.map(id => (
36
- <input type="hidden" name="host_ids[]" key={id} value={id} />
37
- ))}
61
+ {!isIop &&
62
+ hostsIds.map(id => (
63
+ <input type="hidden" name="host_ids[]" key={id} value={id} />
64
+ ))}
65
+ {isIop && (
66
+ <>
67
+ <input type="hidden" name="search" value={fetchBulkParams()} />
68
+ </>
69
+ )}
38
70
  </form>
39
71
  );
40
72
  };
@@ -43,11 +75,13 @@ ModalFooter.propTypes = {
43
75
  toggleModal: PropTypes.func.isRequired,
44
76
  resolutions: PropTypes.array,
45
77
  hostsIds: PropTypes.array,
78
+ isIop: PropTypes.bool,
46
79
  };
47
80
 
48
81
  ModalFooter.defaultProps = {
49
82
  resolutions: [],
50
83
  hostsIds: [],
84
+ isIop: false,
51
85
  };
52
86
 
53
87
  export default ModalFooter;
@@ -2,12 +2,14 @@
2
2
  import React from 'react';
3
3
  import PropTypes from 'prop-types';
4
4
  import { Radio } from '@patternfly/react-core';
5
+ import { getResolutionId } from './RemediationHelpers';
5
6
 
6
7
  const Resolutions = ({
7
8
  resolutions,
8
9
  setResolutions,
9
10
  selectedResolution,
10
11
  hit_id,
12
+ isIop,
11
13
  }) => {
12
14
  const [checkedID, setCheckedID] = React.useState(selectedResolution);
13
15
 
@@ -27,6 +29,15 @@ const Resolutions = ({
27
29
  stateRes.map(res => {
28
30
  if (hit_id === res.hit_id) {
29
31
  setCheckedID(resolution_id);
32
+ if (isIop)
33
+ return {
34
+ ...res,
35
+ resolution_id: getResolutionId(
36
+ resolution_id,
37
+ res.rule_id
38
+ ),
39
+ resolution_type: resolution_id,
40
+ };
30
41
  return { ...res, resolution_id };
31
42
  }
32
43
  return res;
@@ -45,12 +56,14 @@ Resolutions.propTypes = {
45
56
  resolutions: PropTypes.array,
46
57
  hit_id: PropTypes.number,
47
58
  selectedResolution: PropTypes.number,
59
+ isIop: PropTypes.bool,
48
60
  };
49
61
 
50
62
  Resolutions.defaultProps = {
51
63
  resolutions: [],
52
64
  hit_id: null,
53
65
  selectedResolution: null,
66
+ isIop: false,
54
67
  };
55
68
 
56
69
  export default Resolutions;
@@ -67,13 +67,15 @@ export const generateRuleUrl = ruleId =>
67
67
  foremanUrl(`/foreman_rh_cloud/recommendations/${ruleId}`);
68
68
 
69
69
  const IopRecommendationsPage = props => (
70
- <ScalprumComponent
71
- scope={scope}
72
- module={module}
73
- IopRemediationModal={RemediationModal}
74
- generateRuleUrl={generateRuleUrl}
75
- {...props}
76
- />
70
+ <div className="advisor">
71
+ <ScalprumComponent
72
+ scope={scope}
73
+ module={module}
74
+ IopRemediationModal={RemediationModal}
75
+ generateRuleUrl={generateRuleUrl}
76
+ {...props}
77
+ />
78
+ </div>
77
79
  );
78
80
 
79
81
  const IopRecommendationsPageWrapped = props => (
@@ -110,17 +110,18 @@ NewHostDetailsTab.defaultProps = {
110
110
 
111
111
  // Local Insights advisor
112
112
  const scope = 'advisor';
113
- // eslint-disable-next-line spellcheck/spell-checker
114
- const module = './HostDetailsLightspeedTabWrapped';
113
+ const module = './SystemDetailWrapped';
115
114
 
116
115
  const IopInsightsTab = props => (
117
- <ScalprumComponent
118
- scope={scope}
119
- module={module}
120
- IopRemediationModal={RemediationModal}
121
- generateRuleUrl={generateRuleUrl}
122
- {...props}
123
- />
116
+ <div className="advisor">
117
+ <ScalprumComponent
118
+ scope={scope}
119
+ module={module}
120
+ IopRemediationModal={RemediationModal}
121
+ generateRuleUrl={generateRuleUrl}
122
+ {...props}
123
+ />
124
+ </div>
124
125
  );
125
126
 
126
127
  const IopInsightsTabWrapped = props => (
@@ -129,11 +130,11 @@ const IopInsightsTabWrapped = props => (
129
130
  </ScalprumProvider>
130
131
  );
131
132
 
132
- const LightspeedTab = props => {
133
+ const InsightsTab = props => {
133
134
  const { response } = props;
134
135
  const isLocalAdvisorEngine =
135
136
  // eslint-disable-next-line camelcase
136
- response?.insights_attributes?.use_local_advisor_engine;
137
+ response?.insights_attributes?.use_iop_mode;
137
138
 
138
139
  return isLocalAdvisorEngine ? (
139
140
  <IopInsightsTabWrapped {...props} />
@@ -142,16 +143,16 @@ const LightspeedTab = props => {
142
143
  );
143
144
  };
144
145
 
145
- LightspeedTab.propTypes = {
146
+ InsightsTab.propTypes = {
146
147
  response: PropTypes.shape({
147
148
  insights_attributes: {
148
- use_local_advisor_engine: PropTypes.bool,
149
+ use_iop_mode: PropTypes.bool,
149
150
  },
150
151
  }),
151
152
  };
152
153
 
153
- LightspeedTab.defaultProps = {
154
+ InsightsTab.defaultProps = {
154
155
  response: {},
155
156
  };
156
157
 
157
- export default LightspeedTab;
158
+ export default InsightsTab;
@@ -6,7 +6,7 @@ const InsightsVulnerabilityListPage = () => {
6
6
  const scope = 'vulnerability';
7
7
  const module = './CveListPage';
8
8
  return (
9
- <div className="rh-cloud-insights-vulnerability-page">
9
+ <div className="rh-cloud-insights-vulnerability-page vulnerability">
10
10
  <ScalprumComponent scope={scope} module={module} />
11
11
  </div>
12
12
  );
@@ -8,15 +8,12 @@ import { providerOptions } from '../common/ScalprumModule/ScalprumContext';
8
8
  const scope = 'advisor';
9
9
  const module = './RecommendationDetailsWrapped';
10
10
 
11
- const invScope = 'inventory';
12
- const invModule = './HybridInventoryTabs';
13
-
14
11
  const IopRecommendationDetails = props => {
15
12
  const urlParams = useRouteMatch('/foreman_rh_cloud/recommendations/:rule_id');
16
13
  // eslint-disable-next-line camelcase
17
14
  const ruleId = urlParams?.params?.rule_id;
18
15
  return (
19
- <div className="rh-cloud-recommendation-details-cell">
16
+ <div className="iop-recommendation-details-scalprum advisor">
20
17
  <ScalprumComponent
21
18
  scope={scope}
22
19
  module={module}
@@ -24,13 +21,6 @@ const IopRecommendationDetails = props => {
24
21
  ruleId={ruleId}
25
22
  {...props}
26
23
  />
27
- <ScalprumComponent
28
- scope={invScope}
29
- module={invModule}
30
- IopRemediationModal={RemediationModal}
31
- ruleId={ruleId}
32
- {...props}
33
- />
34
24
  </div>
35
25
  );
36
26
  };
@@ -14,6 +14,5 @@ export const useAdvisorEngineConfig = () => {
14
14
  );
15
15
 
16
16
  // eslint-disable-next-line camelcase
17
- const isLocalAdvisorEngine = advisorEngineConfig?.use_local_advisor_engine;
18
- return isLocalAdvisorEngine;
17
+ return advisorEngineConfig?.use_iop_mode;
19
18
  };
@@ -0,0 +1,7 @@
1
+ // overwrite bootstrap style which added unintended bottom margins to chips
2
+ // and dropdowns in Advisor and Vulnerability
3
+ .vulnerability, .advisor {
4
+ ul, ol {
5
+ margin-bottom: 0px;
6
+ }
7
+ }