foreman_leapp 4.0.0 → 4.1.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/controllers/api/v2/preupgrade_report_entries_controller.rb +120 -0
- data/app/controllers/api/v2/preupgrade_reports_controller.rb +2 -2
- data/app/models/preupgrade_report_entry.rb +73 -1
- data/app/views/api/v2/preupgrade_report_entries/index.json.rabl +4 -0
- data/app/views/api/v2/preupgrade_reports/show.json.rabl +2 -5
- data/config/routes.rb +17 -3
- data/lib/foreman_leapp/version.rb +1 -1
- data/test/functional/api/v2/preupgrade_report_entries_controller_test.rb +258 -0
- data/test/functional/api/v2/preupgrade_reports_controller_test.rb +7 -5
- data/test/models/preupgrade_report_entry_test.rb +47 -0
- data/webpack/components/PreupgradeReports/components/NoReports.test.js +26 -12
- data/webpack/components/PreupgradeReportsTable/PreupgradeReportsTable.scss +9 -1
- data/webpack/components/PreupgradeReportsTable/PreupgradeReportsTableHelpers.js +164 -0
- data/webpack/components/PreupgradeReportsTable/__tests__/PreupgradeReportsTable.test.js +245 -38
- data/webpack/components/PreupgradeReportsTable/index.js +337 -238
- metadata +7 -3
- data/webpack/components/PreupgradeReports/components/__snapshots__/NoReports.test.js.snap +0 -27
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 51cdabcb8df392e354e77bbe82ab0e3fc4d2c62d993a129668b9367a78453eb7
|
|
4
|
+
data.tar.gz: ca1c8c041d52581f987d5a3d1a964ff3aeea89290000e0faf55488bd6b4f4a82
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eadf4d4d462b5fa9a9c838946ccca5a7a764e3bcb97f0ee457682f5d43c24a239f9900986d0c088dacb1c51b9d62e1d2800e1b7c2147debf86f465e7fe82bf6c
|
|
7
|
+
data.tar.gz: 8abd151cbd535508601fda873b41b74548055420d8d1cad40454d3f47cfa0761de40bca70ca91474993a8583be174fac6395e66ef69a5c334f93bb131e31f69a
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Api
|
|
4
|
+
module V2
|
|
5
|
+
class PreupgradeReportEntriesController < ::Api::V2::BaseController
|
|
6
|
+
include ApiAuthorizer
|
|
7
|
+
include Foreman::Controller::AutoCompleteSearch
|
|
8
|
+
|
|
9
|
+
skip_before_action :store_redirect_to_url, :reset_redirect_to_url, raise: false
|
|
10
|
+
before_action :find_preupgrade_report, if: -> { params[:preupgrade_report_id].present? }
|
|
11
|
+
|
|
12
|
+
api :GET, '/preupgrade_reports/:preupgrade_report_id/preupgrade_report_entries',
|
|
13
|
+
N_('List entries for a specific preupgrade report')
|
|
14
|
+
api :GET, '/preupgrade_report_entries', N_('List all preupgrade report entries')
|
|
15
|
+
param :preupgrade_report_id, :identifier, required: false,
|
|
16
|
+
desc: N_('ID of the preupgrade report')
|
|
17
|
+
param_group :search_and_pagination, ::Api::V2::BaseController
|
|
18
|
+
def index
|
|
19
|
+
@total = resource_scope.count
|
|
20
|
+
@preupgrade_report_entries = resource_scope_for_index
|
|
21
|
+
@subtotal = @preupgrade_report_entries.total_entries
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
api :GET, '/preupgrade_report_entries/auto_complete_search',
|
|
25
|
+
N_('Search autocomplete for preupgrade report entries')
|
|
26
|
+
param :search, String, required: false, desc: N_('Search string')
|
|
27
|
+
param :preupgrade_report_id, :identifier, required: false, desc: N_('ID of the preupgrade report')
|
|
28
|
+
|
|
29
|
+
api :POST, '/preupgrade_reports/:preupgrade_report_id/preupgrade_report_entries/bulk_remediate',
|
|
30
|
+
N_('Trigger a remediation job for selected preupgrade report entries')
|
|
31
|
+
param :search, String, required: false, desc: N_('Search string')
|
|
32
|
+
param :excluded_ids, Array, required: false, desc: N_('Array of excluded entry IDs')
|
|
33
|
+
def bulk_remediate
|
|
34
|
+
entries = filtered_remediation_entries
|
|
35
|
+
remediation_ids = entries.pluck(:id)
|
|
36
|
+
|
|
37
|
+
if remediation_ids.empty?
|
|
38
|
+
return render json: { error: _('No fixable entries found matching the selection.') },
|
|
39
|
+
status: :unprocessable_entity
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
composer = JobInvocationComposer.for_feature(
|
|
43
|
+
'leapp_remediation_plan',
|
|
44
|
+
target_host_ids(entries),
|
|
45
|
+
{ 'remediation_ids' => remediation_ids.join(',') }
|
|
46
|
+
)
|
|
47
|
+
composer.trigger!
|
|
48
|
+
job_invocation = composer.job_invocation
|
|
49
|
+
|
|
50
|
+
render json: { id: job_invocation.id }
|
|
51
|
+
rescue StandardError => e
|
|
52
|
+
Foreman::Logging.exception('Failed to trigger bulk remediation job', e)
|
|
53
|
+
render json: { error: _('An unexpected error occurred while creating the remediation job.') },
|
|
54
|
+
status: :internal_server_error
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
protected
|
|
58
|
+
|
|
59
|
+
def model_of_controller
|
|
60
|
+
PreupgradeReportEntry
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def resource_scope(_options = {})
|
|
64
|
+
scope = if @preupgrade_report
|
|
65
|
+
@preupgrade_report.preupgrade_report_entries
|
|
66
|
+
else
|
|
67
|
+
PreupgradeReportEntry
|
|
68
|
+
end
|
|
69
|
+
scope.joins(:host).merge(Host.authorized(:view_hosts, Host))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def resource_scope_for_index(options = {})
|
|
73
|
+
order_str = params[:order].to_s
|
|
74
|
+
return super unless order_str.match?(/^(severity|risk_factor)\b/i)
|
|
75
|
+
direction = order_str.match?(/desc$/i) ? 'DESC' : 'ASC'
|
|
76
|
+
|
|
77
|
+
resource_scope(options)
|
|
78
|
+
.search_for(params[:search].to_s)
|
|
79
|
+
.order_by_severity(direction)
|
|
80
|
+
.paginate(paginate_options)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def filtered_remediation_entries
|
|
86
|
+
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
|
+
end
|
|
91
|
+
|
|
92
|
+
def target_host_ids(entries)
|
|
93
|
+
host_ids = entries.pluck(:host_id).uniq.compact
|
|
94
|
+
host_ids = [@preupgrade_report.host_id].compact if host_ids.empty? && @preupgrade_report
|
|
95
|
+
host_ids
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def find_preupgrade_report
|
|
99
|
+
@preupgrade_report = PreupgradeReport
|
|
100
|
+
.joins(:host)
|
|
101
|
+
.merge(Host.authorized(:view_hosts, Host))
|
|
102
|
+
.find(params[:preupgrade_report_id])
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def path_to_authenticate
|
|
106
|
+
auth_action = case action_name
|
|
107
|
+
when 'auto_complete_search'
|
|
108
|
+
'index'
|
|
109
|
+
when 'bulk_remediate'
|
|
110
|
+
'create'
|
|
111
|
+
else
|
|
112
|
+
action_name
|
|
113
|
+
end
|
|
114
|
+
path_params = params.slice(:id, :user_id)
|
|
115
|
+
.merge(action: auth_action, controller: 'api/v2/job_invocations')
|
|
116
|
+
Foreman::AccessControl.normalize_path_hash(path_params)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -30,8 +30,8 @@ module Api
|
|
|
30
30
|
# By overriding path_to_authenticate we can require REX's permission view_job_invocations
|
|
31
31
|
def path_to_authenticate
|
|
32
32
|
params['action'] = 'show' if params['action'] == 'job_invocation'
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
path_params = params.slice(:action, :id, :user_id).merge(controller: 'api/v2/job_invocations')
|
|
34
|
+
Foreman::AccessControl.normalize_path_hash(path_params)
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
end
|
|
@@ -8,9 +8,81 @@ class PreupgradeReportEntry < ApplicationRecord
|
|
|
8
8
|
serialize :flags, Array
|
|
9
9
|
serialize :detail, JSON
|
|
10
10
|
|
|
11
|
-
validates :preupgrade_report, :host, :hostname, :title, :actor,
|
|
11
|
+
validates :preupgrade_report, :host, :hostname, :title, :actor,
|
|
12
|
+
:audience, :severity, :leapp_run_id, presence: true
|
|
13
|
+
|
|
14
|
+
scoped_search on: :title, complete_value: true
|
|
15
|
+
scoped_search on: :hostname, complete_value: true
|
|
16
|
+
scoped_search on: :summary
|
|
17
|
+
|
|
18
|
+
scoped_search on: :severity,
|
|
19
|
+
rename: :risk_factor,
|
|
20
|
+
complete_value: { high: 'high', medium: 'medium', low: 'low', info: 'info' }
|
|
21
|
+
|
|
22
|
+
scoped_search on: :flags,
|
|
23
|
+
rename: :inhibitor,
|
|
24
|
+
only_explicit: true,
|
|
25
|
+
operators: ['=', '!='],
|
|
26
|
+
complete_value: { yes: 'yes', no: 'no' },
|
|
27
|
+
ext_method: :search_yes_no_fields
|
|
28
|
+
|
|
29
|
+
scoped_search on: :detail,
|
|
30
|
+
rename: :has_remediation,
|
|
31
|
+
only_explicit: true,
|
|
32
|
+
operators: ['=', '!='],
|
|
33
|
+
complete_value: { yes: 'yes', no: 'no' },
|
|
34
|
+
ext_method: :search_yes_no_fields
|
|
35
|
+
|
|
36
|
+
scoped_search on: :detail,
|
|
37
|
+
rename: :fix_type,
|
|
38
|
+
only_explicit: true,
|
|
39
|
+
operators: ['=', '!='],
|
|
40
|
+
complete_value: { hint: 'hint', command: 'command' },
|
|
41
|
+
ext_method: :search_fix_type
|
|
42
|
+
|
|
43
|
+
# info(1) < low(2) < medium(3) < high(4)
|
|
44
|
+
scope :order_by_severity, lambda { |direction = 'ASC'|
|
|
45
|
+
dir = direction.to_s.casecmp('DESC').zero? ? 'DESC' : 'ASC'
|
|
46
|
+
order(Arel.sql(
|
|
47
|
+
"CASE preupgrade_report_entries.severity " \
|
|
48
|
+
"WHEN 'high' THEN 4 WHEN 'medium' THEN 3 " \
|
|
49
|
+
"WHEN 'low' THEN 2 WHEN 'info' THEN 1 ELSE 0 END #{dir}"
|
|
50
|
+
))
|
|
51
|
+
}
|
|
12
52
|
|
|
13
53
|
def self.remediation_details(remediation_ids, host)
|
|
14
54
|
where(id: remediation_ids, host: host).where.not(detail: nil).pluck(:detail)
|
|
15
55
|
end
|
|
56
|
+
|
|
57
|
+
def self.search_yes_no_fields(key, operator, value)
|
|
58
|
+
column, term = case key.to_sym
|
|
59
|
+
when :inhibitor then %w[flags inhibitor]
|
|
60
|
+
when :has_remediation then %w[detail remediations]
|
|
61
|
+
else raise Foreman::Exception,
|
|
62
|
+
N_("Unknown search key '%s' for search_yes_no_fields") % key
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if (operator == '=' && value == 'yes') || (operator == '!=' && value == 'no')
|
|
66
|
+
{ conditions: "preupgrade_report_entries.#{column} LIKE '%%#{term}%%'" }
|
|
67
|
+
else
|
|
68
|
+
{ conditions: "(preupgrade_report_entries.#{column} NOT LIKE '%%#{term}%%' OR " \
|
|
69
|
+
"preupgrade_report_entries.#{column} IS NULL)" }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.search_fix_type(_key, operator, value)
|
|
74
|
+
unless %w[hint command].include?(value.to_s.downcase)
|
|
75
|
+
raise Foreman::Exception, N_("Unknown fix_type value '%s' for search_fix_type") % value
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
safe_value = sanitize_sql_like(value.to_s.downcase)
|
|
79
|
+
like_op = (operator == '=') ? 'LIKE' : 'NOT LIKE'
|
|
80
|
+
|
|
81
|
+
conditions = "(preupgrade_report_entries.detail #{like_op} " \
|
|
82
|
+
"'%%\"type\":\"#{safe_value}\"%%' OR " \
|
|
83
|
+
"preupgrade_report_entries.detail #{like_op} " \
|
|
84
|
+
"'%%\"type\": \"#{safe_value}\"%%')"
|
|
85
|
+
conditions = "(#{conditions} OR preupgrade_report_entries.detail IS NULL)" if operator == '!='
|
|
86
|
+
{ conditions: conditions }
|
|
87
|
+
end
|
|
16
88
|
end
|
data/config/routes.rb
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
Rails.application.routes.draw do
|
|
4
|
-
resources :preupgrade_reports, :
|
|
4
|
+
resources :preupgrade_reports, only: %i[index]
|
|
5
5
|
|
|
6
6
|
namespace :api, defaults: { format: 'json' } do
|
|
7
7
|
scope '(:apiv)', module: :v2, defaults: { apiv: 'v2' }, apiv: /v2/,
|
|
8
|
-
constraints: ApiConstraints.new(
|
|
9
|
-
resources :
|
|
8
|
+
constraints: ApiConstraints.new(version: 2, default: true) do
|
|
9
|
+
resources :preupgrade_report_entries, only: %i[index] do
|
|
10
|
+
collection do
|
|
11
|
+
get :auto_complete_search
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
resources :preupgrade_reports, only: %i[index show] do
|
|
16
|
+
resources :preupgrade_report_entries, only: %i[index] do
|
|
17
|
+
collection do
|
|
18
|
+
get :auto_complete_search
|
|
19
|
+
post :bulk_remediate
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
10
24
|
get 'job_invocations/:id/preupgrade_reports', to: 'preupgrade_reports#job_invocation'
|
|
11
25
|
end
|
|
12
26
|
end
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_plugin_helper'
|
|
4
|
+
|
|
5
|
+
module Api
|
|
6
|
+
module V2
|
|
7
|
+
# rubocop:disable Metrics/ClassLength
|
|
8
|
+
class PreupgradeReportEntriesControllerTest < ActionController::TestCase
|
|
9
|
+
setup do
|
|
10
|
+
@host = FactoryBot.create(:host)
|
|
11
|
+
@job_invocation = FactoryBot.create(:job_invocation)
|
|
12
|
+
@report = FactoryBot.create(:preupgrade_report, host: @host, job_invocation: @job_invocation)
|
|
13
|
+
@entry = FactoryBot.create(:preupgrade_report_entry, host: @host, preupgrade_report: @report)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
test 'should get :index' do
|
|
17
|
+
get :index, params: { preupgrade_report_id: @report.id }
|
|
18
|
+
assert_response :success
|
|
19
|
+
|
|
20
|
+
body = JSON.parse(@response.body)
|
|
21
|
+
assert_not_empty body['results']
|
|
22
|
+
assert_equal @entry.id, body['results'].first['id']
|
|
23
|
+
assert_equal 1, body['total']
|
|
24
|
+
assert_equal 1, body['subtotal']
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
test ':index response includes expected entry fields' do
|
|
28
|
+
get :index, params: { preupgrade_report_id: @report.id }
|
|
29
|
+
assert_response :success
|
|
30
|
+
|
|
31
|
+
entry_json = JSON.parse(@response.body)['results'].first
|
|
32
|
+
%w[id title hostname severity host_id preupgrade_report_id flags detail].each do |field|
|
|
33
|
+
assert entry_json.key?(field), "Expected field '#{field}' to be present"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
test ':index returns 404 for non-existent preupgrade_report_id' do
|
|
38
|
+
get :index, params: { preupgrade_report_id: 0 }
|
|
39
|
+
assert_response :not_found
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
test ':index does not return entries belonging to a different report' do
|
|
43
|
+
other_report = FactoryBot.create(:preupgrade_report, host: @host,
|
|
44
|
+
job_invocation: @job_invocation)
|
|
45
|
+
other_entry = FactoryBot.create(:preupgrade_report_entry, host: @host,
|
|
46
|
+
preupgrade_report: other_report)
|
|
47
|
+
|
|
48
|
+
get :index, params: { preupgrade_report_id: @report.id }
|
|
49
|
+
assert_response :success
|
|
50
|
+
|
|
51
|
+
ids = JSON.parse(@response.body)['results'].map { |e| e['id'] }
|
|
52
|
+
assert_includes ids, @entry.id
|
|
53
|
+
assert_not_includes ids, other_entry.id
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
test ':index supports pagination' do
|
|
57
|
+
FactoryBot.create_list(:preupgrade_report_entry, 4, host: @host, preupgrade_report: @report)
|
|
58
|
+
|
|
59
|
+
get :index, params: { preupgrade_report_id: @report.id, per_page: 2, page: 1 }
|
|
60
|
+
assert_response :success
|
|
61
|
+
|
|
62
|
+
body = JSON.parse(@response.body)
|
|
63
|
+
assert_equal 2, body['results'].size
|
|
64
|
+
assert_equal 5, body['total']
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
test ':index second page returns correct entries' do
|
|
68
|
+
FactoryBot.create_list(:preupgrade_report_entry, 4, host: @host, preupgrade_report: @report)
|
|
69
|
+
|
|
70
|
+
get :index, params: { preupgrade_report_id: @report.id, per_page: 2, page: 2 }
|
|
71
|
+
assert_response :success
|
|
72
|
+
assert_equal 2, JSON.parse(@response.body)['results'].size
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
test ':index supports search by title' do
|
|
76
|
+
other = FactoryBot.create(:preupgrade_report_entry, host: @host, preupgrade_report: @report,
|
|
77
|
+
title: 'Unique xqz title for search test')
|
|
78
|
+
|
|
79
|
+
get :index, params: { preupgrade_report_id: @report.id,
|
|
80
|
+
search: "title = \"#{other.title}\"" }
|
|
81
|
+
assert_response :success
|
|
82
|
+
|
|
83
|
+
body = JSON.parse(@response.body)
|
|
84
|
+
assert_equal 1, body['results'].size
|
|
85
|
+
assert_equal other.id, body['results'].first['id']
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
test ':index supports order parameter' do
|
|
89
|
+
FactoryBot.create(:preupgrade_report_entry, host: @host,
|
|
90
|
+
preupgrade_report: @report, title: 'AAAA sorts first')
|
|
91
|
+
FactoryBot.create(:preupgrade_report_entry, host: @host,
|
|
92
|
+
preupgrade_report: @report, title: 'ZZZZ sorts last')
|
|
93
|
+
|
|
94
|
+
get :index, params: { preupgrade_report_id: @report.id, order: 'title ASC' }
|
|
95
|
+
assert_response :success
|
|
96
|
+
|
|
97
|
+
titles = JSON.parse(@response.body)['results'].map { |e| e['title'] }
|
|
98
|
+
assert titles.index('AAAA sorts first') < titles.index('ZZZZ sorts last')
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
test ':index supports semantic risk_factor ordering' do
|
|
102
|
+
FactoryBot.create(:preupgrade_report_entry, host: @host,
|
|
103
|
+
preupgrade_report: @report, severity: 'high')
|
|
104
|
+
FactoryBot.create(:preupgrade_report_entry, host: @host,
|
|
105
|
+
preupgrade_report: @report, severity: 'info')
|
|
106
|
+
|
|
107
|
+
get :index, params: { preupgrade_report_id: @report.id, order: 'risk_factor ASC' }
|
|
108
|
+
assert_response :success
|
|
109
|
+
|
|
110
|
+
severities = JSON.parse(@response.body)['results'].map { |e| e['severity'] }
|
|
111
|
+
assert severities.index('info') < severities.index('high'),
|
|
112
|
+
"Expected 'info' to sort before 'high' in semantic ASC order"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
test 'should get :auto_complete_search' do
|
|
116
|
+
get :auto_complete_search, params: { search: 'title' }
|
|
117
|
+
assert_response :success
|
|
118
|
+
assert_kind_of Array, JSON.parse(@response.body)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
test ':auto_complete_search returns non-blank string suggestions' do
|
|
122
|
+
get :auto_complete_search, params: { search: '' }
|
|
123
|
+
assert_response :success
|
|
124
|
+
results = JSON.parse(@response.body)
|
|
125
|
+
assert_kind_of Array, results
|
|
126
|
+
assert results.none?(&:blank?), 'Autocomplete results should not contain blank values'
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
test ':auto_complete_search scopes suggestions to report when preupgrade_report_id given' do
|
|
130
|
+
get :auto_complete_search,
|
|
131
|
+
params: { search: '', preupgrade_report_id: @report.id }
|
|
132
|
+
assert_response :success
|
|
133
|
+
assert_kind_of Array, JSON.parse(@response.body)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
context 'with permissions' do
|
|
137
|
+
setup do
|
|
138
|
+
@user = FactoryBot.create(:user, admin: false)
|
|
139
|
+
setup_user('view', 'job_invocations', nil, @user)
|
|
140
|
+
setup_user('view', 'hosts', nil, @user)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
test 'should get :index' do
|
|
144
|
+
get :index, params: { preupgrade_report_id: @report.id }, session: set_session_user(@user)
|
|
145
|
+
assert_response :success
|
|
146
|
+
assert_not_empty JSON.parse(@response.body)['results']
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
test 'should get :auto_complete_search' do
|
|
150
|
+
get :auto_complete_search, params: { search: '' }, session: set_session_user(@user)
|
|
151
|
+
assert_response :success
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
context 'without :view_job_invocations' do
|
|
156
|
+
setup do
|
|
157
|
+
@user = FactoryBot.create(:user, admin: false)
|
|
158
|
+
setup_user('view', 'hosts', nil, @user)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
test 'should not get :index' do
|
|
162
|
+
get :index, params: { preupgrade_report_id: @report.id }, session: set_session_user(@user)
|
|
163
|
+
assert_response :forbidden
|
|
164
|
+
assert_includes JSON.parse(@response.body)['error']['missing_permissions'],
|
|
165
|
+
'view_job_invocations'
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
test 'should not get :auto_complete_search' do
|
|
169
|
+
get :auto_complete_search, params: { search: '' }, session: set_session_user(@user)
|
|
170
|
+
assert_response :forbidden
|
|
171
|
+
assert_includes JSON.parse(@response.body)['error']['missing_permissions'],
|
|
172
|
+
'view_job_invocations'
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
context 'without :view_hosts' do
|
|
177
|
+
setup do
|
|
178
|
+
@user = FactoryBot.create(:user, admin: false)
|
|
179
|
+
setup_user('view', 'job_invocations', nil, @user)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
test 'should not get :index' do
|
|
183
|
+
get :index, params: { preupgrade_report_id: @report.id }, session: set_session_user(@user)
|
|
184
|
+
assert_response :forbidden
|
|
185
|
+
assert_includes JSON.parse(@response.body)['error']['missing_permissions'], 'view_hosts'
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
test 'should not get :auto_complete_search' do
|
|
189
|
+
get :auto_complete_search, params: { search: '' }, session: set_session_user(@user)
|
|
190
|
+
assert_response :forbidden
|
|
191
|
+
assert_includes JSON.parse(@response.body)['error']['missing_permissions'], 'view_hosts'
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
context 'POST bulk_remediate' do
|
|
196
|
+
setup do
|
|
197
|
+
@host = FactoryBot.create(:host)
|
|
198
|
+
@report = FactoryBot.create(:preupgrade_report, host: @host)
|
|
199
|
+
|
|
200
|
+
# Fixable entries
|
|
201
|
+
fixable_detail = { 'remediations' => [{ 'type' => 'command', 'context' => ['yum', 'update'] }] }
|
|
202
|
+
@entry1 = FactoryBot.create(:preupgrade_report_entry,
|
|
203
|
+
preupgrade_report: @report, host: @host, detail: fixable_detail)
|
|
204
|
+
@entry2 = FactoryBot.create(:preupgrade_report_entry,
|
|
205
|
+
preupgrade_report: @report, host: @host, detail: fixable_detail)
|
|
206
|
+
@entry3 = FactoryBot.create(:preupgrade_report_entry,
|
|
207
|
+
preupgrade_report: @report, host: @host, detail: fixable_detail)
|
|
208
|
+
|
|
209
|
+
@unfixable = FactoryBot.create(:preupgrade_report_entry, preupgrade_report: @report, host: @host, detail: {})
|
|
210
|
+
@composer = mock('composer')
|
|
211
|
+
@dummy_job = FactoryBot.create(:job_invocation)
|
|
212
|
+
@composer.stubs(:trigger!)
|
|
213
|
+
@composer.stubs(:job_invocation).returns(@dummy_job)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
test 'triggers job invocation for all fixable entries' do
|
|
217
|
+
expected_ids = [@entry1.id, @entry2.id, @entry3.id].join(',')
|
|
218
|
+
|
|
219
|
+
JobInvocationComposer.expects(:for_feature)
|
|
220
|
+
.with('leapp_remediation_plan', [@host.id], { 'remediation_ids' => expected_ids })
|
|
221
|
+
.returns(@composer)
|
|
222
|
+
|
|
223
|
+
post :bulk_remediate, params: { preupgrade_report_id: @report.id }
|
|
224
|
+
assert_response :success
|
|
225
|
+
json = ActiveSupport::JSON.decode(response.body)
|
|
226
|
+
assert_equal @dummy_job.id, json['id']
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
test 'respects the excluded_ids parameter' do
|
|
230
|
+
expected_ids = [@entry1.id, @entry3.id].join(',')
|
|
231
|
+
|
|
232
|
+
JobInvocationComposer.expects(:for_feature)
|
|
233
|
+
.with('leapp_remediation_plan', [@host.id], { 'remediation_ids' => expected_ids })
|
|
234
|
+
.returns(@composer)
|
|
235
|
+
|
|
236
|
+
post :bulk_remediate, params: {
|
|
237
|
+
preupgrade_report_id: @report.id,
|
|
238
|
+
excluded_ids: [@entry2.id],
|
|
239
|
+
}
|
|
240
|
+
assert_response :success
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
test 'returns error if no fixable entries match' do
|
|
244
|
+
JobInvocationComposer.expects(:for_feature).never
|
|
245
|
+
|
|
246
|
+
post :bulk_remediate, params: {
|
|
247
|
+
preupgrade_report_id: @report.id,
|
|
248
|
+
excluded_ids: [@entry1.id, @entry2.id, @entry3.id],
|
|
249
|
+
}
|
|
250
|
+
assert_response :unprocessable_entity
|
|
251
|
+
json = ActiveSupport::JSON.decode(response.body)
|
|
252
|
+
assert_equal 'No fixable entries found matching the selection.', json['error']
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
# rubocop:enable Metrics/ClassLength
|
|
257
|
+
end
|
|
258
|
+
end
|
|
@@ -22,9 +22,9 @@ module Api
|
|
|
22
22
|
get :show, params: { id: @report.id }
|
|
23
23
|
assert_response :success
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
assert_equal
|
|
27
|
-
|
|
25
|
+
body = JSON.parse(@response.body)
|
|
26
|
+
assert_equal @report.id, body['id']
|
|
27
|
+
assert_nil body['results']
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
test 'should get :job_invocation' do
|
|
@@ -49,7 +49,9 @@ module Api
|
|
|
49
49
|
test 'should get :show' do
|
|
50
50
|
get :show, params: { id: @report.id }, session: set_session_user(@user)
|
|
51
51
|
assert_response :success
|
|
52
|
-
|
|
52
|
+
json = ActiveSupport::JSON.decode(@response.body)
|
|
53
|
+
assert_equal @report.id, json['id']
|
|
54
|
+
assert_nil json['results']
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
test 'should get :job_invocation' do
|
|
@@ -96,7 +98,7 @@ module Api
|
|
|
96
98
|
assert_includes JSON.parse(@response.body)['error']['missing_permissions'], 'view_hosts'
|
|
97
99
|
end
|
|
98
100
|
|
|
99
|
-
test 'should not get :
|
|
101
|
+
test 'should not get :show' do
|
|
100
102
|
get :show, params: { id: @report.id }, session: set_session_user(@user)
|
|
101
103
|
assert_response :forbidden
|
|
102
104
|
assert_includes JSON.parse(@response.body)['error']['missing_permissions'], 'view_hosts'
|
|
@@ -33,4 +33,51 @@ class PreupgradeReportEntryTest < ActiveSupport::TestCase
|
|
|
33
33
|
assert_equal details.size, 0
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
|
+
|
|
37
|
+
describe 'scoped_search' do
|
|
38
|
+
it 'filters correctly by has_remediation and inhibitor' do
|
|
39
|
+
host = FactoryBot.create(:host)
|
|
40
|
+
report = FactoryBot.create(:preupgrade_report)
|
|
41
|
+
|
|
42
|
+
entry1 = FactoryBot.create(:preupgrade_report_entry, preupgrade_report: report, host: host,
|
|
43
|
+
flags: ['inhibitor'],
|
|
44
|
+
detail: { 'remediations' => [{ 'type' => 'hint' }] })
|
|
45
|
+
entry2 = FactoryBot.create(:preupgrade_report_entry, preupgrade_report: report, host: host,
|
|
46
|
+
flags: [],
|
|
47
|
+
detail: nil)
|
|
48
|
+
|
|
49
|
+
assert_includes PreupgradeReportEntry.search_for('inhibitor = yes'), entry1
|
|
50
|
+
assert_not_includes PreupgradeReportEntry.search_for('inhibitor = yes'), entry2
|
|
51
|
+
assert_includes PreupgradeReportEntry.search_for('has_remediation = no'), entry2
|
|
52
|
+
assert_not_includes PreupgradeReportEntry.search_for('has_remediation = no'), entry1
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'filters correctly by risk_factor (renamed from severity)' do
|
|
56
|
+
host = FactoryBot.create(:host)
|
|
57
|
+
report = FactoryBot.create(:preupgrade_report)
|
|
58
|
+
|
|
59
|
+
high_entry = FactoryBot.create(:preupgrade_report_entry, preupgrade_report: report,
|
|
60
|
+
host: host, severity: 'high')
|
|
61
|
+
info_entry = FactoryBot.create(:preupgrade_report_entry, preupgrade_report: report,
|
|
62
|
+
host: host, severity: 'info')
|
|
63
|
+
|
|
64
|
+
assert_includes PreupgradeReportEntry.search_for('risk_factor = high'), high_entry
|
|
65
|
+
assert_not_includes PreupgradeReportEntry.search_for('risk_factor = high'), info_entry
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'orders semantically via order_by_severity scope' do
|
|
69
|
+
host = FactoryBot.create(:host)
|
|
70
|
+
report = FactoryBot.create(:preupgrade_report)
|
|
71
|
+
FactoryBot.create(:preupgrade_report_entry, preupgrade_report: report, host: host, severity: 'high')
|
|
72
|
+
FactoryBot.create(:preupgrade_report_entry, preupgrade_report: report, host: host, severity: 'info')
|
|
73
|
+
FactoryBot.create(:preupgrade_report_entry, preupgrade_report: report, host: host, severity: 'medium')
|
|
74
|
+
FactoryBot.create(:preupgrade_report_entry, preupgrade_report: report, host: host, severity: 'low')
|
|
75
|
+
|
|
76
|
+
asc = PreupgradeReportEntry.order_by_severity('ASC').map(&:severity)
|
|
77
|
+
assert_equal %w[info low medium high], asc
|
|
78
|
+
|
|
79
|
+
desc = PreupgradeReportEntry.order_by_severity('DESC').map(&:severity)
|
|
80
|
+
assert_equal %w[high medium low info], desc
|
|
81
|
+
end
|
|
82
|
+
end
|
|
36
83
|
end
|
|
@@ -1,15 +1,29 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
2
4
|
|
|
3
5
|
import NoReports from './NoReports';
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
'should render when reports expected'
|
|
7
|
-
reportsExpected
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
describe('NoReports', () => {
|
|
8
|
+
it('should render when reports expected', () => {
|
|
9
|
+
render(<NoReports reportsExpected />);
|
|
10
|
+
|
|
11
|
+
expect(screen.getByText('No Preupgrade Report Available')).toBeInTheDocument();
|
|
12
|
+
expect(
|
|
13
|
+
screen.getByText(
|
|
14
|
+
'The preupgrade report could not be generated, check the job details for the reason'
|
|
15
|
+
)
|
|
16
|
+
).toBeInTheDocument();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should render when reports not expected', () => {
|
|
20
|
+
render(<NoReports reportsExpected={false} />);
|
|
21
|
+
|
|
22
|
+
expect(screen.getByText('No Preupgrade Report Available')).toBeInTheDocument();
|
|
23
|
+
expect(
|
|
24
|
+
screen.getByText(
|
|
25
|
+
'The preupgrade report will be available after the job finishes'
|
|
26
|
+
)
|
|
27
|
+
).toBeInTheDocument();
|
|
28
|
+
});
|
|
29
|
+
});
|