foreman-tasks 0.15.1 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.babelrc +9 -1
  3. data/.eslintrc +6 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +6 -0
  6. data/.storybook/webpack.config.js +51 -54
  7. data/app/controllers/foreman_tasks/api/tasks_controller.rb +0 -2
  8. data/app/controllers/foreman_tasks/tasks_controller.rb +6 -1
  9. data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +8 -0
  10. data/app/lib/actions/base.rb +7 -0
  11. data/app/lib/actions/helpers/lifecycle_logging.rb +21 -0
  12. data/app/lib/actions/proxy_action.rb +4 -2
  13. data/app/models/foreman_tasks/task.rb +23 -0
  14. data/app/models/foreman_tasks/task/summarizer.rb +96 -6
  15. data/app/models/foreman_tasks/triggering.rb +2 -2
  16. data/app/models/setting/foreman_tasks.rb +8 -1
  17. data/app/services/foreman_tasks/dashboard_table_filter.rb +47 -0
  18. data/app/services/foreman_tasks/troubleshooting_help_generator.rb +92 -0
  19. data/app/services/ui_notifications/tasks.rb +20 -0
  20. data/app/services/ui_notifications/tasks/task_paused_admin.rb +43 -0
  21. data/app/services/ui_notifications/tasks/task_paused_owner.rb +30 -0
  22. data/app/views/foreman_tasks/tasks/_details.html.erb +5 -3
  23. data/app/views/foreman_tasks/tasks/index.html.erb +13 -0
  24. data/config/routes.rb +1 -0
  25. data/db/migrate/20190318153925_add_task_state_updated_at.foreman_tasks.rb +5 -0
  26. data/db/migrate/20190404132157_add_implicit_varchar_uuid_cast.rb +25 -0
  27. data/db/seeds.d/30-notification_blueprints.rb +33 -0
  28. data/foreman-tasks.gemspec +1 -1
  29. data/lib/foreman_tasks/cleaner.rb +5 -4
  30. data/lib/foreman_tasks/engine.rb +1 -1
  31. data/lib/foreman_tasks/test_helpers.rb +10 -0
  32. data/lib/foreman_tasks/version.rb +1 -1
  33. data/package.json +14 -11
  34. data/test/controllers/tasks_controller_test.rb +10 -0
  35. data/test/foreman_tasks_test_helper.rb +4 -0
  36. data/test/support/dummy_dynflow_action.rb +29 -0
  37. data/test/support/history_tasks_builder.rb +42 -0
  38. data/test/unit/actions/action_with_sub_plans_test.rb +4 -1
  39. data/test/unit/actions/bulk_action_test.rb +2 -0
  40. data/test/unit/cleaner_test.rb +15 -0
  41. data/test/unit/dashboard_table_filter_test.rb +65 -0
  42. data/test/unit/summarizer_test.rb +39 -0
  43. data/test/unit/task_test.rb +14 -0
  44. data/test/unit/troubleshooting_help_generator_test.rb +71 -0
  45. data/test/unit/ui_notifications_test.rb +86 -0
  46. data/webpack/ForemanTasks/Components/Chart/Chart.js +128 -0
  47. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.js +20 -0
  48. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.stories.js +51 -0
  49. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.test.js +11 -0
  50. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/__snapshots__/PausedTasksCard.test.js.snap +36 -0
  51. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.js +20 -0
  52. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.stories.js +51 -0
  53. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.test.js +11 -0
  54. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/__snapshots__/RunningTasksCard.test.js.snap +36 -0
  55. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.js +64 -0
  56. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.scss +25 -0
  57. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.stories.js +28 -0
  58. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.test.js +18 -0
  59. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/__snapshots__/ScheduledTasksCard.test.js.snap +94 -0
  60. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.js +89 -0
  61. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.scss +46 -0
  62. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.stories.js +72 -0
  63. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.test.js +48 -0
  64. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCardHelper.js +63 -0
  65. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/__snapshots__/StoppedTasksCard.test.js.snap +973 -0
  66. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.js +96 -0
  67. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.scss +17 -0
  68. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.stories.js +46 -0
  69. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.test.js +43 -0
  70. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/__snapshots__/TasksDonutCard.test.js.snap +183 -0
  71. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.js +166 -0
  72. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.scss +24 -0
  73. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.stories.js +25 -0
  74. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.test.js +40 -0
  75. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartConstants.js +13 -0
  76. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartHelper.js +94 -0
  77. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartHelper.test.js +152 -0
  78. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/__snapshots__/TasksDonutChart.test.js.snap +302 -0
  79. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/__snapshots__/TasksDonutChartHelper.test.js.snap +21 -0
  80. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.fixtures.js +25 -0
  81. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.js +72 -0
  82. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.stories.js +52 -0
  83. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.test.js +21 -0
  84. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/__snapshots__/TasksCardsGrid.test.js.snap +223 -0
  85. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.js +57 -0
  86. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.scss +26 -0
  87. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.stories.js +22 -0
  88. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.test.js +57 -0
  89. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/__snapshots__/TasksLabelsRow.test.js.snap +47 -0
  90. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.js +51 -0
  91. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.stories.js +23 -0
  92. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.test.js +19 -0
  93. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/__snapshots__/TimeDropDown.test.js.snap +85 -0
  94. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.js +33 -0
  95. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.scss +11 -0
  96. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.stories.js +22 -0
  97. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.test.js +15 -0
  98. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/__snapshots__/TasksTimeRow.test.js.snap +41 -0
  99. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.js +77 -0
  100. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.scss +6 -0
  101. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardActions.js +62 -0
  102. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardConstants.js +94 -0
  103. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardHelper.js +78 -0
  104. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardPropTypes.js +13 -0
  105. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardReducer.js +50 -0
  106. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardSelectors.js +44 -0
  107. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboard.test.js +13 -0
  108. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardActions.test.js +37 -0
  109. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardHelper.test.js +36 -0
  110. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardReducer.test.js +58 -0
  111. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardSelectors.test..js +59 -0
  112. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboard.test.js.snap +51 -0
  113. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardActions.test.js.snap +61 -0
  114. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardReducer.test.js.snap +280 -0
  115. data/webpack/ForemanTasks/Components/TasksDashboard/index.js +25 -0
  116. data/webpack/ForemanTasks/ForemanTasksReducers.js +10 -0
  117. data/webpack/ForemanTasks/ForemanTasksSelectors.js +1 -0
  118. data/webpack/__mocks__/foremanReact/API.js +7 -0
  119. data/webpack/__mocks__/foremanReact/common/I18n.js +5 -0
  120. data/webpack/__mocks__/foremanReact/common/helpers.js +3 -0
  121. data/webpack/index.js +13 -1
  122. data/webpack/stories/decorators/index.js +1 -0
  123. data/webpack/stories/decorators/withCardsDecorator.js +14 -0
  124. data/webpack/stories/index.js +1 -3
  125. data/webpack/stories/index.scss +6 -0
  126. metadata +101 -8
  127. data/webpack/ForemanTasks/components/Hello/Hello.stories.js +0 -5
  128. data/webpack/ForemanTasks/components/Hello/__tests__/Hello.test.js +0 -11
  129. data/webpack/ForemanTasks/components/Hello/__tests__/__snapshots__/Hello.test.js.snap +0 -7
  130. data/webpack/ForemanTasks/components/Hello/index.js +0 -5
