foreman-tasks 0.16.0 → 0.16.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.babelrc +4 -1
  3. data/.eslintrc +4 -13
  4. data/app/assets/stylesheets/foreman_tasks/application.css.scss +0 -37
  5. data/app/controllers/foreman_tasks/api/tasks_controller.rb +9 -5
  6. data/app/controllers/foreman_tasks/tasks_controller.rb +23 -19
  7. data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +28 -29
  8. data/app/lib/actions/bulk_action.rb +1 -1
  9. data/app/models/foreman_tasks/task.rb +1 -1
  10. data/app/models/foreman_tasks/task/dynflow_task.rb +30 -0
  11. data/app/services/foreman_tasks/troubleshooting_help_generator.rb +4 -0
  12. data/app/views/foreman_tasks/api/tasks/details.json.rabl +18 -0
  13. data/app/views/foreman_tasks/layouts/react.html.erb +1 -1
  14. data/app/views/foreman_tasks/tasks/index.html.erb +3 -0
  15. data/app/views/foreman_tasks/tasks/show.html.erb +10 -134
  16. data/config/routes.rb +3 -0
  17. data/lib/foreman_tasks/engine.rb +1 -1
  18. data/lib/foreman_tasks/tasks/export_tasks.rake +1 -2
  19. data/lib/foreman_tasks/version.rb +1 -1
  20. data/package.json +6 -20
  21. data/test/controllers/tasks_controller_test.rb +11 -0
  22. data/test/helpers/foreman_tasks/foreman_tasks_helper_test.rb +39 -0
  23. data/test/unit/actions/bulk_action_test.rb +2 -0
  24. data/test/unit/task_test.rb +4 -0
  25. data/webpack/ForemanTasks/Components/TaskDetails/Components/Errors.js +60 -0
  26. data/webpack/ForemanTasks/Components/TaskDetails/Components/Locks.js +46 -0
  27. data/webpack/ForemanTasks/Components/TaskDetails/Components/Raw.js +73 -0
  28. data/webpack/ForemanTasks/Components/TaskDetails/Components/RunningSteps.js +55 -0
  29. data/webpack/ForemanTasks/Components/TaskDetails/Components/Task.js +202 -0
  30. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskHelper.js +38 -0
  31. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskInfo.js +238 -0
  32. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Errors.test.js +36 -0
  33. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Locks.test.js +28 -0
  34. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Raw.test.js +27 -0
  35. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/RunningSteps.test.js +29 -0
  36. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Task.test.js +18 -0
  37. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskHelper.test.js +77 -0
  38. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskInfo.test.js +60 -0
  39. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Errors.test.js.snap +77 -0
  40. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Locks.test.js.snap +108 -0
  41. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Raw.test.js.snap +174 -0
  42. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/RunningSteps.test.js.snap +62 -0
  43. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Task.test.js.snap +282 -0
  44. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskHelper.test.js.snap +37 -0
  45. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskInfo.test.js.snap +568 -0
  46. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.js +87 -0
  47. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.scss +63 -0
  48. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.stories.js +5 -0
  49. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsActions.js +109 -0
  50. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsConstants.js +18 -0
  51. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsReducer.js +44 -0
  52. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +83 -0
  53. data/webpack/ForemanTasks/Components/TaskDetails/TasksDetailsHelper.js +1 -0
  54. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetails.test.js +12 -0
  55. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetailsActions.test.js +20 -0
  56. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetailsReducer.test.js +33 -0
  57. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetails.test.js.snap +97 -0
  58. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetailsActions.test.js.snap +43 -0
  59. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetailsReducer.test.js.snap +26 -0
  60. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/integration.test.js.snap +122 -0
  61. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/integration.test.js +63 -0
  62. data/webpack/ForemanTasks/Components/TaskDetails/index.js +77 -0
  63. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.js +6 -1
  64. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/__snapshots__/PausedTasksCard.test.js.snap +2 -0
  65. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.js +6 -1
  66. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/__snapshots__/RunningTasksCard.test.js.snap +2 -0
  67. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.js +3 -6
  68. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.scss +0 -3
  69. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/__snapshots__/ScheduledTasksCard.test.js.snap +3 -0
  70. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.js +2 -0
  71. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.scss +0 -4
  72. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCardHelper.js +3 -3
  73. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/__snapshots__/StoppedTasksCard.test.js.snap +495 -54
  74. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.js +1 -0
  75. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.scss +6 -0
  76. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.scss +0 -3
  77. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.scss +1 -1
  78. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.scss +2 -2
  79. data/webpack/ForemanTasks/Components/common/ClickConfirmation/index.js +83 -0
  80. data/webpack/ForemanTasks/ForemanTasksReducers.js +2 -0
  81. data/webpack/__mocks__/foremanReact/common/I18n.js +2 -0
  82. data/webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js +2 -0
  83. data/webpack/index.js +5 -0
  84. metadata +46 -9
  85. data/app/views/foreman_tasks/tasks/_details.html.erb +0 -195
  86. data/app/views/foreman_tasks/tasks/_errors.html.erb +0 -42
  87. data/app/views/foreman_tasks/tasks/_locks.html.erb +0 -19
  88. data/app/views/foreman_tasks/tasks/_raw.html.erb +0 -28
  89. data/app/views/foreman_tasks/tasks/_running_steps.html.erb +0 -40
