foreman_rh_cloud 14.7.0 → 15.0.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/assets/javascripts/foreman_rh_cloud/locale/fr/foreman_rh_cloud.js +32 -32
- data/app/assets/javascripts/foreman_rh_cloud/locale/ja/foreman_rh_cloud.js +43 -43
- data/app/assets/javascripts/foreman_rh_cloud/locale/ka/foreman_rh_cloud.js +3 -3
- data/app/assets/javascripts/foreman_rh_cloud/locale/ko/foreman_rh_cloud.js +27 -27
- data/app/assets/javascripts/foreman_rh_cloud/locale/zh_CN/foreman_rh_cloud.js +26 -26
- data/app/services/foreman_rh_cloud/tags_auth.rb +28 -13
- data/config/routes.rb +10 -0
- data/lib/foreman_rh_cloud/plugin.rb +14 -0
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/locale/foreman_rh_cloud.pot +2 -2
- data/locale/fr/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
- data/locale/fr/foreman_rh_cloud.po +34 -34
- data/locale/ja/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
- data/locale/ja/foreman_rh_cloud.po +43 -43
- data/locale/ka/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
- data/locale/ka/foreman_rh_cloud.po +3 -3
- data/locale/ko/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
- data/locale/ko/foreman_rh_cloud.po +27 -27
- data/locale/zh_CN/LC_MESSAGES/foreman_rh_cloud.mo +0 -0
- data/locale/zh_CN/foreman_rh_cloud.po +27 -27
- data/package.json +1 -1
- data/test/controllers/insights_cloud/api/machine_telemetries_controller_test.rb +0 -1
- data/test/test_plugin_helper.rb +0 -8
- data/test/unit/services/foreman_rh_cloud/cloud_request_forwarder_test.rb +0 -1
- data/test/unit/services/foreman_rh_cloud/tags_auth_test.rb +26 -0
- data/test/unit/tags_generator_test.rb +0 -1
- data/webpack/ComplianceHostDetailsTab/ComplianceHostDetailsTab.js +36 -0
- data/webpack/ComplianceHostDetailsTab/ComplianceHostDetailsTab.scss +3 -0
- data/webpack/ComplianceHostDetailsTab/__tests__/ComplianceHostDetailsTab.test.js +159 -0
- data/webpack/ComplianceHostDetailsTab/index.js +3 -0
- data/webpack/ForemanRhCloudFills.js +11 -0
- data/webpack/ForemanRhCloudPages.js +7 -0
- data/webpack/InsightsCompliance/ComplianceIframe.js +34 -0
- data/webpack/InsightsCompliance/ComplianceIframe.scss +12 -0
- data/webpack/InsightsCompliance/InsightsCompliancePage.js +11 -0
- data/webpack/InsightsCompliance/InsightsCompliancePage.test.js +35 -0
- data/webpack/InsightsCompliance/complianceIframeConstants.js +7 -0
- data/webpack/InsightsCompliance/complianceIframeHelpers.js +23 -0
- data/webpack/InsightsCompliance/complianceIframeHelpers.test.js +50 -0
- data/webpack/common/Hooks/PermissionsHooks.js +10 -0
- data/webpack/common/IopIframe/IopAppIframe.js +74 -0
- data/webpack/common/IopIframe/IopHostDetailsIframe.js +66 -0
- data/webpack/common/IopIframe/IopHostDetailsIframe.scss +6 -0
- data/webpack/common/IopIframe/IopHostDetailsIframeTab.js +81 -0
- data/webpack/common/IopIframe/__tests__/IopAppIframe.test.js +92 -0
- data/webpack/common/IopIframe/__tests__/IopHostDetailsIframeTab.test.js +93 -0
- data/webpack/common/IopIframe/__tests__/iopIframeHelpers.test.js +63 -0
- data/webpack/common/IopIframe/__tests__/postIopChromeToIframe.test.js +60 -0
- data/webpack/common/IopIframe/__tests__/useIopIframeHeight.test.js +37 -0
- data/webpack/common/IopIframe/iopIframeConstants.js +5 -0
- data/webpack/common/IopIframe/iopIframeHelpers.js +49 -0
- data/webpack/common/IopIframe/postIopChromeToIframe.js +35 -0
- data/webpack/common/IopIframe/useIopIframeChrome.js +54 -0
- data/webpack/common/IopIframe/useIopIframeHeight.js +35 -0
- data/webpack/common/IopIframe/useIopIframeNavigate.js +40 -0
- metadata +27 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { fireEvent, render } from '@testing-library/react';
|
|
3
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
4
|
+
import ComplianceHostDetailsTabWrapper from '../ComplianceHostDetailsTab';
|
|
5
|
+
import { OVERVIEW_TAB_PATH } from '../../ForemanRhCloudHelpers';
|
|
6
|
+
|
|
7
|
+
const mockHistoryReplace = jest.fn();
|
|
8
|
+
|
|
9
|
+
jest.mock('react-router-dom', () => ({
|
|
10
|
+
...jest.requireActual('react-router-dom'),
|
|
11
|
+
useHistory: () => ({
|
|
12
|
+
replace: mockHistoryReplace,
|
|
13
|
+
}),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock('foremanReact/Root/Context/ForemanContext', () => ({
|
|
17
|
+
useForemanContext: () => ({
|
|
18
|
+
metadata: {
|
|
19
|
+
permissions: new Set(['view_hosts']),
|
|
20
|
+
},
|
|
21
|
+
}),
|
|
22
|
+
useForemanPermissions: () => new Set(['view_hosts']),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
jest.mock('foremanReact/common/I18n', () => ({
|
|
26
|
+
translate: str => str,
|
|
27
|
+
sprintf: str => str,
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
jest.mock('../../InsightsCompliance/complianceIframeHelpers', () => ({
|
|
31
|
+
getComplianceIframeSrc: () =>
|
|
32
|
+
'https://example.test/assets/apps/compliance/index.html',
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
const mockPostChrome = jest.fn();
|
|
36
|
+
jest.mock('../../common/IopIframe/postIopChromeToIframe', () => ({
|
|
37
|
+
postIopChromeToIframe: (...args) => mockPostChrome(...args),
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
const defaultResponse = {
|
|
41
|
+
id: 1,
|
|
42
|
+
operatingsystem_name: 'Red Hat Enterprise Linux 8',
|
|
43
|
+
insights_attributes: { uuid: 'insights-1' },
|
|
44
|
+
subscription_facet_attributes: { uuid: '1-2-3' },
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
describe('ComplianceHostDetailsTabWrapper', () => {
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
jest.clearAllMocks();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('renders compliance iframe for a valid host without redirecting', () => {
|
|
53
|
+
const { getByTestId, container } = render(
|
|
54
|
+
<MemoryRouter>
|
|
55
|
+
<ComplianceHostDetailsTabWrapper response={defaultResponse} />
|
|
56
|
+
</MemoryRouter>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
expect(
|
|
60
|
+
container.querySelector(
|
|
61
|
+
'.rh-cloud-insights-compliance-host-details-component'
|
|
62
|
+
)
|
|
63
|
+
).toBeTruthy();
|
|
64
|
+
|
|
65
|
+
const iframe = getByTestId('compliance-host-details-iframe');
|
|
66
|
+
expect(iframe.getAttribute('src')).toBe(
|
|
67
|
+
'https://example.test/assets/apps/compliance/index.html'
|
|
68
|
+
);
|
|
69
|
+
expect(mockHistoryReplace).not.toHaveBeenCalled();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('posts systems/<uuid> appRoute for the host-tab iframe', () => {
|
|
73
|
+
const { getByTestId } = render(
|
|
74
|
+
<MemoryRouter>
|
|
75
|
+
<ComplianceHostDetailsTabWrapper response={defaultResponse} />
|
|
76
|
+
</MemoryRouter>
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
fireEvent.load(getByTestId('compliance-host-details-iframe'));
|
|
80
|
+
|
|
81
|
+
expect(mockPostChrome).toHaveBeenCalledWith(
|
|
82
|
+
expect.anything(),
|
|
83
|
+
expect.objectContaining({
|
|
84
|
+
appRoute: 'systems/1-2-3',
|
|
85
|
+
embedded: 'host-tab',
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('posts updated appRoute when inventoryId changes', () => {
|
|
91
|
+
const responseHostA = {
|
|
92
|
+
...defaultResponse,
|
|
93
|
+
subscription_facet_attributes: { uuid: 'uuid-host-A' },
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const { rerender, getByTestId } = render(
|
|
97
|
+
<MemoryRouter>
|
|
98
|
+
<ComplianceHostDetailsTabWrapper response={responseHostA} />
|
|
99
|
+
</MemoryRouter>
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
expect(getByTestId('compliance-host-details-iframe')).toBeTruthy();
|
|
103
|
+
|
|
104
|
+
const responseHostB = {
|
|
105
|
+
...defaultResponse,
|
|
106
|
+
subscription_facet_attributes: { uuid: 'uuid-host-B' },
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
rerender(
|
|
110
|
+
<MemoryRouter>
|
|
111
|
+
<ComplianceHostDetailsTabWrapper response={responseHostB} />
|
|
112
|
+
</MemoryRouter>
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
fireEvent.load(getByTestId('compliance-host-details-iframe'));
|
|
116
|
+
|
|
117
|
+
expect(mockPostChrome).toHaveBeenCalledWith(
|
|
118
|
+
expect.anything(),
|
|
119
|
+
expect.objectContaining({
|
|
120
|
+
appRoute: 'systems/uuid-host-B',
|
|
121
|
+
embedded: 'host-tab',
|
|
122
|
+
})
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('redirects to Overview when tab should be hidden', () => {
|
|
127
|
+
const nonRhelResponse = {
|
|
128
|
+
id: 2,
|
|
129
|
+
operatingsystem_name: 'Ubuntu 20.04',
|
|
130
|
+
insights_attributes: { uuid: 'insights-1' },
|
|
131
|
+
subscription_facet_attributes: { uuid: '1-2-3' },
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const { container } = render(
|
|
135
|
+
<MemoryRouter>
|
|
136
|
+
<ComplianceHostDetailsTabWrapper response={nonRhelResponse} />
|
|
137
|
+
</MemoryRouter>
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
expect(mockHistoryReplace).toHaveBeenCalledWith(OVERVIEW_TAB_PATH);
|
|
141
|
+
expect(
|
|
142
|
+
container.querySelector(
|
|
143
|
+
'.rh-cloud-insights-compliance-host-details-component'
|
|
144
|
+
)
|
|
145
|
+
).toBeNull();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('does not redirect when host data is not yet loaded', () => {
|
|
149
|
+
const emptyResponse = { subscription_facet_attributes: { uuid: '1-2-3' } };
|
|
150
|
+
|
|
151
|
+
render(
|
|
152
|
+
<MemoryRouter>
|
|
153
|
+
<ComplianceHostDetailsTabWrapper response={emptyResponse} />
|
|
154
|
+
</MemoryRouter>
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
expect(mockHistoryReplace).not.toHaveBeenCalled();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
hasNoInsightsFacet,
|
|
11
11
|
} from './ForemanRhCloudHelpers';
|
|
12
12
|
import CVEsHostDetailsTabWrapper from './CVEsHostDetailsTab/CVEsHostDetailsTab';
|
|
13
|
+
import ComplianceHostDetailsTabWrapper from './ComplianceHostDetailsTab/ComplianceHostDetailsTab';
|
|
13
14
|
import InsightsVulnerabilityActionsBar from './InsightsVulnerabilityActionsBar';
|
|
14
15
|
|
|
15
16
|
const fills = [
|
|
@@ -51,6 +52,16 @@ const fills = [
|
|
|
51
52
|
title: __('Vulnerabilities'),
|
|
52
53
|
},
|
|
53
54
|
},
|
|
55
|
+
{
|
|
56
|
+
slot: 'host-details-page-tabs',
|
|
57
|
+
name: 'Compliance',
|
|
58
|
+
component: props => <ComplianceHostDetailsTabWrapper {...props} />,
|
|
59
|
+
weight: 350,
|
|
60
|
+
metadata: {
|
|
61
|
+
hideTab: props => isNotRhelHost(props) || hasNoInsightsFacet(props),
|
|
62
|
+
title: __('Compliance'),
|
|
63
|
+
},
|
|
64
|
+
},
|
|
54
65
|
];
|
|
55
66
|
|
|
56
67
|
export const registerFills = () => {
|
|
@@ -8,6 +8,7 @@ import IopRecommendationDetails from './IopRecommendationDetails/IopRecommendati
|
|
|
8
8
|
import IopPathwayDetails from './IopPathwayDetails/IopPathwayDetails';
|
|
9
9
|
import InsightsHostDetailsTab from './InsightsHostDetailsTab';
|
|
10
10
|
import CveDetailsPage from './CveDetailsPage';
|
|
11
|
+
import InsightsCompliancePage from './InsightsCompliance/InsightsCompliancePage';
|
|
11
12
|
import './common/styles.scss';
|
|
12
13
|
|
|
13
14
|
const pages = [
|
|
@@ -20,6 +21,7 @@ const pages = [
|
|
|
20
21
|
type: InsightsVulnerabilityListPage,
|
|
21
22
|
},
|
|
22
23
|
{ name: 'CveDetailsPage', type: CveDetailsPage },
|
|
24
|
+
{ name: 'InsightsCompliancePage', type: InsightsCompliancePage },
|
|
23
25
|
];
|
|
24
26
|
|
|
25
27
|
export const registerPages = () => {
|
|
@@ -57,6 +59,11 @@ export const routes = [
|
|
|
57
59
|
exact: true,
|
|
58
60
|
render: props => <CveDetailsPage {...props} />,
|
|
59
61
|
},
|
|
62
|
+
{
|
|
63
|
+
path: '/foreman_rh_cloud/insights_compliance',
|
|
64
|
+
exact: false,
|
|
65
|
+
render: props => <InsightsCompliancePage {...props} />,
|
|
66
|
+
},
|
|
60
67
|
];
|
|
61
68
|
|
|
62
69
|
export const registerRoutes = () => {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import IopAppIframe from '../common/IopIframe/IopAppIframe';
|
|
4
|
+
import {
|
|
5
|
+
getComplianceAppRoute,
|
|
6
|
+
getComplianceForemanPath,
|
|
7
|
+
getComplianceIframeSrc,
|
|
8
|
+
} from './complianceIframeHelpers';
|
|
9
|
+
import {
|
|
10
|
+
IOP_COMPLIANCE_NAVIGATE,
|
|
11
|
+
IOP_COMPLIANCE_READY,
|
|
12
|
+
} from './complianceIframeConstants';
|
|
13
|
+
|
|
14
|
+
import './ComplianceIframe.scss';
|
|
15
|
+
|
|
16
|
+
const ComplianceIframe = ({ appName }) => (
|
|
17
|
+
<IopAppIframe
|
|
18
|
+
appName={appName}
|
|
19
|
+
iframeSrc={getComplianceIframeSrc()}
|
|
20
|
+
getAppRoute={getComplianceAppRoute}
|
|
21
|
+
getForemanPath={getComplianceForemanPath}
|
|
22
|
+
defaultAppRoute="reports"
|
|
23
|
+
readyMessageType={IOP_COMPLIANCE_READY}
|
|
24
|
+
navigateMessageType={IOP_COMPLIANCE_NAVIGATE}
|
|
25
|
+
className="rh-cloud-insights-compliance-iframe"
|
|
26
|
+
testId="compliance-iframe"
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
ComplianceIframe.propTypes = {
|
|
31
|
+
appName: PropTypes.string.isRequired,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default ComplianceIframe;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
.rh-cloud-insights-compliance-page {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
min-height: calc(100vh - var(--pf-v5-global--spacer--2xl, 3rem));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.rh-cloud-insights-compliance-iframe {
|
|
8
|
+
border: 0;
|
|
9
|
+
flex: 1 1 auto;
|
|
10
|
+
min-height: 70vh;
|
|
11
|
+
width: 100%;
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import ComplianceIframe from './ComplianceIframe';
|
|
4
|
+
|
|
5
|
+
const InsightsCompliancePage = () => (
|
|
6
|
+
<div className="rh-cloud-insights-compliance-page compliance">
|
|
7
|
+
<ComplianceIframe appName="Compliance" />
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
export default InsightsCompliancePage;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
5
|
+
|
|
6
|
+
import InsightsCompliancePage from './InsightsCompliancePage';
|
|
7
|
+
|
|
8
|
+
jest.mock('foremanReact/Root/Context/ForemanContext', () => ({
|
|
9
|
+
useForemanContext: () => ({
|
|
10
|
+
metadata: {
|
|
11
|
+
permissions: new Set(['view_compliance']),
|
|
12
|
+
},
|
|
13
|
+
}),
|
|
14
|
+
useForemanPermissions: () => new Set(['view_compliance']),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
describe('InsightsCompliancePage', () => {
|
|
18
|
+
it('renders the compliance iframe container', () => {
|
|
19
|
+
render(
|
|
20
|
+
<MemoryRouter
|
|
21
|
+
initialEntries={['/foreman_rh_cloud/insights_compliance/reports']}
|
|
22
|
+
>
|
|
23
|
+
<InsightsCompliancePage />
|
|
24
|
+
</MemoryRouter>
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
expect(
|
|
28
|
+
document.querySelector('.rh-cloud-insights-compliance-page')
|
|
29
|
+
).toBeTruthy();
|
|
30
|
+
expect(screen.getByTestId('compliance-iframe')).toHaveAttribute(
|
|
31
|
+
'src',
|
|
32
|
+
`${window.location.origin}/assets/apps/compliance/index.html`
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const COMPLIANCE_STATIC_APP_PATH = '/assets/apps/compliance/index.html';
|
|
2
|
+
|
|
3
|
+
export const IOP_COMPLIANCE_READY = 'IOP_COMPLIANCE_READY';
|
|
4
|
+
|
|
5
|
+
export const IOP_COMPLIANCE_NAVIGATE = 'IOP_COMPLIANCE_NAVIGATE';
|
|
6
|
+
|
|
7
|
+
export const COMPLIANCE_ROUTE_PREFIX = '/foreman_rh_cloud/insights_compliance';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COMPLIANCE_ROUTE_PREFIX,
|
|
3
|
+
COMPLIANCE_STATIC_APP_PATH,
|
|
4
|
+
} from './complianceIframeConstants';
|
|
5
|
+
import {
|
|
6
|
+
getIopAppRoute,
|
|
7
|
+
getIopForemanPath,
|
|
8
|
+
getIopIframeSrc,
|
|
9
|
+
} from '../common/IopIframe/iopIframeHelpers';
|
|
10
|
+
|
|
11
|
+
const COMPLIANCE_IOP_PATH = {
|
|
12
|
+
routePrefix: COMPLIANCE_ROUTE_PREFIX,
|
|
13
|
+
fallbackRoute: 'reports',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const getComplianceForemanPath = (appRoute = 'reports') =>
|
|
17
|
+
getIopForemanPath(appRoute, COMPLIANCE_IOP_PATH);
|
|
18
|
+
|
|
19
|
+
export const getComplianceAppRoute = (pathname = '') =>
|
|
20
|
+
getIopAppRoute(pathname, COMPLIANCE_IOP_PATH);
|
|
21
|
+
|
|
22
|
+
export const getComplianceIframeSrc = () =>
|
|
23
|
+
getIopIframeSrc(COMPLIANCE_STATIC_APP_PATH);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getComplianceAppRoute,
|
|
3
|
+
getComplianceForemanPath,
|
|
4
|
+
getComplianceIframeSrc,
|
|
5
|
+
} from './complianceIframeHelpers';
|
|
6
|
+
|
|
7
|
+
describe('complianceIframeHelpers', () => {
|
|
8
|
+
describe('getComplianceAppRoute', () => {
|
|
9
|
+
it('maps foreman compliance paths to app routes', () => {
|
|
10
|
+
expect(
|
|
11
|
+
getComplianceAppRoute('/foreman_rh_cloud/insights_compliance/reports')
|
|
12
|
+
).toBe('reports');
|
|
13
|
+
expect(
|
|
14
|
+
getComplianceAppRoute(
|
|
15
|
+
'/foreman_rh_cloud/insights_compliance/reports/report-1'
|
|
16
|
+
)
|
|
17
|
+
).toBe('reports/report-1');
|
|
18
|
+
expect(
|
|
19
|
+
getComplianceAppRoute(
|
|
20
|
+
'/foreman_rh_cloud/insights_compliance/scappolicies/new'
|
|
21
|
+
)
|
|
22
|
+
).toBe('scappolicies/new');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('defaults to reports for unknown paths', () => {
|
|
26
|
+
expect(getComplianceAppRoute('/foreman_rh_cloud/insights_cloud')).toBe(
|
|
27
|
+
'reports'
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('getComplianceForemanPath', () => {
|
|
33
|
+
it('maps app routes to foreman compliance paths', () => {
|
|
34
|
+
expect(getComplianceForemanPath('reports')).toBe(
|
|
35
|
+
'/foreman_rh_cloud/insights_compliance/reports'
|
|
36
|
+
);
|
|
37
|
+
expect(getComplianceForemanPath('scappolicies/new')).toBe(
|
|
38
|
+
'/foreman_rh_cloud/insights_compliance/scappolicies/new'
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('getComplianceIframeSrc', () => {
|
|
44
|
+
it('returns the static compliance app entry', () => {
|
|
45
|
+
expect(getComplianceIframeSrc()).toBe(
|
|
46
|
+
`${window.location.origin}/assets/apps/compliance/index.html`
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -25,6 +25,16 @@ const PERMISSION_MAPPING = {
|
|
|
25
25
|
],
|
|
26
26
|
view_advisor: ['advisor:recommendation-results:read', 'advisor:exports:read'],
|
|
27
27
|
edit_advisor: ['advisor:disable-recommendations:write'],
|
|
28
|
+
view_compliance: [
|
|
29
|
+
'inventory:hosts:read',
|
|
30
|
+
'compliance:report:read',
|
|
31
|
+
'compliance:policy:read',
|
|
32
|
+
],
|
|
33
|
+
edit_compliance: [
|
|
34
|
+
'compliance:policy:write',
|
|
35
|
+
'compliance:policy:create',
|
|
36
|
+
'compliance:policy:delete',
|
|
37
|
+
],
|
|
28
38
|
};
|
|
29
39
|
|
|
30
40
|
/**
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { useHistory, useLocation } from 'react-router-dom';
|
|
4
|
+
import { useInsightsPermissions } from '../Hooks/PermissionsHooks';
|
|
5
|
+
import { useIopIframeChrome } from './useIopIframeChrome';
|
|
6
|
+
import { useIopIframeNavigate } from './useIopIframeNavigate';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Full-page IoP app iframe: chrome sync + Foreman URL updates from NAVIGATE.
|
|
10
|
+
* App shells supply src, route mappers, and message types.
|
|
11
|
+
*/
|
|
12
|
+
const IopAppIframe = ({
|
|
13
|
+
appName,
|
|
14
|
+
iframeSrc,
|
|
15
|
+
getAppRoute,
|
|
16
|
+
getForemanPath,
|
|
17
|
+
defaultAppRoute,
|
|
18
|
+
readyMessageType,
|
|
19
|
+
navigateMessageType,
|
|
20
|
+
className = '',
|
|
21
|
+
testId = 'iop-app-iframe',
|
|
22
|
+
}) => {
|
|
23
|
+
const iframeRef = useRef(null);
|
|
24
|
+
const history = useHistory();
|
|
25
|
+
const { pathname } = useLocation();
|
|
26
|
+
const permissions = useInsightsPermissions();
|
|
27
|
+
const appRoute = getAppRoute(pathname);
|
|
28
|
+
|
|
29
|
+
const sendChromeContext = useIopIframeChrome({
|
|
30
|
+
iframeRef,
|
|
31
|
+
permissions,
|
|
32
|
+
appRoute,
|
|
33
|
+
pathname,
|
|
34
|
+
readyMessageType,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
useIopIframeNavigate({
|
|
38
|
+
history,
|
|
39
|
+
navigateMessageType,
|
|
40
|
+
getForemanPath,
|
|
41
|
+
defaultAppRoute,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
// eslint-disable-next-line jsx-a11y/iframe-has-title
|
|
46
|
+
<iframe
|
|
47
|
+
ref={iframeRef}
|
|
48
|
+
className={className}
|
|
49
|
+
data-testid={testId}
|
|
50
|
+
src={iframeSrc}
|
|
51
|
+
aria-label={appName}
|
|
52
|
+
onLoad={sendChromeContext}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
IopAppIframe.propTypes = {
|
|
58
|
+
appName: PropTypes.string.isRequired,
|
|
59
|
+
iframeSrc: PropTypes.string.isRequired,
|
|
60
|
+
getAppRoute: PropTypes.func.isRequired,
|
|
61
|
+
getForemanPath: PropTypes.func.isRequired,
|
|
62
|
+
defaultAppRoute: PropTypes.string.isRequired,
|
|
63
|
+
readyMessageType: PropTypes.string.isRequired,
|
|
64
|
+
navigateMessageType: PropTypes.string.isRequired,
|
|
65
|
+
className: PropTypes.string,
|
|
66
|
+
testId: PropTypes.string,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
IopAppIframe.defaultProps = {
|
|
70
|
+
className: '',
|
|
71
|
+
testId: 'iop-app-iframe',
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default IopAppIframe;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { useInsightsPermissions } from '../Hooks/PermissionsHooks';
|
|
4
|
+
import { useIopIframeChrome } from './useIopIframeChrome';
|
|
5
|
+
import { useIopIframeHeight } from './useIopIframeHeight';
|
|
6
|
+
import './IopHostDetailsIframe.scss';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Host-details tab iframe for an IoP app (chrome post + READY re-sync).
|
|
10
|
+
* App-specific wrappers supply src, appRoute, title, and styles.
|
|
11
|
+
*/
|
|
12
|
+
const IopHostDetailsIframe = ({
|
|
13
|
+
inventoryId,
|
|
14
|
+
iframeSrc,
|
|
15
|
+
appRoute,
|
|
16
|
+
title,
|
|
17
|
+
readyMessageType,
|
|
18
|
+
className = '',
|
|
19
|
+
iframeClassName = 'rh-cloud-iop-host-details-iframe',
|
|
20
|
+
testId = 'iop-host-details-iframe',
|
|
21
|
+
}) => {
|
|
22
|
+
const iframeRef = useRef(null);
|
|
23
|
+
const permissions = useInsightsPermissions();
|
|
24
|
+
const sendChromeContext = useIopIframeChrome({
|
|
25
|
+
iframeRef,
|
|
26
|
+
permissions,
|
|
27
|
+
appRoute,
|
|
28
|
+
embedded: 'host-tab',
|
|
29
|
+
readyMessageType,
|
|
30
|
+
});
|
|
31
|
+
useIopIframeHeight({ iframeRef });
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className={className}>
|
|
35
|
+
<iframe
|
|
36
|
+
key={inventoryId}
|
|
37
|
+
ref={iframeRef}
|
|
38
|
+
className={iframeClassName}
|
|
39
|
+
data-testid={testId}
|
|
40
|
+
src={iframeSrc}
|
|
41
|
+
title={title}
|
|
42
|
+
aria-label={title}
|
|
43
|
+
onLoad={sendChromeContext}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
IopHostDetailsIframe.propTypes = {
|
|
50
|
+
inventoryId: PropTypes.string.isRequired,
|
|
51
|
+
iframeSrc: PropTypes.string.isRequired,
|
|
52
|
+
appRoute: PropTypes.string.isRequired,
|
|
53
|
+
title: PropTypes.string.isRequired,
|
|
54
|
+
readyMessageType: PropTypes.string.isRequired,
|
|
55
|
+
className: PropTypes.string,
|
|
56
|
+
iframeClassName: PropTypes.string,
|
|
57
|
+
testId: PropTypes.string,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
IopHostDetailsIframe.defaultProps = {
|
|
61
|
+
className: '',
|
|
62
|
+
iframeClassName: 'rh-cloud-iop-host-details-iframe',
|
|
63
|
+
testId: 'iop-host-details-iframe',
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default IopHostDetailsIframe;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import {
|
|
4
|
+
isNotRhelHost,
|
|
5
|
+
hasNoInsightsFacet,
|
|
6
|
+
useTabRedirect,
|
|
7
|
+
} from '../../ForemanRhCloudHelpers';
|
|
8
|
+
import IopHostDetailsIframe from './IopHostDetailsIframe';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Host-details fill wrapper: hide/redirect for non-RHEL / no Insights facet,
|
|
12
|
+
* then render an IoP app iframe for the subscription UUID.
|
|
13
|
+
*/
|
|
14
|
+
const IopHostDetailsIframeTab = ({
|
|
15
|
+
response = {},
|
|
16
|
+
iframeSrc,
|
|
17
|
+
buildAppRoute,
|
|
18
|
+
title,
|
|
19
|
+
readyMessageType,
|
|
20
|
+
className = '',
|
|
21
|
+
iframeClassName = 'rh-cloud-iop-host-details-iframe',
|
|
22
|
+
testId = 'iop-host-details-iframe',
|
|
23
|
+
}) => {
|
|
24
|
+
const isHostDataLoaded = Boolean(response?.id);
|
|
25
|
+
const shouldHideTab = useTabRedirect(
|
|
26
|
+
isHostDataLoaded &&
|
|
27
|
+
(isNotRhelHost({ hostDetails: response }) ||
|
|
28
|
+
hasNoInsightsFacet({ response, hostDetails: response }))
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
if (shouldHideTab) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// eslint-disable-next-line camelcase
|
|
36
|
+
const inventoryId = response?.subscription_facet_attributes?.uuid;
|
|
37
|
+
|
|
38
|
+
if (!inventoryId) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<IopHostDetailsIframe
|
|
44
|
+
inventoryId={inventoryId}
|
|
45
|
+
iframeSrc={iframeSrc}
|
|
46
|
+
appRoute={buildAppRoute(inventoryId)}
|
|
47
|
+
title={title}
|
|
48
|
+
readyMessageType={readyMessageType}
|
|
49
|
+
className={className}
|
|
50
|
+
iframeClassName={iframeClassName}
|
|
51
|
+
testId={testId}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
IopHostDetailsIframeTab.propTypes = {
|
|
57
|
+
response: PropTypes.shape({
|
|
58
|
+
id: PropTypes.number,
|
|
59
|
+
operatingsystem_name: PropTypes.string,
|
|
60
|
+
insights_attributes: PropTypes.object,
|
|
61
|
+
subscription_facet_attributes: PropTypes.shape({
|
|
62
|
+
uuid: PropTypes.string,
|
|
63
|
+
}),
|
|
64
|
+
}),
|
|
65
|
+
iframeSrc: PropTypes.string.isRequired,
|
|
66
|
+
buildAppRoute: PropTypes.func.isRequired,
|
|
67
|
+
title: PropTypes.string.isRequired,
|
|
68
|
+
readyMessageType: PropTypes.string.isRequired,
|
|
69
|
+
className: PropTypes.string,
|
|
70
|
+
iframeClassName: PropTypes.string,
|
|
71
|
+
testId: PropTypes.string,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
IopHostDetailsIframeTab.defaultProps = {
|
|
75
|
+
response: {},
|
|
76
|
+
className: '',
|
|
77
|
+
iframeClassName: 'rh-cloud-iop-host-details-iframe',
|
|
78
|
+
testId: 'iop-host-details-iframe',
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export default IopHostDetailsIframeTab;
|