Skip to content

Commit 0c1d50a

Browse files
committed
add custom layout for preupgrade entries index because of fixable_count
1 parent 9dfb3be commit 0c1d50a

6 files changed

Lines changed: 211 additions & 7 deletions

File tree

app/controllers/api/v2/preupgrade_report_entries_controller.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ class PreupgradeReportEntriesController < ::Api::V2::BaseController
99
skip_before_action :store_redirect_to_url, :reset_redirect_to_url, raise: false
1010
before_action :find_preupgrade_report, if: -> { params[:preupgrade_report_id].present? }
1111

12+
layout 'api/v2/layouts/preupgrade_report_entries_index', only: :index
13+
1214
api :GET, '/preupgrade_reports/:preupgrade_report_id/preupgrade_report_entries',
1315
N_('List entries for a specific preupgrade report')
1416
api :GET, '/preupgrade_report_entries', N_('List all preupgrade report entries')
@@ -19,6 +21,7 @@ def index
1921
@total = resource_scope.count
2022
@preupgrade_report_entries = resource_scope_for_index
2123
@subtotal = @preupgrade_report_entries.total_entries
24+
@fixable_count = fixable_scope.count
2225
end
2326

2427
api :GET, '/preupgrade_report_entries/auto_complete_search',
@@ -82,11 +85,14 @@ def resource_scope_for_index(options = {})
8285

8386
private
8487

85-
def filtered_remediation_entries
88+
def fixable_scope
8689
combined_search = [params[:search].presence, 'fix_type = command'].compact.join(' AND ')
87-
entries = resource_scope.search_for(combined_search)
88-
entries = entries.where.not(id: params[:excluded_ids]) if params[:excluded_ids].present?
89-
entries
90+
resource_scope.search_for(combined_search)
91+
end
92+
93+
def filtered_remediation_entries
94+
scope = fixable_scope
95+
params[:excluded_ids].present? ? scope.where.not(id: params[:excluded_ids]) : scope
9096
end
9197

9298
def target_host_ids(entries)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"total": <%= @total.to_json.html_safe %>,
3+
"subtotal": <%= @subtotal.to_json.html_safe %>,
4+
"fixable_count": <%= @fixable_count.to_json.html_safe %>,
5+
"page": <%= params[:page].to_json.html_safe %>,
6+
"per_page": <%= params[:per_page].to_json.html_safe %>,
7+
"search": <%= params[:search].to_json.html_safe %>,
8+
<% if params.has_key?(:include_permissions) %>
9+
<%= index_node_permissions_snippet.html_safe %>,
10+
<% end %>
11+
"sort": {
12+
"by": <%= params[:order].to_s.split.first.to_json.html_safe %>,
13+
"order": <%= (params[:order].to_s.match?(/desc$/i) ? 'DESC' : 'ASC').to_json.html_safe %>
14+
},
15+
"<%= "#{root_node_name}" %>": <%= yield %>
16+
}

test/functional/api/v2/preupgrade_report_entries_controller_test.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class PreupgradeReportEntriesControllerTest < ActionController::TestCase
2222
assert_equal @entry.id, body['results'].first['id']
2323
assert_equal 1, body['total']
2424
assert_equal 1, body['subtotal']
25+
assert body.key?('fixable_count'), "Expected 'fixable_count' field to be present"
2526
end
2627

2728
test ':index response includes expected entry fields' do
@@ -85,6 +86,43 @@ class PreupgradeReportEntriesControllerTest < ActionController::TestCase
8586
assert_equal other.id, body['results'].first['id']
8687
end
8788

89+
test ':index returns fixable_count for entries with fix_type = command' do
90+
FactoryBot.create(:preupgrade_report_entry, host: @host,
91+
preupgrade_report: @report,
92+
detail: { remediations: [{ type: 'command', context: ['yum', 'install', 'pkg'] }] })
93+
FactoryBot.create(:preupgrade_report_entry, host: @host,
94+
preupgrade_report: @report,
95+
detail: { remediations: [{ type: 'command', context: ['yum', 'remove', 'pkg'] }] })
96+
FactoryBot.create(:preupgrade_report_entry, host: @host,
97+
preupgrade_report: @report,
98+
detail: { remediations: [{ type: 'hint', context: 'Manual fix required' }] })
99+
100+
get :index, params: { preupgrade_report_id: @report.id }
101+
assert_response :success
102+
103+
body = JSON.parse(@response.body)
104+
assert_equal 4, body['total']
105+
assert_equal 3, body['fixable_count']
106+
end
107+
108+
test ':index fixable_count respects search filter' do
109+
FactoryBot.create(:preupgrade_report_entry, host: @host,
110+
preupgrade_report: @report,
111+
title: 'Fixable issue AAA',
112+
detail: { remediations: [{ type: 'command', context: ['fix', 'aaa'] }] })
113+
FactoryBot.create(:preupgrade_report_entry, host: @host,
114+
preupgrade_report: @report,
115+
title: 'Fixable issue BBB',
116+
detail: { remediations: [{ type: 'command', context: ['fix', 'bbb'] }] })
117+
118+
get :index, params: { preupgrade_report_id: @report.id, search: 'title ~ AAA' }
119+
assert_response :success
120+
121+
body = JSON.parse(@response.body)
122+
assert_equal 1, body['fixable_count']
123+
assert_equal 1, body['subtotal']
124+
end
125+
88126
test ':index supports order parameter' do
89127
FactoryBot.create(:preupgrade_report_entry, host: @host,
90128
preupgrade_report: @report, title: 'AAAA sorts first')