@@ -18,8 +18,8 @@ module ForemanTasks
18
18
  ALLOWED_INPUT_TYPES = [:cronline, :monthly, :weekly, :daily, :hourly].freeze
19
19
 
20
20
  TIME_FORMAT = '%Y-%m-%d %H:%M'.freeze
21
- TIME_REGEXP = /\A\d{4}-\d{2}-\d{2} \d{2}:\d{2}\Z/
22
- DAYS_REGEXP = /\A(\s*\d{1,2}\s*)(,\s*\d{1,2}\s*)*\Z/
21
+ TIME_REGEXP = /\A\d{4}-\d{2}-\d{2} \d{2}:\d{2}\Z/.freeze
22
+ DAYS_REGEXP = /\A(\s*\d{1,2}\s*)(,\s*\d{1,2}\s*)*\Z/.freeze
23
23
 
24
24
  has_one :recurring_logic, :foreign_key => :triggering_id, :dependent => :nullify
25
25
 
@@ -3,6 +3,8 @@ class Setting::ForemanTasks < Setting
3
3
  # Check the table exists
4
4
  return unless super
5
5
 
6
+ Setting::BLANK_ATTRS.push('foreman_tasks_troubleshooting_url')
7
+
6
8
  transaction do
7
9
  [
8
10
  set('foreman_tasks_sync_task_timeout', N_('Number of seconds to wait for synchronous task to finish.'), 120),
@@ -12,7 +14,12 @@ class Setting::ForemanTasks < Setting
12
14
  set('foreman_tasks_proxy_action_retry_count', N_('Number of attempts to start a task on the smart proxy before failing'), 4),
13
15
  set('foreman_tasks_proxy_action_retry_interval', N_('Time in seconds between retries'), 15),
14
16
  set('foreman_tasks_proxy_batch_trigger', N_('Allow triggering tasks on the smart proxy in batches'), true),
15
- set('foreman_tasks_proxy_batch_size', N_('Number of tasks which should be sent to the smart proxy in one request, if foreman_tasks_proxy_batch_trigger is enabled'), 1000)
17
+ set('foreman_tasks_proxy_batch_size', N_('Number of tasks which should be sent to the smart proxy in one request, if foreman_tasks_proxy_batch_trigger is enabled'), 100),
18
+ set('foreman_tasks_troubleshooting_url',
19
+ N_('Url pointing to the task troubleshooting documentation. '\
20
+ 'It should contain %{label} placeholder, that will be replaced with normalized task label '\
21
+ '(restricted to only alphanumeric characters)). %{version} placeholder is also available.'),
22
+ nil)
16
23
  ].each { |s| create! s.update(:category => 'Setting::ForemanTasks') }