@@ -35,6 +35,9 @@ Foreman::Application.routes.draw do
35
35
  end
36
36
 
37
37
  resources :tasks, :only => [:show, :index] do
38
+ member do
39
+ get :details
40
+ end
38
41
  collection do
39
42
  post :bulk_search
40
43
  post :bulk_resume
@@ -58,7 +58,7 @@ module ForemanTasks
58
58
  security_block :foreman_tasks do |_map|
59
59
  permission :view_foreman_tasks, { :'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :summary, :show],
60
60
  :'foreman_tasks/react' => [:index],
61
- :'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary] }, :resource_type => ForemanTasks::Task.name
61
+ :'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary, :details] }, :resource_type => ForemanTasks::Task.name
62
62
  permission :edit_foreman_tasks, { :'foreman_tasks/tasks' => [:resume, :unlock, :force_unlock, :cancel_step, :cancel, :abort],
63
63
  :'foreman_tasks/api/tasks' => [:bulk_resume] }, :resource_type => ForemanTasks::Task.name
64
64
 
@@ -211,7 +211,6 @@ namespace :foreman_tasks do
211
211
  'javascripts/application.js',
212
212
  'vendor/bootstrap/css/bootstrap.css',
213
213
  'stylesheets/application.css'].each do |file|
214
-
215
214
  filename = File.join(Gem::Specification.find_by_name('dynflow').gem_dir, 'web', 'assets', file) # rubocop:disable Rails/DynamicFindBy
216
215
  FileUtils.copy_file(filename, File.join(tmp_dir, File.basename(file)))
217
216
  end
@@ -244,6 +243,7 @@ namespace :foreman_tasks do
244
243
 
245
244
  tasks = ForemanTasks::Task.search_for(filter)
246
245
 
246
+ puts _("Exporting all tasks matching filter #{filter}")
247
247
  puts _("Gathering #{tasks.count} tasks.")
248
248
  if format == 'html'
249
249
  Dir.mktmpdir('task-export') do |tmp_dir|
@@ -255,7 +255,6 @@ namespace :foreman_tasks do
255
255
  tasks.each_with_index do |task, count|
256
256
  File.open(File.join(tmp_dir, "#{task.id}.html"), 'w') { |file| file.write(PageHelper.pagify(renderer.render_task(task))) }
257
257
  puts "#{count + 1}/#{total}"
258
- count += 1
259
258
  end
260
259
 
261
260
  File.open(File.join(tmp_dir, 'index.html'), 'w') { |file| file.write(PageHelper.pagify(PageHelper.generate_index(tasks))) }
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '0.16.0'.freeze
2
+ VERSION = '0.16.1'.freeze
3
3
  end
@@ -21,17 +21,22 @@
21
21
  "bugs": {
22
22
  "url": "http://projects.theforeman.org/projects/foreman-tasks/issues"
23
23
  },
