foreman_leapp 3.1.0 → 3.2.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/views/api/v2/preupgrade_report_entries/base.json.rabl +1 -1
- data/lib/foreman_leapp/engine.rb +2 -0
- data/lib/foreman_leapp/version.rb +1 -1
- data/webpack/components/PreupgradeReports/PreupgradeReports.js +1 -1
- data/webpack/components/PreupgradeReports/PreupgradeReportsHelpers.js +2 -2
- data/webpack/components/PreupgradeReports/__tests__/__snapshots__/PreupgradeReports.test.js.snap +1 -1
- data/webpack/components/PreupgradeReports/__tests__/__snapshots__/PreupgradeReportsHelpers.test.js.snap +25 -1
- data/webpack/components/PreupgradeReports/components/__snapshots__/NoReports.test.js.snap +6 -0
- data/webpack/components/PreupgradeReportsList/__tests__/PreupgradeReportsList.test.js +119 -35
- data/webpack/components/PreupgradeReportsList/index.js +2 -3
- data/webpack/components/PreupgradeReportsTable/__tests__/PreupgradeReportsTable.test.js +117 -0
- data/webpack/components/PreupgradeReportsTable/index.js +180 -0
- data/webpack/global_index.js +10 -0
- metadata +6 -8
- data/webpack/__mocks__/foremanReact/common/I18n.js +0 -2
- data/webpack/__mocks__/foremanReact/components/Pagination.js +0 -2
- data/webpack/__mocks__/foremanReact/components/common/EmptyState.js +0 -1
- data/webpack/__mocks__/foremanReact/components/common/MessageBox.js +0 -2
- data/webpack/components/PreupgradeReportsList/__tests__/__snapshots__/PreupgradeReportsList.test.js.snap +0 -135
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 16c14820991bf9652f57e0d771fcf79adff5d66f94c4f30e68317660c45f0e86
|
|
4
|
+
data.tar.gz: 54b3dd8932c705785e17f56332e458be65e5f6d63b4e1077683d34116d0164ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 946f344825f4eef41f8043959a44f4aff7d590007d655498454338881782057ef41fab6ae18016e489227af24ee5658da3d19a296180df2c2df3607154ba73f3
|
|
7
|
+
data.tar.gz: 290907b7b7ee820d26568000380baaff26d281c98f7efaee2aaaae28a43d8eaa5ed0e300d43c95e46f38f7b1fdf16403bad8de9d2eb6d2dc7f15882a5ee0f505
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
object @preupgrade_report_entry
|
|
2
2
|
|
|
3
|
-
attributes :id, :preupgrade_report_id, :host_id, :hostname, :title, :actor, :audience,
|
|
3
|
+
attributes :id, :detail, :preupgrade_report_id, :host_id, :hostname, :title, :actor, :audience,
|
|
4
4
|
:severity, :leapp_run_id, :summary, :tags, :flags, :created_at, :updated_at
|
data/lib/foreman_leapp/engine.rb
CHANGED
|
@@ -119,7 +119,7 @@ const withLoadingState = Component => componentProps => {
|
|
|
119
119
|
<MessageBox
|
|
120
120
|
key="preupgrade-reports-error"
|
|
121
121
|
icontype="error-circle-o"
|
|
122
|
-
msg={sprintf(__('Could not retrieve data: %(status) - %(msg)'), {
|
|
122
|
+
msg={sprintf(__('Could not retrieve data: %(status)s - %(msg)s'), {
|
|
123
123
|
status: error.statusText,
|
|
124
124
|
msg: error.errorMsg,
|
|
125
125
|
})}
|
|
@@ -57,9 +57,9 @@ export const idsForInvocationFromReports = reports =>
|
|
|
57
57
|
idsForInvocationFromEntries(flattenEntries(reports));
|
|
58
58
|
|
|
59
59
|
export const entriesPage = (entries, pagination) => {
|
|
60
|
-
const offset = (pagination.page - 1) * pagination.
|
|
60
|
+
const offset = (pagination.page - 1) * pagination.perPage;
|
|
61
61
|
|
|
62
|
-
return entries.slice(offset, offset + pagination.
|
|
62
|
+
return entries.slice(offset, offset + pagination.perPage);
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
export const filterEntries = (attribute, value, entries) => {
|
data/webpack/components/PreupgradeReports/__tests__/__snapshots__/PreupgradeReports.test.js.snap
CHANGED
|
@@ -4,7 +4,7 @@ exports[`PreupgradeReports should render error 1`] = `
|
|
|
4
4
|
<MessageBox
|
|
5
5
|
icontype="error-circle-o"
|
|
6
6
|
key="preupgrade-reports-error"
|
|
7
|
-
msg="Could not retrieve data:
|
|
7
|
+
msg="Could not retrieve data: Internal server error - Well, this is embarassing"
|
|
8
8
|
/>
|
|
9
9
|
`;
|
|
10
10
|
|
|
@@ -131,7 +131,31 @@ Array [
|
|
|
131
131
|
]
|
|
132
132
|
`;
|
|
133
133
|
|
|
134
|
-
exports[`PreupgradeReportsHelpers should return entries page 1`] = `
|
|
134
|
+
exports[`PreupgradeReportsHelpers should return entries page 1`] = `
|
|
135
|
+
Array [
|
|
136
|
+
Object {
|
|
137
|
+
"flags": Array [],
|
|
138
|
+
"hostname": "foo.example.com",
|
|
139
|
+
"id": 45,
|
|
140
|
+
"severity": "low",
|
|
141
|
+
"title": "Not enough credits",
|
|
142
|
+
},
|
|
143
|
+
Object {
|
|
144
|
+
"flags": Array [],
|
|
145
|
+
"hostname": "foo.example.com",
|
|
146
|
+
"id": 46,
|
|
147
|
+
"severity": "medium",
|
|
148
|
+
"title": "SELinux is turned off",
|
|
149
|
+
},
|
|
150
|
+
Object {
|
|
151
|
+
"flags": Array [],
|
|
152
|
+
"hostname": "foo.example.com",
|
|
153
|
+
"id": 47,
|
|
154
|
+
"severity": "medium",
|
|
155
|
+
"title": "Root password is too short",
|
|
156
|
+
},
|
|
157
|
+
]
|
|
158
|
+
`;
|
|
135
159
|
|
|
136
160
|
exports[`PreupgradeReportsHelpers should return fixable entries 1`] = `
|
|
137
161
|
Array [
|
|
@@ -2,18 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`NoReports should render when reports expected 1`] = `
|
|
4
4
|
<EmptyStatePattern
|
|
5
|
+
action={null}
|
|
5
6
|
description="The preupgrade report could not be generated, check the job details for the reason"
|
|
7
|
+
documentation={null}
|
|
6
8
|
header="No Preupgrade Report Available"
|
|
7
9
|
icon="warning-triangle-o"
|
|
8
10
|
iconType="pf"
|
|
11
|
+
secondaryActions={Array []}
|
|
9
12
|
/>
|
|
10
13
|
`;
|
|
11
14
|
|
|
12
15
|
exports[`NoReports should render when reports not expected 1`] = `
|
|
13
16
|
<EmptyStatePattern
|
|
17
|
+
action={null}
|
|
14
18
|
description="The preupgrade report will be available after the job finishes"
|
|
19
|
+
documentation={null}
|
|
15
20
|
header="No Preupgrade Report Available"
|
|
16
21
|
icon="in-progress"
|
|
17
22
|
iconType="pf"
|
|
23
|
+
secondaryActions={Array []}
|
|
18
24
|
/>
|
|
19
25
|
`;
|
|
@@ -1,40 +1,124 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
2
4
|
|
|
3
5
|
import PreupgradeReportsList from '../index';
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
7
|
+
jest.mock('foremanReact/components/Pagination', () => {
|
|
8
|
+
const MockPagination = () => <div data-testid="pagination">Pagination</div>;
|
|
9
|
+
return MockPagination;
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
jest.mock('../components/images/i_severity-high.svg', () => 'severity-high.svg');
|
|
13
|
+
jest.mock('../components/images/i_severity-med.svg', () => 'severity-med.svg');
|
|
14
|
+
jest.mock('../components/images/i_severity-low.svg', () => 'severity-low.svg');
|
|
15
|
+
|
|
16
|
+
const createMockEntries = count =>
|
|
17
|
+
Array.from({ length: count }, (_, i) => ({
|
|
18
|
+
id: i + 1,
|
|
19
|
+
preupgradeReportId: 100,
|
|
20
|
+
title: `Entry ${i + 1}`,
|
|
21
|
+
hostname: `host${i + 1}.example.com`,
|
|
22
|
+
severity: i % 3 === 0 ? 'high' : i % 3 === 1 ? 'medium' : 'low',
|
|
23
|
+
flags: i === 0 ? ['inhibitor'] : [],
|
|
24
|
+
detail: {
|
|
25
|
+
remediations:
|
|
26
|
+
i < 2
|
|
27
|
+
? [{ type: 'command', context: ['echo', 'fix', 'command'] }]
|
|
28
|
+
: [],
|
|
29
|
+
},
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
const defaultProps = {
|
|
33
|
+
allEntries: createMockEntries(4),
|
|
34
|
+
isSelected: () => false,
|
|
35
|
+
toggleSelected: jest.fn(),
|
|
36
|
+
sort: { attribute: '', order: 'asc' },
|
|
37
|
+
changeSort: jest.fn(),
|
|
38
|
+
toggleSelectAll: jest.fn(),
|
|
37
39
|
};
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
const renderComponent = (props = {}) =>
|
|
42
|
+
render(<PreupgradeReportsList {...defaultProps} {...props} />);
|
|
43
|
+
|
|
44
|
+
describe('PreupgradeReportsList', () => {
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
jest.clearAllMocks();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('renders the list header', () => {
|
|
50
|
+
renderComponent();
|
|
51
|
+
|
|
52
|
+
expect(screen.getByText('Title')).toBeInTheDocument();
|
|
53
|
+
expect(screen.getByText('Host')).toBeInTheDocument();
|
|
54
|
+
expect(screen.getByText('Risk Factor')).toBeInTheDocument();
|
|
55
|
+
expect(screen.getByText('Has Remediation?')).toBeInTheDocument();
|
|
56
|
+
expect(screen.getByText('Inhibitor?')).toBeInTheDocument();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('renders entry titles', () => {
|
|
60
|
+
renderComponent();
|
|
61
|
+
|
|
62
|
+
expect(screen.getByText('Entry 1')).toBeInTheDocument();
|
|
63
|
+
expect(screen.getByText('Entry 2')).toBeInTheDocument();
|
|
64
|
+
expect(screen.getByText('Entry 3')).toBeInTheDocument();
|
|
65
|
+
expect(screen.getByText('Entry 4')).toBeInTheDocument();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('renders entry hostnames', () => {
|
|
69
|
+
renderComponent();
|
|
70
|
+
|
|
71
|
+
expect(screen.getByText('host1.example.com')).toBeInTheDocument();
|
|
72
|
+
expect(screen.getByText('host2.example.com')).toBeInTheDocument();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('renders checkboxes for entries', () => {
|
|
76
|
+
renderComponent();
|
|
77
|
+
|
|
78
|
+
const checkboxes = screen.getAllByRole('checkbox');
|
|
79
|
+
expect(checkboxes.length).toBeGreaterThan(0);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('calls toggleSelectAll when header checkbox is clicked', () => {
|
|
83
|
+
const toggleSelectAll = jest.fn();
|
|
84
|
+
renderComponent({ toggleSelectAll });
|
|
85
|
+
|
|
86
|
+
const checkboxes = screen.getAllByRole('checkbox');
|
|
87
|
+
fireEvent.click(checkboxes[0]);
|
|
88
|
+
|
|
89
|
+
expect(toggleSelectAll).toHaveBeenCalled();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('renders with empty entries array', () => {
|
|
93
|
+
renderComponent({ allEntries: [] });
|
|
94
|
+
|
|
95
|
+
expect(screen.getByText('Title')).toBeInTheDocument();
|
|
96
|
+
expect(screen.queryByText('Entry 1')).not.toBeInTheDocument();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('renders entries with selected state', () => {
|
|
100
|
+
const isSelected = entry => entry.id === 1;
|
|
101
|
+
renderComponent({ isSelected });
|
|
102
|
+
|
|
103
|
+
const checkboxes = screen.getAllByRole('checkbox');
|
|
104
|
+
const entryCheckboxes = checkboxes.slice(1);
|
|
105
|
+
expect(entryCheckboxes[0]).toBeChecked();
|
|
106
|
+
expect(entryCheckboxes[1]).not.toBeChecked();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('calls toggleSelected when entry checkbox is clicked', () => {
|
|
110
|
+
const toggleSelected = jest.fn();
|
|
111
|
+
renderComponent({ toggleSelected });
|
|
112
|
+
|
|
113
|
+
const checkboxes = screen.getAllByRole('checkbox');
|
|
114
|
+
fireEvent.click(checkboxes[1]);
|
|
115
|
+
|
|
116
|
+
expect(toggleSelected).toHaveBeenCalled();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('renders pagination component', () => {
|
|
120
|
+
renderComponent();
|
|
121
|
+
|
|
122
|
+
expect(screen.getByTestId('pagination')).toBeInTheDocument();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -19,11 +19,10 @@ const PreupgradeReportsList = ({
|
|
|
19
19
|
changeSort,
|
|
20
20
|
toggleSelectAll,
|
|
21
21
|
}) => {
|
|
22
|
-
const { perPage
|
|
22
|
+
const { perPage } = useForemanSettings();
|
|
23
23
|
const [pagination, setPagination] = useState({
|
|
24
24
|
page: 1,
|
|
25
|
-
|
|
26
|
-
perPageOptions,
|
|
25
|
+
perPage,
|
|
27
26
|
});
|
|
28
27
|
|
|
29
28
|
return (
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
4
|
+
import { Provider } from 'react-redux';
|
|
5
|
+
import configureMockStore from 'redux-mock-store';
|
|
6
|
+
import thunk from 'redux-thunk';
|
|
7
|
+
import { APIActions } from 'foremanReact/redux/API';
|
|
8
|
+
import PreupgradeReportsTable from '../index';
|
|
9
|
+
|
|
10
|
+
jest.mock('foremanReact/redux/API');
|
|
11
|
+
|
|
12
|
+
const mockStore = configureMockStore([thunk]);
|
|
13
|
+
|
|
14
|
+
const mockJobId = 42;
|
|
15
|
+
const mockReportId = 999;
|
|
16
|
+
const mockJobData = {
|
|
17
|
+
id: mockJobId,
|
|
18
|
+
template_name: 'Run preupgrade via Leapp',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const mockEntries = Array.from({ length: 12 }, (_, i) => ({
|
|
22
|
+
id: i + 1,
|
|
23
|
+
title: `Report Entry ${i + 1}`,
|
|
24
|
+
hostname: 'example.com',
|
|
25
|
+
severity: i === 0 ? 'high' : 'low',
|
|
26
|
+
flags: i === 0 ? ['inhibitor'] : [],
|
|
27
|
+
detail: { remediations: i === 0 ? [{ type: 'cmd' }] : [] },
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
describe('PreupgradeReportsTable', () => {
|
|
31
|
+
let store;
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
store = mockStore({ API: {} });
|
|
35
|
+
jest.clearAllMocks();
|
|
36
|
+
|
|
37
|
+
APIActions.get.mockImplementation(({ key, handleSuccess }) => {
|
|
38
|
+
return dispatch => {
|
|
39
|
+
if (key.includes('GET_LEAPP_REPORT_LIST'))
|
|
40
|
+
handleSuccess({ results: [{ id: mockReportId }] });
|
|
41
|
+
if (key.includes('GET_LEAPP_REPORT_DETAIL'))
|
|
42
|
+
handleSuccess({
|
|
43
|
+
id: mockReportId,
|
|
44
|
+
preupgrade_report_entries: mockEntries,
|
|
45
|
+
});
|
|
46
|
+
return { type: 'MOCK_API_SUCCESS' };
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const renderComponent = () =>
|
|
52
|
+
render(
|
|
53
|
+
<Provider store={store}>
|
|
54
|
+
<PreupgradeReportsTable data={mockJobData} />
|
|
55
|
+
</Provider>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const expandSection = () => {
|
|
59
|
+
fireEvent.click(screen.getByText('Leapp preupgrade report'));
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
it('renders data', async () => {
|
|
63
|
+
renderComponent();
|
|
64
|
+
expandSection();
|
|
65
|
+
|
|
66
|
+
await waitFor(() => screen.getByText('Report Entry 1'));
|
|
67
|
+
|
|
68
|
+
expect(screen.getByText('Report Entry 1')).toBeInTheDocument();
|
|
69
|
+
expect(screen.getByText('Report Entry 5')).toBeInTheDocument();
|
|
70
|
+
expect(screen.queryByText('Report Entry 6')).not.toBeInTheDocument();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('paginates to the next page', async () => {
|
|
74
|
+
renderComponent();
|
|
75
|
+
expandSection();
|
|
76
|
+
await waitFor(() => screen.getByText('Report Entry 1'));
|
|
77
|
+
|
|
78
|
+
fireEvent.click(screen.getAllByLabelText('Go to next page')[0]);
|
|
79
|
+
|
|
80
|
+
await waitFor(() => screen.getByText('Report Entry 6'));
|
|
81
|
+
expect(screen.getByText('Report Entry 10')).toBeInTheDocument();
|
|
82
|
+
expect(screen.queryByText('Report Entry 1')).not.toBeInTheDocument();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('changes perPage limit to 10', async () => {
|
|
86
|
+
renderComponent();
|
|
87
|
+
expandSection();
|
|
88
|
+
await waitFor(() => screen.getByText('Report Entry 1'));
|
|
89
|
+
|
|
90
|
+
fireEvent.click(screen.getAllByLabelText('Items per page')[0]);
|
|
91
|
+
fireEvent.click(screen.getAllByText('10 per page')[0]);
|
|
92
|
+
|
|
93
|
+
await waitFor(() => {
|
|
94
|
+
expect(screen.getByText('Report Entry 10')).toBeInTheDocument();
|
|
95
|
+
expect(screen.queryByText('Report Entry 11')).not.toBeInTheDocument();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('renders empty state message when no issues found', async () => {
|
|
100
|
+
APIActions.get.mockImplementation(({ key, handleSuccess }) => {
|
|
101
|
+
return () => {
|
|
102
|
+
if (key.includes('GET_LEAPP_REPORT_LIST'))
|
|
103
|
+
handleSuccess({ results: [{ id: mockReportId }] });
|
|
104
|
+
if (key.includes('GET_LEAPP_REPORT_DETAIL'))
|
|
105
|
+
handleSuccess({ id: mockReportId, preupgrade_report_entries: [] });
|
|
106
|
+
return { type: 'EMPTY' };
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
renderComponent();
|
|
111
|
+
expandSection();
|
|
112
|
+
|
|
113
|
+
await waitFor(() => {
|
|
114
|
+
expect(screen.getByText('The preupgrade report shows no issues.')).toBeInTheDocument();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import React, { useEffect, useState } from 'react';
|
|
3
|
+
import { useDispatch } from 'react-redux';
|
|
4
|
+
import { ExpandableSection, Label, Tooltip } from '@patternfly/react-core';
|
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
6
|
+
import { Table } from 'foremanReact/components/PF4/TableIndexPage/Table/Table';
|
|
7
|
+
import { APIActions } from 'foremanReact/redux/API';
|
|
8
|
+
import { STATUS } from 'foremanReact/constants';
|
|
9
|
+
|
|
10
|
+
import { entriesPage } from '../PreupgradeReports/PreupgradeReportsHelpers';
|
|
11
|
+
|
|
12
|
+
const renderSeverityLabel = severity => {
|
|
13
|
+
switch (severity) {
|
|
14
|
+
case 'high':
|
|
15
|
+
return <Label color="red">{__('High')}</Label>;
|
|
16
|
+
case 'medium':
|
|
17
|
+
return <Label color="orange">{__('Medium')}</Label>;
|
|
18
|
+
case 'low':
|
|
19
|
+
return <Label color="blue">{__('Low')}</Label>;
|
|
20
|
+
case 'info':
|
|
21
|
+
return <Label color="grey">{__('Info')}</Label>;
|
|
22
|
+
default:
|
|
23
|
+
return <Label color="grey">{severity || __('Info')}</Label>;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const PreupgradeReportsTable = ({ data = {} }) => {
|
|
28
|
+
const [error, setError] = useState(null);
|
|
29
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
30
|
+
const [pagination, setPagination] = useState({ page: 1, perPage: 5 });
|
|
31
|
+
const [reportData, setReportData] = useState(null);
|
|
32
|
+
const [status, setStatus] = useState(STATUS.RESOLVED);
|
|
33
|
+
const dispatch = useDispatch();
|
|
34
|
+
// eslint-disable-next-line camelcase
|
|
35
|
+
const isLeappJob = data?.template_name?.includes('Run preupgrade via Leapp');
|
|
36
|
+
|
|
37
|
+
const columns = {
|
|
38
|
+
title: {
|
|
39
|
+
title: __('Title'),
|
|
40
|
+
},
|
|
41
|
+
host: {
|
|
42
|
+
title: __('Host'),
|
|
43
|
+
wrapper: entry =>
|
|
44
|
+
entry.hostname || (reportData && reportData.hostname) || '-',
|
|
45
|
+
},
|
|
46
|
+
risk_factor: {
|
|
47
|
+
title: __('Risk Factor'),
|
|
48
|
+
wrapper: ({ severity }) => renderSeverityLabel(severity),
|
|
49
|
+
},
|
|
50
|
+
has_remediation: {
|
|
51
|
+
title: __('Has Remediation?'),
|
|
52
|
+
wrapper: entry =>
|
|
53
|
+
entry.detail && entry.detail.remediations ? __('Yes') : __('No'),
|
|
54
|
+
},
|
|
55
|
+
inhibitor: {
|
|
56
|
+
title: __('Inhibitor?'),
|
|
57
|
+
wrapper: entry =>
|
|
58
|
+
entry.flags && entry.flags.some(flag => flag === 'inhibitor') ? (
|
|
59
|
+
<Tooltip content={__('This issue inhibits the upgrade.')}>
|
|
60
|
+
<span>{__('Yes')}</span>
|
|
61
|
+
</Tooltip>
|
|
62
|
+
) : (
|
|
63
|
+
__('No')
|
|
64
|
+
),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
let isMounted = true;
|
|
70
|
+
if (!isLeappJob || !isExpanded || reportData) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
setStatus(STATUS.PENDING);
|
|
74
|
+
|
|
75
|
+
dispatch(
|
|
76
|
+
APIActions.get({
|
|
77
|
+
key: `GET_LEAPP_REPORT_LIST_${data.id}`,
|
|
78
|
+
url: `/api/job_invocations/${data.id}/preupgrade_reports`,
|
|
79
|
+
handleSuccess: listResponse => {
|
|
80
|
+
if (!isMounted) return;
|
|
81
|
+
const listPayload = listResponse.data || listResponse;
|
|
82
|
+
const summary = listPayload.results?.[0];
|
|
83
|
+
if (summary?.id) {
|
|
84
|
+
dispatch(
|
|
85
|
+
APIActions.get({
|
|
86
|
+
key: `GET_LEAPP_REPORT_DETAIL_${summary.id}`,
|
|
87
|
+
url: `/api/preupgrade_reports/${summary.id}`,
|
|
88
|
+
handleSuccess: detailResponse => {
|
|
89
|
+
if (isMounted) {
|
|
90
|
+
const detailPayload = detailResponse.data || detailResponse;
|
|
91
|
+
setReportData(detailPayload);
|
|
92
|
+
setStatus(STATUS.RESOLVED);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
handleError: err => {
|
|
96
|
+
if (isMounted) {
|
|
97
|
+
setError(err);
|
|
98
|
+
setStatus(STATUS.ERROR);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
);
|
|
103
|
+
} else if (isMounted) {
|
|
104
|
+
setReportData({});
|
|
105
|
+
setStatus(STATUS.RESOLVED);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
handleError: err => {
|
|
109
|
+
if (isMounted) {
|
|
110
|
+
setError(err);
|
|
111
|
+
setStatus(STATUS.ERROR);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
return () => {
|
|
118
|
+
isMounted = false;
|
|
119
|
+
};
|
|
120
|
+
}, [isExpanded, data.id, isLeappJob, reportData, dispatch]);
|
|
121
|
+
|
|
122
|
+
// eslint-disable-next-line camelcase
|
|
123
|
+
const entries = reportData?.preupgrade_report_entries || [];
|
|
124
|
+
const pagedEntries = entriesPage(entries, pagination);
|
|
125
|
+
|
|
126
|
+
const handleParamsChange = newParams => {
|
|
127
|
+
setPagination(prev => ({
|
|
128
|
+
...prev,
|
|
129
|
+
page: newParams.page || prev.page,
|
|
130
|
+
perPage: newParams.per_page || prev.perPage,
|
|
131
|
+
}));
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
if (!isLeappJob) return null;
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<ExpandableSection
|
|
138
|
+
className="leapp-report-section"
|
|
139
|
+
isExpanded={isExpanded}
|
|
140
|
+
onToggle={(_event, val) => setIsExpanded(val)}
|
|
141
|
+
toggleText={__('Leapp preupgrade report')}
|
|
142
|
+
>
|
|
143
|
+
<Table
|
|
144
|
+
ouiaId="leapp-report-table"
|
|
145
|
+
columns={columns}
|
|
146
|
+
isEmbedded
|
|
147
|
+
params={{
|
|
148
|
+
page: pagination.page,
|
|
149
|
+
perPage: pagination.perPage,
|
|
150
|
+
order: '',
|
|
151
|
+
}}
|
|
152
|
+
results={pagedEntries}
|
|
153
|
+
itemCount={entries.length}
|
|
154
|
+
url=""
|
|
155
|
+
isPending={status === STATUS.PENDING}
|
|
156
|
+
errorMessage={
|
|
157
|
+
status === STATUS.ERROR && error?.message ? error.message : null
|
|
158
|
+
}
|
|
159
|
+
showCheckboxes={false}
|
|
160
|
+
refreshData={() => {}}
|
|
161
|
+
isDeleteable={false}
|
|
162
|
+
emptyMessage={__('The preupgrade report shows no issues.')}
|
|
163
|
+
setParams={handleParamsChange}
|
|
164
|
+
/>
|
|
165
|
+
</ExpandableSection>
|
|
166
|
+
);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
PreupgradeReportsTable.propTypes = {
|
|
170
|
+
data: PropTypes.shape({
|
|
171
|
+
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
172
|
+
template_name: PropTypes.string,
|
|
173
|
+
}),
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
PreupgradeReportsTable.defaultProps = {
|
|
177
|
+
data: {},
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export default PreupgradeReportsTable;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
|
|
3
|
+
import PreupgradeReportsTable from './components/PreupgradeReportsTable';
|
|
4
|
+
|
|
5
|
+
addGlobalFill(
|
|
6
|
+
'job-invocation-additional-info',
|
|
7
|
+
'leapp-preupgrade-report-fill',
|
|
8
|
+
<PreupgradeReportsTable key="leapp-report-fill" />,
|
|
9
|
+
100
|
|
10
|
+
);
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_leapp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Foreman Leapp team
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2026-01-28 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: foreman_remote_execution
|
|
@@ -130,10 +130,6 @@ files:
|
|
|
130
130
|
- test/unit/actions/preupgrade_job_test.rb
|
|
131
131
|
- test/unit/helpers/job_helper_test.rb
|
|
132
132
|
- webpack/__mocks__/foremanReact/Root/Context/ForemanContext.js
|
|
133
|
-
- webpack/__mocks__/foremanReact/common/I18n.js
|
|
134
|
-
- webpack/__mocks__/foremanReact/components/Pagination.js
|
|
135
|
-
- webpack/__mocks__/foremanReact/components/common/EmptyState.js
|
|
136
|
-
- webpack/__mocks__/foremanReact/components/common/MessageBox.js
|
|
137
133
|
- webpack/components/PreupgradeReports/PreupgradeReports.js
|
|
138
134
|
- webpack/components/PreupgradeReports/PreupgradeReportsActions.js
|
|
139
135
|
- webpack/components/PreupgradeReports/PreupgradeReportsHelpers.js
|
|
@@ -164,7 +160,6 @@ files:
|
|
|
164
160
|
- webpack/components/PreupgradeReports/index.js
|
|
165
161
|
- webpack/components/PreupgradeReportsList/PreupgradeReportList.scss
|
|
166
162
|
- webpack/components/PreupgradeReportsList/__tests__/PreupgradeReportsList.test.js
|
|
167
|
-
- webpack/components/PreupgradeReportsList/__tests__/__snapshots__/PreupgradeReportsList.test.js.snap
|
|
168
163
|
- webpack/components/PreupgradeReportsList/components/EmptyInfoItem.js
|
|
169
164
|
- webpack/components/PreupgradeReportsList/components/InfoItem.js
|
|
170
165
|
- webpack/components/PreupgradeReportsList/components/InhibitorInfoItem.js
|
|
@@ -191,8 +186,11 @@ files:
|
|
|
191
186
|
- webpack/components/PreupgradeReportsList/components/images/i_severity-low.svg
|
|
192
187
|
- webpack/components/PreupgradeReportsList/components/images/i_severity-med.svg
|
|
193
188
|
- webpack/components/PreupgradeReportsList/index.js
|
|
189
|
+
- webpack/components/PreupgradeReportsTable/__tests__/PreupgradeReportsTable.test.js
|
|
190
|
+
- webpack/components/PreupgradeReportsTable/index.js
|
|
194
191
|
- webpack/consts.js
|
|
195
192
|
- webpack/csrf.js
|
|
193
|
+
- webpack/global_index.js
|
|
196
194
|
- webpack/index.js
|
|
197
195
|
- webpack/reducer.js
|
|
198
196
|
- webpack/testSetup.js
|
|
@@ -214,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
214
212
|
- !ruby/object:Gem::Version
|
|
215
213
|
version: '0'
|
|
216
214
|
requirements: []
|
|
217
|
-
rubygems_version:
|
|
215
|
+
rubygems_version: 4.0.3
|
|
218
216
|
specification_version: 4
|
|
219
217
|
summary: A Foreman plugin for Leapp utility.
|
|
220
218
|
test_files:
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const EmptyStatePattern = () => jest.fn();
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`PreupgradeReportsList should render 1`] = `
|
|
4
|
-
<ListView
|
|
5
|
-
className=""
|
|
6
|
-
id="preupgrade-report-entries-list-view"
|
|
7
|
-
>
|
|
8
|
-
<PreupgradeReportsListHeader
|
|
9
|
-
changeSort={[Function]}
|
|
10
|
-
sort={
|
|
11
|
-
Object {
|
|
12
|
-
"attribute": "",
|
|
13
|
-
"order": "asc",
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
toggleSelectAll={[Function]}
|
|
17
|
-
/>
|
|
18
|
-
<PreupgradeReportEntry
|
|
19
|
-
entry={
|
|
20
|
-
Object {
|
|
21
|
-
"severity": "Too severe to talk about",
|
|
22
|
-
"title": "Fix me!",
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
isEntrySelected={false}
|
|
26
|
-
key="0"
|
|
27
|
-
toggleSelected={[Function]}
|
|
28
|
-
/>
|
|
29
|
-
<PreupgradeReportEntry
|
|
30
|
-
entry={
|
|
31
|
-
Object {
|
|
32
|
-
"severity": "medium",
|
|
33
|
-
"title": "I am broken too",
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
isEntrySelected={false}
|
|
37
|
-
key="1"
|
|
38
|
-
toggleSelected={[Function]}
|
|
39
|
-
/>
|
|
40
|
-
<PreupgradeReportEntry
|
|
41
|
-
entry={
|
|
42
|
-
Object {
|
|
43
|
-
"severity": "high",
|
|
44
|
-
"title": "Octocat is not happy",
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
isEntrySelected={false}
|
|
48
|
-
key="2"
|
|
49
|
-
toggleSelected={[Function]}
|
|
50
|
-
/>
|
|
51
|
-
<PreupgradeReportEntry
|
|
52
|
-
entry={
|
|
53
|
-
Object {
|
|
54
|
-
"severity": "low",
|
|
55
|
-
"title": "Not enough credits",
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
isEntrySelected={false}
|
|
59
|
-
key="3"
|
|
60
|
-
toggleSelected={[Function]}
|
|
61
|
-
/>
|
|
62
|
-
<Pagination
|
|
63
|
-
itemCount={4}
|
|
64
|
-
onChange={[Function]}
|
|
65
|
-
viewType="list"
|
|
66
|
-
/>
|
|
67
|
-
</ListView>
|
|
68
|
-
`;
|
|
69
|
-
|
|
70
|
-
exports[`PreupgradeReportsList should render when working 1`] = `
|
|
71
|
-
<ListView
|
|
72
|
-
className=""
|
|
73
|
-
id="preupgrade-report-entries-list-view"
|
|
74
|
-
>
|
|
75
|
-
<PreupgradeReportsListHeader
|
|
76
|
-
changeSort={[Function]}
|
|
77
|
-
sort={
|
|
78
|
-
Object {
|
|
79
|
-
"attribute": "",
|
|
80
|
-
"order": "asc",
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
toggleSelectAll={[Function]}
|
|
84
|
-
/>
|
|
85
|
-
<PreupgradeReportEntry
|
|
86
|
-
entry={
|
|
87
|
-
Object {
|
|
88
|
-
"severity": "Too severe to talk about",
|
|
89
|
-
"title": "Fix me!",
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
isEntrySelected={false}
|
|
93
|
-
key="0"
|
|
94
|
-
toggleSelected={[Function]}
|
|
95
|
-
/>
|
|
96
|
-
<PreupgradeReportEntry
|
|
97
|
-
entry={
|
|
98
|
-
Object {
|
|
99
|
-
"severity": "medium",
|
|
100
|
-
"title": "I am broken too",
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
isEntrySelected={false}
|
|
104
|
-
key="1"
|
|
105
|
-
toggleSelected={[Function]}
|
|
106
|
-
/>
|
|
107
|
-
<PreupgradeReportEntry
|
|
108
|
-
entry={
|
|
109
|
-
Object {
|
|
110
|
-
"severity": "high",
|
|
111
|
-
"title": "Octocat is not happy",
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
isEntrySelected={false}
|
|
115
|
-
key="2"
|
|
116
|
-
toggleSelected={[Function]}
|
|
117
|
-
/>
|
|
118
|
-
<PreupgradeReportEntry
|
|
119
|
-
entry={
|
|
120
|
-
Object {
|
|
121
|
-
"severity": "low",
|
|
122
|
-
"title": "Not enough credits",
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
isEntrySelected={false}
|
|
126
|
-
key="3"
|
|
127
|
-
toggleSelected={[Function]}
|
|
128
|
-
/>
|
|
129
|
-
<Pagination
|
|
130
|
-
itemCount={4}
|
|
131
|
-
onChange={[Function]}
|
|
132
|
-
viewType="list"
|
|
133
|
-
/>
|
|
134
|
-
</ListView>
|
|
135
|
-
`;
|