17
24
  end
18
25
 
@@ -0,0 +1,47 @@
1
+ module ForemanTasks
2
+ # narrows the scope for the tasks table based on params coming from tasks dashboard
3
+ #
4
+ # Supported filters:
5
+ #
6
+ # * :result
7
+ # * :state
8
+ # * :time_horizon - expected format of Hxy, where the xy is the time horizon in hours we're interested in
9
+ # :time_mode can be set to 'recent' to filter the recent tasks, or 'older' (default) to filter earlier ones
10
+ class DashboardTableFilter
11
+ def initialize(scope, params)
12
+ @scope = scope
13
+ @params = params
14
+ end
15
+
16
+ def scope
17
+ @new_scope = @scope
18
+ scope_by(:result)
19
+ scope_by(:state)
20
+ scope_by_time
21
+ @new_scope
22
+ end
23
+
24
+ private
25
+
26
+ def scope_by(field)
27
+ @new_scope = @new_scope.where(field => @params[field]) if @params[field].present?
28
+ end
29
+
30
+ def scope_by_time
31
+ return if @params[:time_horizon].blank?
32
+ hours = @params[:time_horizon][/\AH(\d{1,2})$/i, 1]
33
+ unless hours
34
+ raise Foreman::Exception, 'Unexpected format of time: should be in form of "H24"'
35
+ end
36
+ timestamp = Time.now.utc - hours.to_i.hours
37
+ case @params[:time_mode]
38
+ when 'recent'
39
+ operator = '>'
40
+ else
41
+ operator = '<'
42
+ search_suffix = 'OR state_updated_at IS NULL'
43
+ end
44
+ @new_scope = @new_scope.where("state_updated_at #{operator} ? #{search_suffix}", timestamp)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,92 @@
1
+ module ForemanTasks
2
+ class TroubleshootingHelpGenerator
3
+ class Link
4
+ attr_accessor :name, :title, :description, :href
5
+
6
+ def initialize(name:, title:, description:, href:)
7
+ @name = name
8
+ @title = title
9
+ @description = description
10
+ @href = href
11
+ end
12
+
13
+ def to_h(capitalize_title: false)
14
+ title = capitalize_title ? self.title.titlecase : self.title
15
+ { name: name, title: title, description: description, href: href, external: true }
16
+ end
17
+ end
18
+
19
+ class Info
20
+ attr_reader :links, :description_lines
21
+
22
+ def initialize
23
+ @description_lines = []
24
+ @links = []
25
+ end
26
+
27
+ def add_line(line)
28
+ @description_lines << line
29
+ end
30
+
31
+ def add_link(link)
32
+ @links << link
33
+ end
34
+ end
35
+
36
+ def initialize(action)
37
+ @action = action
38
+ @custom_info = action.troubleshooting_info if action.respond_to?(:troubleshooting_info)
39
+ end
40
+
41
+ def generate_html
42
+ # rubocop:disable Rails/OutputSafety
43
+ (description + link_descriptions_html).join('<br/>').html_safe
44
+ # rubocop:enable Rails/OutputSafety
45
+ end
46
+
47
+ def link_descriptions_html
48
+ links.map do |link|
49
+ link.description % { link: %(<a href="%{href}">%{title}</a>) % link.to_h }
50
+ end
51
+ end
52
+
53
+ def description
54
+ ret = generic_info.description_lines
55
+ ret += @custom_info.description_lines if @custom_info
56
+ ret
57
+ end
58
+
59
+ def links
60
+ links = generic_info.links
61
+ links += @custom_info.links if @custom_info
62
+ links
63
+ end
64
+
65
+ def generic_info
66
+ @generic_info ||= Info.new.tap do |i|
67
+ i.add_line _('A paused task represents a process that has not finished properly. '\
68
+ 'Any task in paused state can lead to potential inconsistency '\
69
+ 'and needs to be resolved.')
70
+ i.add_line _("The recommended approach is to investigate the error messages below and in 'errors' tab, "\
71
+ 'address the primary cause of the issue and resume the task.')
72
+ if (link = troubleshooting_link)
73
+ i.add_link(link)
74
+ end
75
+ end
76
+ end
77
+
78
+ def troubleshooting_link(generic_only: false)
79
+ url_template = Setting[:foreman_tasks_troubleshooting_url]
80
+ return if url_template.blank?
81
+ url = url_template % { label: generic_only ? '' : link_anchor, version: SETTINGS[:version].short }
82
+ Link.new(name: :troubleshooting,
83
+ title: _('troubleshooting documentation'),
84
+ description: _('See %{link} for more details on how to resolve the issue'),
85
+ href: url)
86
+ end
87
+
88
+ def link_anchor
89
+ @action.label.to_s
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,20 @@
1
+ module UINotifications
2
+ module Tasks
3
+ class Base < ::UINotifications::Base
4
+ def initialize(task)
5
+ @subject = @task = task
6
+ end
7
+
8
+ def initiator
9
+ User.anonymous_admin
10
+ end
11
+
12
+ def troubleshooting_help_generator
13
+ return @troubleshooting_help_generator if defined? @troubleshooting_help_generator
14
+ @troubleshooting_help_generator = if @task.main_action
15
+ ForemanTasks::TroubleshootingHelpGenerator.new(@task.main_action)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,43 @@
1
+ module UINotifications
2
+ module Tasks
3
+ class TaskPausedAdmin < Tasks::Base
4
+ def deliver!
5
+ # delete previous notifications about paused tasks first
6
+ Notification.where(notification_blueprint_id: blueprint.id).each(&:destroy)
7
+ notification = ::Notification.new(
8
+ :audience => Notification::AUDIENCE_ADMIN,
9
+ :notification_blueprint => blueprint,
10
+ :initiator => initiator,
11
+ :subject => subject,
12
+ :message => message
13
+ )
14
+
15
+ notification.send(:set_custom_attributes) # to add links from blueprint
16
+ notification.actions['links'] ||= []
17
+ if troubleshooting_help_generator
18
+ troubleshooting_link = troubleshooting_help_generator.troubleshooting_link(generic_only: true)
19
+ notification.actions['links'] << troubleshooting_link.to_h(capitalize_title: true) if troubleshooting_link
20
+ end
21
+ notification.save!
22
+ notification
23
+ end
24
+
25
+ def initiator
26
+ User.anonymous_admin
27
+ end
28
+
29
+ def blueprint
30
+ @blueprint ||= NotificationBlueprint.unscoped.find_by(:name => 'tasks_paused_admin')
31
+ end
32
+
33
+ def message
34
+ return @message if @message
35
+
36
+ tasks_count = ForemanTasks::Task.where(state: 'paused').count
37
+ @message = n_('There is %{count} paused task in the system that need attention',
38
+ 'There are %{count} paused tasks in the system that need attention',
39
+ tasks_count) % { count: tasks_count }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ module UINotifications
2
+ module Tasks
3
+ class TaskPausedOwner < Tasks::Base
4
+ def deliver!
5
+ notification = ::Notification.new(
6
+ :audience => Notification::AUDIENCE_USER,
7
+ :notification_blueprint => blueprint,
8
+ :initiator => initiator,
9
+ :message => message,
10
+ :subject => subject
11
+ )
12
+ notification.send(:set_custom_attributes) # to add links from blueprint
13
+ notification.actions['links'] ||= []
14
+ if troubleshooting_help_generator
15
+ notification.actions['links'].concat(troubleshooting_help_generator.links.map { |l| l.to_h(capitalize_title: true) })
16
+ end
17
+ notification.save!
18
+ notification
19
+ end
20
+
21
+ def blueprint
22
+ @blueprint ||= NotificationBlueprint.unscoped.find_by(:name => 'tasks_paused_owner')
23
+ end
24
+
25
+ def message
26
+ StringParser.new(blueprint.message, subject: subject.action).to_s
27
+ end
28
+ end
29
+ end
30
+ end
@@ -174,6 +174,8 @@
174
174
  </div>