24
+ "dependencies": {
25
+ "@theforeman/vendor": "^0.1.1",
26
+ "humanize-duration": "^3.20.1",
27
+ "react-intl": "^2.8.0"
28
+ },
24
29
  "devDependencies": {
25
30
  "@storybook/addon-actions": "^5.0.1",
26
31
  "@storybook/addon-knobs": "^5.0.1",
27
32
  "@storybook/react": "^5.0.1",
33
+ "@theforeman/vendor-dev": "^0.1.1",
28
34
  "babel-cli": "^6.10.1",
29
35
  "babel-core": "^6.26.3",
30
36
  "babel-eslint": "^8.2.3",
31
37
  "babel-jest": "^23.6.0",
32
38
  "babel-loader": "^7.1.1",
33
39
  "babel-plugin-dynamic-import-node": "^2.0.0",
34
- "babel-plugin-lodash": "^3.3.4",
35
40
  "babel-plugin-module-resolver": "^3.2.0",
36
41
  "babel-plugin-syntax-dynamic-import": "^6.18.0",
37
42
  "babel-plugin-transform-class-properties": "^6.24.1",
@@ -61,25 +66,6 @@
61
66
  "stylelint-config-standard": "^18.0.0",
62
67
  "surge": "^0.20.3"
63
68
  },