webpack/components/PreupgradeReportsTable/PreupgradeReportsTableHelpers.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const usePreupgradeTableState = (data, isExpanded) => {
1515
const [reportId, setReportId] = useState(null);
1616
const [rows, setRows] = useState([]);
1717
const [totalCount, setTotalCount] = useState(0);
18+
const [fixableCount, setFixableCount] = useState(0);
1819
const [searchValue, setSearchValue] = useState('');
1920
const [pagination, setPagination] = useState({
2021
page: 1,
@@ -98,6 +99,7 @@ export const usePreupgradeTableState = (data, isExpanded) => {
9899
const fetchedResults = payload.results;
99100
setRows(fetchedResults ? [].concat(fetchedResults) : []);
100101
setTotalCount(payload.subtotal ?? payload.total ?? 0);
102+
setFixableCount(payload.fixable_count ?? 0);
101103
setStatus(STATUS.RESOLVED);
102104
setExpandedRowIds(new Set());
103105
},
@@ -150,6 +152,7 @@ export const usePreupgradeTableState = (data, isExpanded) => {
150152
reportId,
151153
rows,
152154
totalCount,
155+
fixableCount,
153156
pagination,
154157
sortBy,
155158
searchValue,

webpack/components/PreupgradeReportsTable/__tests__/PreupgradeReportsTable.test.js

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const mockEntries = Array.from({ length: 12 }, (_, i) => ({
5656
flags: i === 0 ? ['inhibitor'] : [],
5757
detail: {
5858
remediations:
59-
i === 0
59+
i === 0 || i === 3 || i === 4
6060
? [{ type: 'command', context: ['echo', 'fix_command'] }]
6161
: i === 1
6262
? [{ type: 'hint', context: 'Do something manually' }]
@@ -76,10 +76,14 @@ describe('PreupgradeReportsTable', () => {
7676
handleSuccess({ results: [{ id: mockReportId }] });
7777
}
7878
if (key.includes('GET_LEAPP_REPORT_ENTRIES')) {
79+
const fixableCount = mockEntries.filter(
80+
e => e.detail?.remediations?.some(r => r.type === 'command')
81+
).length;
7982
handleSuccess({
8083
id: mockReportId,
8184
results: mockEntries,
8285
total: mockEntries.length,
86+
fixable_count: fixableCount,
8387
});
8488
}
8589
return { type: 'MOCK_API_SUCCESS' };
@@ -560,4 +564,140 @@ describe('PreupgradeReportsTable', () => {
560564
})
561565
);
562566
});
567+
568+
it('fetches and displays fixableCount from API response', async () => {
569+
const mockFixableCount = 3;
570+
APIActions.get.mockImplementation(({ key, handleSuccess }) => {
571+
return () => {
572+
if (key.includes('GET_LEAPP_REPORT_LIST')) {
573+
handleSuccess({ results: [{ id: mockReportId }] });
574+
}
575+
if (key.includes('GET_LEAPP_REPORT_ENTRIES')) {
576+
handleSuccess({
577+
id: mockReportId,
578+
results: mockEntries,
579+
total: mockEntries.length,
580+
subtotal: mockEntries.length,
581+
fixable_count: mockFixableCount,
582+
});
583+
}
584+
return { type: 'MOCK_API_SUCCESS' };
585+
};
586+
});
587+
588+
renderComponent();
589+
expandSection();
590+
await waitForTable();
591+
592+
const selectDropdown = screen.getByRole('button', { name: 'Select' });
593+
fireEvent.click(selectDropdown);
594+
595+
await waitFor(() => {
596+
const selectAllText = screen.getByText(/Select all \(3/i);
597+
expect(selectAllText).toBeInTheDocument();
598+
});
599+
});
600+
601+
it('displays correct fixableCount when search filters are applied', async () => {
602+
const mockFilteredFixableCount = 1;
603+
APIActions.get.mockImplementation(({ key, handleSuccess, params }) => {
604+
return () => {
605+
if (key.includes('GET_LEAPP_REPORT_LIST')) {
606+
handleSuccess({ results: [{ id: mockReportId }] });
607+
}
608+
if (key.includes('GET_LEAPP_REPORT_ENTRIES')) {
609+
if (params?.search) {
610+
handleSuccess({
611+
id: mockReportId,
612+
results: [mockEntries[0]], // Only one filtered result
613+
total: mockEntries.length,
614+
subtotal: 1,
615+
fixable_count: mockFilteredFixableCount,
616+
});
617+
} else {
618+
handleSuccess({
619+
id: mockReportId,
620+
results: mockEntries,
621+
total: mockEntries.length,
622+
subtotal: mockEntries.length,
623+
fixable_count: 3,
624+
});
625+
}
626+
}
627+
return { type: 'MOCK_API_SUCCESS' };
628+
};
629+
});
630+
631+
renderComponent();
632+
expandSection();
633+
await waitForTable();
634+
635+
const input = screen.getByTestId('search-input');
636+
fireEvent.change(input, { target: { value: 'title = "Report Entry 1"' } });
637+
fireEvent.keyDown(input, { key: 'Enter' });
638+
639+
await waitFor(() => screen.getByText('Report Entry 1', { selector: 'td' }));
640+
641+
const selectDropdown = screen.getByRole('button', { name: 'Select' });
642+
fireEvent.click(selectDropdown);
643+
644+
await waitFor(() => {
645+
const selectAllText = screen.getByText(/Select all \(1/i);
646+
expect(selectAllText).toBeInTheDocument();
647+
});
648+
});
649+
650+
it('shows 0 fixable items when all entries are non-fixable (hint-only)', async () => {
651+
APIActions.get.mockImplementation(({ key, handleSuccess }) => {
652+
return () => {
653+
if (key.includes('GET_LEAPP_REPORT_LIST')) {
654+
handleSuccess({ results: [{ id: mockReportId }] });
655+
}
656+
if (key.includes('GET_LEAPP_REPORT_ENTRIES')) {
657+
handleSuccess({
658+
id: mockReportId,
659+
results: [
660+
{
661+
id: 1,
662+
title: 'Hint-only Entry 1',
663+
hostname: 'test.com',
664+
host_id: 100,
665+
severity: 'low',
666+
summary: 'Summary',
667+
detail: {
668+
remediations: [{ type: 'hint', context: 'Manual fix required' }],
669+
},
670+
},
671+
{
672+
id: 2,
673+
title: 'Hint-only Entry 2',
674+
hostname: 'test.com',
675+
host_id: 101,
676+
severity: 'low',
677+
summary: 'Summary',
678+
detail: {
679+
remediations: [{ type: 'hint', context: 'Manual fix required' }],
680+
},
681+
},
682+
],
683+
total: 2,
684+
subtotal: 2,
685+
fixable_count: 0,
686+
});
687+
}
688+
return { type: 'MOCK_API_SUCCESS' };
689+
};
690+
});
691+
692+
renderComponent();
693+
expandSection();
694+
await waitFor(() => screen.getByText('Hint-only Entry 1', { selector: 'td' }));
695+
696+
const checkboxes = screen.getAllByRole('checkbox');
697+
checkboxes.forEach(checkbox => {
698+
expect(checkbox).toBeDisabled();
699+
});
700+
701+
expect(screen.getByRole('button', { name: 'Fix Selected' })).toBeDisabled();
702+
});
563703
});

webpack/components/PreupgradeReportsTable/index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ const PreupgradeReportsTable = ({ data = {} }) => {
9191
reportId,
9292
rows,
9393
totalCount,
94+
fixableCount,
9495
pagination,
9596
sortBy,
9697
searchValue,
@@ -178,7 +179,7 @@ const PreupgradeReportsTable = ({ data = {} }) => {
178179
metadata: {
179180
total: totalCount,
180181
page: pagination.page,
181-
selectable: totalCount,
182+
selectable: fixableCount,
182183
},
183184
initialSearchQuery: searchValue,
184185
});
@@ -372,7 +373,7 @@ const PreupgradeReportsTable = ({ data = {} }) => {
372373
selectNone={selectNone}
373374
selectedCount={selectedCount}
374375
pageRowCount={pagedFixableEntries.length}
375-
totalCount={totalCount}
376+
totalCount={fixableCount}
376377
areAllRowsOnPageSelected={areAllPageFixableSelected}
377378
areAllRowsSelected={areAllRowsSelected()}
378379
isDisabled={status === STATUS.PENDING || isSubmitting}

0 commit comments

Comments
 (0)