175
175
  </div>
176
176
 
177
+ <%= troubleshooting_info %>
178
+
177
179
  <% unless @task.humanized[:output].blank? %>
178
180
  <div>
179
181
  <span class="param-name list-group-item-heading"><%= _("Output") %>:</span>
@@ -184,10 +186,10 @@
184
186
  <% end %>
185
187
 
186
188
  <% unless @task.humanized[:errors].blank? %>
187
- <div>
188
- <span class="param-name"><%= _("Errors") %>:</span>
189
+ <p>
190
+ <span class="param-name"><b><%= _("Errors") %>:</b></span>
189
191
  <span class="param-value">
190
192
  <pre><%=Array(@task.humanized[:errors]).join("\n") %></pre>
191
193
  </span>
192
- </div>
194
+ </p>
193
195
  <% end %>
@@ -1,3 +1,16 @@
1
+ <% content_for(:javascripts) do %>
2
+ <%= webpacked_plugins_js_for :'foreman-tasks' %>
3
+ <% end %>
4
+ <% content_for(:stylesheets) do %>
5
+ <%= webpacked_plugins_css_for :'foreman-tasks' %>
6
+ <% end %>
7
+ <% content_for(:before_search_bar) do %>
8
+ <div id="tasks-dashboard"></div>
9
+ <%= mount_react_component('TasksDashboard', '#tasks-dashboard', {
10
+ yo: 'ya'
11
+ }.to_json) %>
12
+ <% end %>
13
+
1
14
  <% title _("Tasks") %>