64
- "dependencies": {
65
- "babel-polyfill": "^6.26.0",
66
- "classnames": "^2.2.5",
67
- "lodash": "^4.17.11",
68
- "patternfly-react": "^2.29.0",
69
- "prop-types": "^15.6.0",
70
- "react": "^16.8.1",
71
- "react-dom": "^16.8.1",
72
- "react-redux": "^5.0.6",
73
- "react-router": "^4.3.1",
74
- "react-router-bootstrap": "^0.24.4",
75
- "react-router-dom": "^4.3.1",
76
- "redux": "^3.6.0",
77
- "redux-thunk": "^2.3.0",
78
- "reselect": "^3.0.1",
79
- "seamless-immutable": "^7.1.2",
80
- "urijs": "^1.19.1",
81
- "uuid": "^3.3.2"
82
- },
83
69
  "jest": {
84
70
  "verbose": true,
85
71
  "testMatch": [
@@ -67,6 +67,17 @@ module ForemanTasks
67
67
  session: set_session_user(User.current)
68
68
  assert_response :not_found
69
69
  end
70
+
71
+ it 'supports csv export' do
72
+ parent = FactoryBot.create(:some_task, :action => 'Some action')
73
+ child = FactoryBot.create(:some_task, :action => 'Child action')
74
+ child.parent_task_id = parent.id
75
+ child.save!
76
+ get(:sub_tasks, params: { id: parent.id, format: :csv }, session: set_session_user)
77
+ assert_response :success
78
+ assert_equal 2, response.body.lines.size
79
+ assert_include response.body.lines[1], 'Child action'
80
+ end
70
81
  end
71
82
 
72
83
  describe 'taxonomy scoping' do
@@ -0,0 +1,39 @@
1
+ require 'foreman_tasks_test_helper'
2
+
3
+ module ForemanTasks
4
+ class ForemanTasksHelperTest < ActionView::TestCase
5
+ describe 'breadcrumb items' do
6
+ before do
7
+ self.class.send(:include, ForemanTasks::TasksHelper)
8
+ end
9
+
10
+ it 'prepares items for index correctly' do
11
+ stubs(:action_name).returns('index')
12
+ items = breadcrumb_items
13
+ items.count.must_equal 1
14
+ items.first[:caption].must_equal 'Tasks'
15
+ items.first[:url].must_be_nil
16
+ end
17
+
18
+ it 'prepares items for show correctly' do
19
+ @task = FactoryBot.build(:dynflow_task, :user_create_task)
20
+ @task.action = 'A task'
21
+ stubs(:action_name).returns('show')
22
+ items = breadcrumb_items
23
+ items.map { |i| i[:caption] }.must_equal ['Tasks', 'A task']
24
+ items.last[:url].must_be_nil
25
+ end
26
+
27
+ it 'prepares items for sub tasks correctly' do
28
+ @task = FactoryBot.build(:dynflow_task, :user_create_task)
29
+ child = FactoryBot.build(:dynflow_task, :user_create_task)
30
+ @task.sub_tasks = [child]
31
+ @task.action = 'A task'
32
+ stubs(:action_name).returns('sub_tasks')
33
+ items = breadcrumb_items
34
+ items.map { |i| i[:caption] }.must_equal ['Tasks', 'A task', 'Sub tasks']
35
+ items.last[:url].must_be_nil
36
+ end
37
+ end
38
+ end
39
+ end
@@ -28,6 +28,7 @@ module ForemanTasks
28
28
  end
29
29
 
30
30
  specify 'it plans a task for each target' do
31
+ Target.expects(:unscoped).returns(Target)
31
32
  Target.expects(:where).with(:id => targets.map(&:id)).returns(targets)
32
33
 
33
34
  task.sub_tasks.count.must_equal targets.count
@@ -37,6 +38,7 @@ module ForemanTasks
37
38
  end
38
39
 
39
40
  specify 'it plans a task for each target even if target cannot be found' do
41
+ Target.expects(:unscoped).returns(Target)
40
42
  Target.expects(:where).with(:id => targets.map(&:id)).returns(targets.take(4))
41
43
 
42
44
  task.sub_tasks.count.must_equal targets.count
@@ -43,6 +43,10 @@ class TasksTest < ActiveSupport::TestCase
43
43
  test 'can search the tasks by array' do
44
44
  assert_equal [@task_one], ForemanTasks::Task.search_for("user ^ (this_user, #{@user_one.login}, that_user)")
45
45
  end
46
+
47
+ test 'properly returns username' do
48
+ assert_equal @task_one.username, @user_one.login
49
+ end
46
50
  end
47
51
 
48
52
  describe 'state_updated_at' do
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Alert } from 'patternfly-react';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
+
6
+ const Errors = ({ ...props }) => {
7
+ const { failedSteps, executionPlan } = props;
8
+ if (!executionPlan)
9
+ return (
10
+ <Alert type="error">{__('Execution plan data not available ')}</Alert>
11
+ );
12
+ if (!failedSteps.length)
13
+ return <Alert type="success">{__('No errors')}</Alert>;
14
+ return (
15
+ <div>
16
+ {failedSteps.map((step, i) => (
17
+ <Alert type="error" key={i}>
18
+ <span>{__('Action')}:</span>
19
+ <span>
20
+ <pre>{step.action_class}</pre>
21
+ </span>
22
+ <span>{__('Input')}:</span>
23
+ <span>
24
+ <pre>{step.input}</pre>
25
+ </span>
26
+ <span>{__('Output')}:</span>
27
+ <span>
28
+ <pre>{step.output}</pre>
29
+ </span>
30
+ {step.error && (
31
+ <React.Fragment>
32
+ <span>{__('Exception')}:</span>
33
+ <span>
34
+ <pre>
35
+ {step.error.exception_class}: {step.error.message}
36
+ </pre>
37
+ </span>
38
+ <span>{__('Backtrace')}:</span>
39
+ <span>
40
+ <pre>{step.error.backtrace.join('\n')}</pre>
41
+ </span>
42
+ </React.Fragment>
43
+ )}
44
+ </Alert>
45
+ ))}
46
+ </div>
47
+ );
48
+ };
49
+
50
+ Errors.propTypes = {
51
+ failedSteps: PropTypes.array,
52
+ executionPlan: PropTypes.shape({}),
53
+ };
54
+
55
+ Errors.defaultProps = {
56
+ failedSteps: [],
57
+ executionPlan: {},
58
+ };
59
+
60
+ export default Errors;
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Alert, Card, Row, Col } from 'patternfly-react';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
+
6
+ const Locks = ({ locks }) => (
7
+ <div>
8
+ <Alert type="info">
9
+ {__(
10
+ 'You can find resource locks on this page. Exclusive lock marked with locked icon means that no other task can use locked resource while this task is running. Non-exclusive lock marked with unlocked icon means other tasks can access the resource freely, it is only used to indicate the relation of this task with the resource'
11
+ )}
12
+ </Alert>
13
+ <Card.Grid>
14
+ <Row>
15
+ {locks.map((lock, key) => (
16
+ <Col xs={6} sm={4} md={4} key={key}>
17
+ <Card className="card-pf-aggregate-status" accented>
18
+ <Card.Title>
19
+ <span
20
+ className={`fa ${
21
+ lock.exclusive ? 'fa-lock' : 'fa-unlock-alt'
22
+ }`}
23
+ />
24
+ {lock.name}
25
+ </Card.Title>
26
+ <Card.Body>
27
+ {`${lock.resource_type} id:${lock.resource_id}`}
28
+ <br />
29
+ </Card.Body>
30
+ </Card>
31
+ </Col>
32
+ ))}
33
+ </Row>
34
+ </Card.Grid>
35
+ </div>
36
+ );
37
+
38
+ Locks.propTypes = {
39
+ locks: PropTypes.array,
40
+ };
41
+
42
+ Locks.defaultProps = {
43
+ locks: [],
44
+ };
45
+
46
+ export default Locks;
@@ -0,0 +1,73 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { translate as __ } from 'foremanReact/common/I18n';
4
+
5
+ import { durationInWords } from './TaskHelper';
6
+
7
+ const Raw = ({ id, label, startedAt, endedAt, input, output, externalId }) => (
8
+ <div>
9
+ <div>
10
+ <span className="param-name list-group-item-heading">{__('Id')}:</span>
11
+ <span className="param-value">{id}</span>
12
+ </div>
13
+ <div>
14
+ <span className="param-name list-group-item-heading">{__('Label')}:</span>
15
+ <span className="param-value">{label}</span>
16
+ </div>
17
+ <div>
18
+ <span className="param-name list-group-item-heading">
19
+ {__('Duration')}:
20
+ </span>
21
+ <span
22
+ className="param-value"
23
+ title={durationInWords(startedAt, endedAt || new Date()).tooltip}
24
+ >
25
+ {durationInWords(startedAt, endedAt || new Date()).text}
26
+ </span>
27
+ </div>
28
+ <div>
29
+ <span className="param-name list-group-item-heading">
30
+ {__('Raw input')}:
31
+ </span>
32
+ <span className="param-value">
33
+ <pre>{JSON.stringify(input, null, ' ')}</pre>
34
+ </span>
35
+ </div>
36
+ <div>
37
+ <span className="param-name list-group-item-heading">
38
+ {__('Raw output')}:
39
+ </span>
40
+ <span className="param-value">
41
+ <pre>{JSON.stringify(output, null, ' ')}</pre>
42
+ </span>
43
+ </div>
44
+ <div>
45
+ <span className="param-name list-group-item-heading">
46
+ {__('External Id')}:
47
+ </span>
48
+ <span className="param-value">{externalId}</span>
49
+ </div>
50
+ </div>
51
+ );
52
+
53
+ Raw.propTypes = {
54
+ id: PropTypes.string,
55
+ label: PropTypes.string,
56
+ startedAt: PropTypes.string,
57
+ endedAt: PropTypes.string,
58
+ input: PropTypes.oneOfType([PropTypes.array, PropTypes.shape({})]),
59
+ output: PropTypes.shape({}),
60
+ externalId: PropTypes.string,
61
+ };
62
+
63
+ Raw.defaultProps = {
64
+ id: '',
65
+ label: '',
66
+ startedAt: '',
67
+ endedAt: '',
68
+ input: [],
69
+ output: {},
70
+ externalId: '',
71
+ };
72
+
73
+ export default Raw;
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Alert, Button } from 'patternfly-react';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
+
6
+ const RunningSteps = ({ runningSteps }) => {
7
+ if (!runningSteps.length) return <span>{__('No running steps')}</span>;
8
+ return (
9
+ <div>
10
+ {runningSteps.map((step, i) => (
11
+ <Alert type="warning" key={i}>
12
+ {step.cancellable && (
13
+ <p>
14
+ <Button
15
+ bsSize="small"
16
+ data-method="post"
17
+ href={`/foreman_tasks/tasks/${step.id}/cancel_step`}
18
+ >
19
+ {__('Cancel')}
20
+ </Button>
21
+ </p>
22
+ )}
23
+
24
+ <p>
25
+ <span>{__('Action')}:</span>
26
+ <span />
27
+ </p>
28
+ <pre>{step.action_class}</pre>
29
+ <p>
30
+ <span>{__('State')}:</span>
31
+ <span>{step.state}</span>
32
+ </p>
33
+ <span>{__('Input')}:</span>
34
+ <span>
35
+ <pre>{step.input}</pre>
36
+ </span>
37
+ <span>{__('Output')}:</span>
38
+ <span>
39
+ <pre>{step.output}</pre>
40
+ </span>
41
+ </Alert>
42
+ ))}
43
+ </div>
44
+ );
45
+ };
46
+
47
+ RunningSteps.propTypes = {
48
+ runningSteps: PropTypes.array,
49
+ };
50
+
51
+ RunningSteps.defaultProps = {
52
+ runningSteps: [],
53
+ };
54
+
55
+ export default RunningSteps;
@@ -0,0 +1,202 @@
1
+ import React, { Component } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Grid, Row, Col, Button } from 'patternfly-react';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
+ import TaskInfo from './TaskInfo';
6
+ import { ClickConfirmation } from '../../common/ClickConfirmation';
7
+
8
+ class Task extends Component {
9
+ taskProgressToggle = () => {
10
+ const {
11
+ timeoutId,
12
+ refetchTaskDetails,
13
+ id,
14
+ loading,
15
+ taskReloadStop,
16
+ taskReloadStart,
17
+ } = this.props;
18
+ if (timeoutId) {
19
+ taskReloadStop(timeoutId);
20
+ } else {
21
+ taskReloadStart(timeoutId, refetchTaskDetails, id, loading);
22
+ }
23
+ };
24
+
25
+ render() {
26
+ const {
27
+ taskReload,
28
+ externalId,
29
+ id,
30
+ state,
31
+ allowDangerousActions,
32
+ resumable,
33
+ cancellable,
34
+ hasSubTasks,
35
+ parentTask,
36
+ showUnlockModal,
37
+ showForceUnlockModal,
38
+ toggleUnlockModal,
39
+ toggleForceUnlockModal,
40
+ } = this.props;
41
+ const modalUnlock = (
42
+ <ClickConfirmation
43
+ showModal={showUnlockModal}
44
+ title={__('Unlock')}
45
+ body={__(
46
+ "This will unlock the resources that the task is running against. Please note that this might lead to inconsistent state and should be used with caution, after making sure that the task can't be resumed."
47
+ )}
48
+ confirmationMessage={__(
49
+ 'I understand that this may cause harm and have working database backups of all backend services.'
50
+ )}
51
+ confirmAction={__('Unlock')}
52
+ path={`/foreman_tasks/tasks/${id}/unlock`}
53
+ confirmType="warning"
54
+ closeModal={toggleUnlockModal}
55
+ />
56
+ );
57
+
58
+ const modalForceUnlock = (
59
+ <ClickConfirmation
60
+ showModal={showForceUnlockModal}
61
+ title={__('Force Unlock')}
62
+ body={__(
63
+ 'Resources will be unlocked and will not prevent other tasks from being run. As the task might be still running, it should be avoided to use this unless you are really sure the task got stuck'
64
+ )}
65
+ confirmationMessage={__(
66
+ 'I understand that this may cause harm and have working database backups of all backend services.'
67
+ )}
68
+ confirmAction={__('Force Unlock')}
69
+ path={`/foreman_tasks/tasks/${id}/force_unlock`}
70
+ confirmType="danger"
71
+ closeModal={toggleForceUnlockModal}
72
+ />
73
+ );
74
+ return (
75
+ <React.Fragment>
76
+ {modalUnlock}
77
+ {modalForceUnlock}
78
+ <Grid>
79
+ <Row>
80
+ <Col xs={12}>
81
+ <Button
82
+ hidden={!allowDangerousActions}
83
+ className="reload-button"
84
+ bsSize="small"
85
+ onClick={this.taskProgressToggle}
86
+ >
87
+ <span
88
+ className={`glyphicon glyphicon-refresh ${
89
+ taskReload ? 'spin' : ''
90
+ }`}
91
+ />
92
+ {__(`${taskReload ? 'Stop' : 'Start'} auto-reloading`)}
93
+ </Button>
94
+ <Button
95
+ bsSize="small"
96
+ href={`/foreman_tasks/dynflow/${externalId}`}
97
+ >
98
+ {__('Dynflow console')}
99
+ </Button>
100
+ <Button
101
+ bsSize="small"
102
+ bsStyle="primary"
103
+ data-method="post"
104
+ href={`/foreman_tasks/tasks/${id}/resume`}
105
+ disabled={!resumable}
106
+ >
107
+ {__('Resume')}
108
+ </Button>
109
+ <Button
110
+ bsSize="small"
111
+ data-method="post"
112
+ href={`/foreman_tasks/tasks/${id}/cancel`}
113
+ disabled={!cancellable}
114
+ >
115
+ {__('Cancel')}
116
+ </Button>
117
+
118
+ {parentTask && (
119
+ <Button
120
+ bsSize="small"
121
+ href={`/foreman_tasks/tasks/${parentTask}`}
122
+ >
123
+ {__('Parent task')}
124
+ </Button>
125
+ )}
126
+ {hasSubTasks && (
127
+ <Button
128
+ bsSize="small"
129
+ href={`/foreman_tasks/tasks/${id}/sub_tasks`}
130
+ >
131
+ {__('Sub tasks')}
132
+ </Button>
133
+ )}
134
+ {allowDangerousActions && (
135
+ <Button
136
+ bsSize="small"
137
+ disabled={state !== 'paused'}
138
+ onClick={toggleUnlockModal}
139
+ >
140
+ {__('Unlock')}
141
+ </Button>
142
+ )}
143
+ {allowDangerousActions && (
144
+ <Button
145
+ bsSize="small"
146
+ disabled={state === 'stopped'}
147
+ onClick={toggleForceUnlockModal}
148
+ >
149
+ {__('Force Unlock')}
150
+ </Button>
151
+ )}
152
+ </Col>
153
+ </Row>
154
+ <TaskInfo {...this.props} />
155
+ </Grid>
156
+ </React.Fragment>
157
+ );
158
+ }
159
+ }
160
+
161
+ Task.propTypes = {
162
+ ...TaskInfo.PropTypes,
163
+ state: PropTypes.string,
164
+ allowDangerousActions: PropTypes.bool,
165
+ resumable: PropTypes.bool,
166
+ cancellable: PropTypes.bool,
167
+ refetchTaskDetails: PropTypes.func,
168
+ hasSubTasks: PropTypes.bool,
169
+ parentTask: PropTypes.string,
170
+ taskReload: PropTypes.bool,
171
+ taskReloadStop: PropTypes.func,
172
+ taskReloadStart: PropTypes.func,
173
+ timeoutId: PropTypes.number,
174
+ externalId: PropTypes.string,
175
+ id: PropTypes.string.isRequired,
176
+ showUnlockModal: PropTypes.bool,
177
+ showForceUnlockModal: PropTypes.bool,
178
+ toggleUnlockModal: PropTypes.func,
179
+ toggleForceUnlockModal: PropTypes.func,
180
+ };
181
+
182
+ Task.defaultProps = {
183
+ ...TaskInfo.defaultProps,
184
+ state: '',
185
+ allowDangerousActions: false,
186
+ resumable: false,
187
+ cancellable: false,
188
+ refetchTaskDetails: () => null,
189
+ hasSubTasks: false,
190
+ parentTask: '',
191
+ taskReload: false,
192
+ taskReloadStop: () => null,
193
+ taskReloadStart: () => null,
194
+ timeoutId: null,
195
+ externalId: '',
196
+ showUnlockModal: false,
197
+ showForceUnlockModal: false,
198
+ toggleUnlockModal: () => null,
199
+ toggleForceUnlockModal: () => null,
200
+ };
201
+
202
+ export default Task;