2
15
  <% title_actions csv_link, help_button %>
3
16
  <% stylesheet 'foreman_tasks/tasks' %>
data/config/routes.rb CHANGED
@@ -11,6 +11,7 @@ Foreman::Application.routes.draw do
11
11
  resources :tasks, :only => [:index, :show] do
12
12
  collection do
13
13
  get 'auto_complete_search'
14
+ get '/summary/:recent_timeframe', action: 'summary'
14
15
  end
15
16
  member do
16
17
  get :sub_tasks
@@ -0,0 +1,5 @@
1
+ class AddTaskStateUpdatedAt < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :foreman_tasks_tasks, :state_updated_at, :timestamp, index: true
4
+ end
5
+ end
@@ -0,0 +1,25 @@
1
+ class AddImplicitVarcharUuidCast < ActiveRecord::Migration[5.2]
2
+ def up
3
+ if on_postgresql?
4
+ ActiveRecord::Base.connection.execute <<~SQL
5
+ CREATE CAST (varchar AS uuid)
6
+ WITH INOUT
7
+ AS IMPLICIT
8
+ SQL
9
+ end
10
+ end
11
+
12
+ def down
13
+ if on_postgresql?
14
+ ActiveRecord::Base.connection.execute <<~SQL
15
+ DROP CAST (varchar AS uuid)
16
+ SQL
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def on_postgresql?
23
+ ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ blueprints = [
2
+ {
3
+ group: N_('Tasks'),
4
+ name: 'tasks_paused_admin',
5
+ message: "DYNAMIC",
6
+ level: 'warning',
7
+ actions:
8
+ {
9
+ links:
10
+ [
11
+ href: "/foreman_tasks/tasks?search=#{CGI.escape('state=paused')}",
12
+ title: N_('List of tasks')
13
+ ]
14
+ }
15
+ },
16
+
17
+ {
18
+ group: N_('Tasks'),
19
+ name: 'tasks_paused_owner',
20
+ message: "The task '%{subject}' got paused",
21
+ level: 'warning',
22
+ actions:
23
+ {
24
+ links:
25
+ [
26
+ path_method: :foreman_tasks_task_path,
27
+ title: N_('Task Details')
28
+ ]
29
+ }
30
+ }
31
+ ]
32
+
33
+ blueprints.each { |blueprint| UINotifications::Seed.new(blueprint).configure }
@@ -29,7 +29,7 @@ same resource. It also optionally provides Dynflow infrastructure for using it f
29
29
  s.extra_rdoc_files = Dir['README*', 'LICENSE']
30
30
 
31
31
  s.add_dependency "foreman-tasks-core"
32
- s.add_dependency "dynflow", '>= 1.2.2'
32
+ s.add_dependency "dynflow", '>= 1.2.3'
33
33
  s.add_dependency "sinatra" # for Dynflow web console
34
34
  s.add_dependency "parse-cron", '~> 0.1.4'
35
35
  s.add_dependency "get_process_mem" # for memory polling
@@ -193,15 +193,16 @@ module ForemanTasks
193
193
 
194
194
  def orphaned_dynflow_tasks
195
195
  db = ForemanTasks.dynflow.world.persistence.adapter.db
196
- uuid_select = Sequel.lit(ForemanTasks::Task.select(:external_id).to_sql)
197
- db[:dynflow_execution_plans].filter(:uuid => [uuid_select]).invert
196
+ db.fetch("select dynflow_execution_plans.uuid from dynflow_execution_plans left join "\
197
+ "foreman_tasks_tasks on (dynflow_execution_plans.uuid = foreman_tasks_tasks.external_id) "\
198
+ "where foreman_tasks_tasks.id IS NULL")
198
199
  end
199
200
 
200
201
  def prepare_filter
201
202
  filter_parts = [filter]
202
203
  filter_parts << %(started_at < "#{after.ago.to_s(:db)}") if after > 0
203
- filter_parts << states.map { |s| "state = #{s}" }.join(' OR ') if states.any?
204
- filter_parts.select(&:present?).join(' AND ')
204
+ filter_parts << "state ^ (#{states.join(',')})" if states.any?
205
+ filter_parts.select(&:present?).map { |segment| "(#{segment})" }.join(' AND ')
205
206
  end
206
207
 
207
208
  def start_tracking_progress(name, total = tasks.size)
@@ -56,7 +56,7 @@ module ForemanTasks
56
56
  :last => true
57
57
 
58
58
  security_block :foreman_tasks do |_map|
59
- permission :view_foreman_tasks, { :'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :show],
59
+ permission :view_foreman_tasks, { :'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :summary, :show],
60
60
  :'foreman_tasks/react' => [:index],
61
61
  :'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary] }, :resource_type => ForemanTasks::Task.name
62
62
  permission :edit_foreman_tasks, { :'foreman_tasks/tasks' => [:resume, :unlock, :force_unlock, :cancel_step, :cancel, :abort],
@@ -1,9 +1,19 @@
1
1
  require 'dynflow/testing'
2
2
  module ForemanTasks
3
3
  module TestHelpers
4
+ def self.use_in_memory_sqlite!
5
+ raise 'the in thread world have already been initialized' if @test_in_thread_world
6
+ @use_in_memory_sqlite = true
7
+ end
8
+
4
9
  def self.test_in_thread_world
5
10
  return @test_in_thread_world if @test_in_thread_world
6
11
  world_config = ForemanTasks.dynflow.config.world_config
12
+ if @use_in_memory_sqlite
13
+ world_config.persistence_adapter = lambda do |*_args|
14
+ ::ForemanTasks::Dynflow::Persistence.new('adapter' => 'sqlite', 'database' => ':memory:')
15
+ end
16
+ end
7
17
  @test_in_thread_world = ::Dynflow::Testing::InThreadWorld.new(world_config)
8
18
  end
9
19