foreman-tasks 2.0.3 → 3.0.0

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/.github/workflows/js_tests.yml +27 -0
  3. data/.github/workflows/ruby_tests.yml +74 -0
  4. data/.rubocop.yml +12 -4
  5. data/.rubocop_todo.yml +32 -25
  6. data/Gemfile +5 -0
  7. data/app/controllers/foreman_tasks/api/tasks_controller.rb +31 -58
  8. data/app/controllers/foreman_tasks/concerns/parameters/triggering.rb +1 -1
  9. data/app/controllers/foreman_tasks/recurring_logics_controller.rb +7 -0
  10. data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +3 -3
  11. data/app/models/foreman_tasks/recurring_logic.rb +4 -4
  12. data/app/models/foreman_tasks/task.rb +11 -0
  13. data/app/models/foreman_tasks/task/dynflow_task.rb +27 -33
  14. data/app/models/foreman_tasks/task/status_explicator.rb +1 -1
  15. data/app/models/foreman_tasks/triggering.rb +1 -1
  16. data/app/models/setting/foreman_tasks.rb +1 -1
  17. data/app/views/foreman_tasks/api/tasks/index.json.rabl +2 -0
  18. data/app/views/foreman_tasks/recurring_logics/index.html.erb +3 -1
  19. data/config/routes.rb +2 -1
  20. data/db/migrate/20200517215015_rename_bookmarks_controller.rb +2 -2
  21. data/db/seeds.d/30-notification_blueprints.rb +7 -7
  22. data/db/seeds.d/61-foreman_tasks_bookmarks.rb +1 -1
  23. data/lib/foreman_tasks/cleaner.rb +4 -6
  24. data/lib/foreman_tasks/dynflow/configuration.rb +1 -1
  25. data/lib/foreman_tasks/dynflow/persistence.rb +4 -6
  26. data/lib/foreman_tasks/engine.rb +2 -2
  27. data/lib/foreman_tasks/version.rb +1 -1
  28. data/package.json +0 -1
  29. data/test/controllers/api/recurring_logics_controller_test.rb +1 -1
  30. data/test/controllers/api/tasks_controller_test.rb +7 -7
  31. data/test/controllers/tasks_controller_test.rb +6 -6
  32. data/test/core/unit/runner_test.rb +20 -20
  33. data/test/core/unit/task_launcher_test.rb +8 -8
  34. data/test/helpers/foreman_tasks/foreman_tasks_helper_test.rb +7 -7
  35. data/test/helpers/foreman_tasks/tasks_helper_test.rb +3 -3
  36. data/test/lib/actions/middleware/keep_current_request_id_test.rb +3 -3
  37. data/test/support/history_tasks_builder.rb +1 -1
  38. data/test/tasks/generate_task_actions_test.rb +1 -1
  39. data/test/unit/actions/action_with_sub_plans_test.rb +2 -2
  40. data/test/unit/actions/bulk_action_test.rb +6 -6
  41. data/test/unit/actions/proxy_action_test.rb +20 -20
  42. data/test/unit/actions/recurring_action_test.rb +30 -32
  43. data/test/unit/cleaner_test.rb +24 -24
  44. data/test/unit/dashboard_table_filter_test.rb +5 -5
  45. data/test/unit/otp_manager_test.rb +2 -2
  46. data/test/unit/proxy_selector_test.rb +9 -9
  47. data/test/unit/recurring_logic_test.rb +32 -38
  48. data/test/unit/remote_task_test.rb +2 -2
  49. data/test/unit/task_groups_test.rb +4 -4
  50. data/test/unit/task_test.rb +18 -18
  51. data/test/unit/triggering_test.rb +8 -8
  52. data/test/unit/troubleshooting_help_generator_test.rb +6 -6
  53. data/test/unit/ui_notifications_test.rb +11 -11
  54. data/webpack/ForemanTasks/Components/TaskDetails/Components/RunningSteps.js +3 -3
  55. data/webpack/ForemanTasks/Components/TaskDetails/Components/Task.js +8 -157
  56. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskButtons.js +168 -0
  57. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskInfo.js +6 -7
  58. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskSkeleton.js +48 -0
  59. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/RunningSteps.test.js +1 -1
  60. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Task.test.js +12 -70
  61. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskButtons.test.js +95 -0
  62. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Task.test.js.snap +78 -225
  63. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskButtons.test.js.snap +212 -0
  64. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskInfo.test.js.snap +8 -4
  65. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.js +87 -70
  66. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsActions.js +48 -125
  67. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsConstants.js +3 -16
  68. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +55 -29
  69. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetails.fixtures.js +2 -2
  70. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetails.test.js +6 -0
  71. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetailsActions.test.js +2 -18
  72. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetails.test.js.snap +77 -27
  73. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetailsActions.test.js.snap +14 -101
  74. data/webpack/ForemanTasks/Components/TaskDetails/index.js +6 -3
  75. data/webpack/ForemanTasks/Components/common/urlHelpers.js +7 -0
  76. data/webpack/ForemanTasks/ForemanTasksReducers.js +0 -2
  77. data/webpack/__mocks__/foremanReact/common/helpers.js +2 -0
  78. data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +10 -0
  79. data/webpack/__mocks__/foremanReact/redux/API/index.js +10 -0
  80. data/webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware.js +5 -0
  81. metadata +17 -14
  82. data/.travis.yml +0 -7
  83. data/script/travis_run_js_tests.sh +0 -7
  84. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsReducer.js +0 -38
  85. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetailsReducer.test.js +0 -33
  86. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetailsReducer.test.js.snap +0 -26
  87. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/integration.test.js.snap +0 -122
  88. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/integration.test.js +0 -72
  89. data/webpack/__mocks__/foremanReact/redux/API.js +0 -7
@@ -29,8 +29,8 @@ module ForemanTasks
29
29
 
30
30
  it 'generates html from the main action troubleshooting_info' do
31
31
  generated_html = subject.generate_html
32
- generated_html.must_include "A paused task represents a process that has not finished properly"
33
- generated_html.must_include %(See <a href="#{expected_troubleshooting_url}">troubleshooting documentation</a> for more details on how to resolve the issue)
32
+ _(generated_html).must_include "A paused task represents a process that has not finished properly"
33
+ _(generated_html).must_include %(See <a href="#{expected_troubleshooting_url}">troubleshooting documentation</a> for more details on how to resolve the issue)
34
34
  end
35
35
 
36
36
  it 'exposes link details' do
@@ -48,10 +48,10 @@ module ForemanTasks
48
48
 
49
49
  it 'includes additional description in generated html' do
50
50
  generated_html = subject.generate_html
51
- generated_html.must_include 'A paused task represents a process that has not finished properly'
52
- generated_html.must_match %r{See <a href=".*">troubleshooting documentation</a> for more details on how to resolve the issue}
53
- generated_html.must_include 'This task requires special handling'
54
- generated_html.must_include 'Investigate <a href="/additional_troubleshooting_page">custom link</a> on more details for this custom error'
51
+ _(generated_html).must_include 'A paused task represents a process that has not finished properly'
52
+ _(generated_html).must_match %r{See <a href=".*">troubleshooting documentation</a> for more details on how to resolve the issue}
53
+ _(generated_html).must_include 'This task requires special handling'
54
+ _(generated_html).must_include 'Investigate <a href="/additional_troubleshooting_page">custom link</a> on more details for this custom error'
55
55
  end
56
56
 
57
57
  it 'includes additional links' do
@@ -23,11 +23,11 @@ module ForemanTasks
23
23
  it 'notifies all admins about current amount of paused tasks when some paused task occurs' do
24
24
  trigger_task
25
25
  notification = user_notifications(admin_user).first
26
- notification.message.must_equal "There is 1 paused task in the system that need attention"
26
+ _(notification.message).must_equal "There is 1 paused task in the system that need attention"
27
27
  links = notification.actions['links']
28
- links.must_include('href' => '/foreman_tasks/tasks?search=state%3Dpaused',
28
+ _(links).must_include('href' => '/foreman_tasks/tasks?search=state%3Dpaused',
29
29
  'title' => 'List of tasks')
30
- links.must_include('name' => 'troubleshooting',
30
+ _(links).must_include('name' => 'troubleshooting',
31
31
  'title' => 'Troubleshooting Documentation',
32
32
  'description' => 'See %{link} for more details on how to resolve the issue',
33
33
  'href' => "https://theforeman.org/manuals/#{SETTINGS[:version].short}/tasks_troubleshooting.html#",
@@ -37,19 +37,19 @@ module ForemanTasks
37
37
  it 'aggregates the notification when multiple tasks get paused' do
38
38
  trigger_task
39
39
  recipient1 = NotificationRecipient.find_by(user_id: admin_user)
40
- recipient1.notification.message.must_match(/1 paused task/)
40
+ _(recipient1.notification.message).must_match(/1 paused task/)
41
41
 
42
42
  new_admin_user = FactoryBot.create(:user, :admin)
43
43
 
44
44
  trigger_task
45
45
 
46
- NotificationRecipient.find_by(id: recipient1.id).must_be_nil
47
- Notification.find_by(id: recipient1.notification.id).must_be_nil
46
+ _(NotificationRecipient.find_by(id: recipient1.id)).must_be_nil
47
+ _(Notification.find_by(id: recipient1.notification.id)).must_be_nil
48
48
  recipient2 = NotificationRecipient.find_by(user_id: admin_user)
49
- recipient2.notification.message.must_match(/2 paused tasks/)
49
+ _(recipient2.notification.message).must_match(/2 paused tasks/)
50
50
 
51
51
  new_recipient = NotificationRecipient.find_by(user_id: new_admin_user)
52
- new_recipient.notification.must_equal recipient2.notification
52
+ _(new_recipient.notification).must_equal recipient2.notification
53
53
  end
54
54
  end
55
55
 
@@ -59,11 +59,11 @@ module ForemanTasks
59
59
  notifications = user_notifications(task_owner)
60
60
  assert_equal 1, notifications.size, 'Only notification for the main action should be triggered'
61
61
  notification = notifications.first
62
- notification.message.must_equal "The task 'Dummy pause action' got paused"
62
+ _(notification.message).must_equal "The task 'Dummy pause action' got paused"
63
63
  links = notification.actions['links']
64
- links.must_include("href" => "/foreman_tasks/tasks/#{task.id}",
64
+ _(links).must_include("href" => "/foreman_tasks/tasks/#{task.id}",
65
65
  "title" => "Task Details")
66
- links.must_include('name' => 'troubleshooting',
66
+ _(links).must_include('name' => 'troubleshooting',
67
67
  'title' => 'Troubleshooting Documentation',
68
68
  'description' => 'See %{link} for more details on how to resolve the issue',
69
69
  'href' => "https://theforeman.org/manuals/#{SETTINGS[:version].short}/tasks_troubleshooting.html#Support::DummyPauseAction",
@@ -8,7 +8,7 @@ const RunningSteps = ({
8
8
  id,
9
9
  cancelStep,
10
10
  taskReload,
11
- taskProgressToggle,
11
+ taskReloadStart,
12
12
  }) => {
13
13
  if (!runningSteps.length) return <span>{__('No running steps')}</span>;
14
14
  return (
@@ -21,7 +21,7 @@ const RunningSteps = ({
21
21
  bsSize="small"
22
22
  onClick={() => {
23
23
  if (!taskReload) {
24
- taskProgressToggle();
24
+ taskReloadStart(id);
25
25
  }
26
26
  cancelStep(id, step.id);
27
27
  }}
@@ -59,7 +59,7 @@ RunningSteps.propTypes = {
59
59
  id: PropTypes.string.isRequired,
60
60
  cancelStep: PropTypes.func.isRequired,
61
61
  taskReload: PropTypes.bool.isRequired,
62
- taskProgressToggle: PropTypes.func.isRequired,
62
+ taskReloadStart: PropTypes.func.isRequired,
63
63
  };
64
64
 
65
65
  RunningSteps.defaultProps = {
@@ -1,159 +1,37 @@
1
1
  import React 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 { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
2
+ import { Grid, Row } from 'patternfly-react';
6
3
  import TaskInfo from './TaskInfo';
7
- import {
8
- UNLOCK_MODAL,
9
- FORCE_UNLOCK_MODAL,
10
- } from '../../TaskActions/TaskActionsConstants';
11
4
  import { ForceUnlockModal, UnlockModal } from '../../TaskActions/UnlockModals';
5
+ import { TaskButtons } from './TaskButtons';
12
6
 
13
7
  const Task = props => {
14
- const unlockModalActions = useForemanModal({
15
- id: UNLOCK_MODAL,
16
- });
17
- const forceUnlockModalActions = useForemanModal({
18
- id: FORCE_UNLOCK_MODAL,
19
- });
20
-
21
8
  const {
22
9
  taskReload,
23
- externalId,
24
10
  id,
25
- state,
26
- resumable,
27
- cancellable,
28
- hasSubTasks,
29
- parentTask,
30
- cancelTaskRequest,
31
- resumeTaskRequest,
32
11
  forceCancelTaskRequest,
33
12
  unlockTaskRequest,
34
13
  action,
35
- dynflowEnableConsole,
36
- taskProgressToggle,
37
- canEdit,
14
+ taskReloadStart,
38
15
  } = props;
39
16
  const forceUnlock = () => {
40
17
  if (!taskReload) {
41
- taskProgressToggle();
18
+ taskReloadStart(id);
42
19
  }
43
20
  forceCancelTaskRequest(id, action);
44
21
  };
45
22
  const unlock = () => {
46
23
  if (!taskReload) {
47
- taskProgressToggle();
24
+ taskReloadStart(id);
48
25
  }
49
26
  unlockTaskRequest(id, action);
50
27
  };
51
- const editActionsTitle = canEdit
52
- ? undefined
53
- : __('You do not have permission');
54
- const dynflowTitle = dynflowEnableConsole
55
- ? undefined
56
- : `dynflow_enable_console ${__('Setting is off')}`;
57
28
  return (
58
29
  <React.Fragment>
59
30
  <UnlockModal onClick={unlock} />
60
31
  <ForceUnlockModal onClick={forceUnlock} />
61
32
  <Grid>
62
33
  <Row>
63
- <Col xs={12}>
64
- <Button
65
- className="reload-button"
66
- bsSize="small"
67
- onClick={taskProgressToggle}
68
- >
69
- <span
70
- className={`glyphicon glyphicon-refresh ${
71
- taskReload ? 'spin' : ''
72
- }`}
73
- />
74
- {__(`${taskReload ? 'Stop' : 'Start'} auto-reloading`)}
75
- </Button>
76
- <Button
77
- className="dynflow-button"
78
- bsSize="small"
79
- href={`/foreman_tasks/dynflow/${externalId}`}
80
- disabled={!dynflowEnableConsole}
81
- rel="noopener noreferrer"
82
- target="_blank"
83
- >
84
- <span title={dynflowTitle} data-original-title={dynflowTitle}>
85
- {__('Dynflow console')}
86
- </span>
87
- </Button>
88
- <Button
89
- className="resume-button"
90
- bsSize="small"
91
- title={editActionsTitle}
92
- data-original-title={editActionsTitle}
93
- disabled={!canEdit || !resumable}
94
- onClick={() => {
95
- if (!taskReload) {
96
- taskProgressToggle();
97
- }
98
- resumeTaskRequest(id, action);
99
- }}
100
- >
101
- {__('Resume')}
102
- </Button>
103
- <Button
104
- className="cancel-button"
105
- bsSize="small"
106
- title={editActionsTitle}
107
- data-original-title={editActionsTitle}
108
- disabled={!canEdit || !cancellable}
109
- onClick={() => {
110
- if (!taskReload) {
111
- taskProgressToggle();
112
- }
113
- cancelTaskRequest(id, action);
114
- }}
115
- >
116
- {__('Cancel')}
117
- </Button>
118
- {parentTask && (
119
- <Button
120
- className="parent-button"
121
- bsSize="small"
122
- href={`/foreman_tasks/tasks/${parentTask}`}
123
- >
124
- {__('Parent task')}
125
- </Button>
126
- )}
127
- {hasSubTasks && (
128
- <Button
129
- className="subtask-button"
130
- bsSize="small"
131
- href={`/foreman_tasks/tasks/${id}/sub_tasks`}
132
- >
133
- {__('Sub tasks')}
134
- </Button>
135
- )}
136
- <Button
137
- className="unlock-button"
138
- bsSize="small"
139
- disabled={!canEdit || state !== 'paused'}
140
- onClick={unlockModalActions.setModalOpen}
141
- title={editActionsTitle}
142
- data-original-title={editActionsTitle}
143
- >
144
- {__('Unlock')}
145
- </Button>
146
- <Button
147
- className="force-unlock-button"
148
- bsSize="small"
149
- disabled={!canEdit || state === 'stopped'}
150
- onClick={forceUnlockModalActions.setModalOpen}
151
- title={editActionsTitle}
152
- data-original-title={editActionsTitle}
153
- >
154
- {__('Force Unlock')}
155
- </Button>
156
- </Col>
34
+ <TaskButtons taskReloadStart={taskReloadStart} {...props} />
157
35
  </Row>
158
36
  <TaskInfo {...props} />
159
37
  </Grid>
@@ -163,39 +41,12 @@ const Task = props => {
163
41
 
164
42
  Task.propTypes = {
165
43
  ...TaskInfo.PropTypes,
166
- state: PropTypes.string,
167
- resumable: PropTypes.bool,
168
- cancellable: PropTypes.bool,
169
- refetchTaskDetails: PropTypes.func,
170
- hasSubTasks: PropTypes.bool,
171
- parentTask: PropTypes.string,
172
- taskReload: PropTypes.bool,
173
- taskReloadStop: PropTypes.func,
174
- timeoutId: PropTypes.number,
175
- externalId: PropTypes.string,
176
- id: PropTypes.string.isRequired,
177
- cancelTaskRequest: PropTypes.func,
178
- resumeTaskRequest: PropTypes.func,
179
- dynflowEnableConsole: PropTypes.bool,
180
- canEdit: PropTypes.bool,
44
+ ...TaskButtons.PropTypes,
181
45
  };
182
46
 
183
47
  Task.defaultProps = {
184
48
  ...TaskInfo.defaultProps,
185
- state: '',
186
- resumable: false,
187
- cancellable: false,
188
- refetchTaskDetails: () => null,
189
- hasSubTasks: false,
190
- parentTask: '',
191
- taskReload: false,
192
- taskReloadStop: () => null,
193
- timeoutId: null,
194
- externalId: '',
195
- cancelTaskRequest: () => null,
196
- resumeTaskRequest: () => null,
197
- dynflowEnableConsole: false,
198
- canEdit: false,
49
+ ...TaskButtons.defaultProps,
199
50
  };
200
51
 
201
52
  export default Task;
@@ -0,0 +1,168 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Col, Button } from 'patternfly-react';
4
+ import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
5
+ import { translate as __ } from 'foremanReact/common/I18n';
6
+ import {
7
+ UNLOCK_MODAL,
8
+ FORCE_UNLOCK_MODAL,
9
+ } from '../../TaskActions/TaskActionsConstants';
10
+
11
+ export const TaskButtons = ({
12
+ canEdit,
13
+ dynflowEnableConsole,
14
+ taskReloadStart,
15
+ taskProgressToggle,
16
+ taskReload,
17
+ externalId,
18
+ id,
19
+ action,
20
+ state,
21
+ resumable,
22
+ cancellable,
23
+ hasSubTasks,
24
+ parentTask,
25
+ cancelTaskRequest,
26
+ resumeTaskRequest,
27
+ }) => {
28
+ const unlockModalActions = useForemanModal({
29
+ id: UNLOCK_MODAL,
30
+ });
31
+ const forceUnlockModalActions = useForemanModal({
32
+ id: FORCE_UNLOCK_MODAL,
33
+ });
34
+ const editActionsTitle = canEdit
35
+ ? undefined
36
+ : __('You do not have permission');
37
+ const dynflowTitle = dynflowEnableConsole
38
+ ? undefined
39
+ : `dynflow_enable_console ${__('Setting is off')}`;
40
+
41
+ return (
42
+ <Col xs={12}>
43
+ <Button
44
+ className="reload-button"
45
+ bsSize="small"
46
+ onClick={taskProgressToggle}
47
+ >
48
+ <span
49
+ className={`glyphicon glyphicon-refresh ${taskReload ? 'spin' : ''}`}
50
+ />
51
+ {__(`${taskReload ? 'Stop' : 'Start'} auto-reloading`)}
52
+ </Button>
53
+ <Button
54
+ className="dynflow-button"
55
+ bsSize="small"
56
+ href={`/foreman_tasks/dynflow/${externalId}`}
57
+ disabled={!dynflowEnableConsole}
58
+ rel="noopener noreferrer"
59
+ target="_blank"
60
+ >
61
+ <span title={dynflowTitle} data-original-title={dynflowTitle}>
62
+ {__('Dynflow console')}
63
+ </span>
64
+ </Button>
65
+ <Button
66
+ className="resume-button"
67
+ bsSize="small"
68
+ title={editActionsTitle}
69
+ data-original-title={editActionsTitle}
70
+ disabled={!canEdit || !resumable}
71
+ onClick={() => {
72
+ if (!taskReload) {
73
+ taskReloadStart(id);
74
+ }
75
+ resumeTaskRequest(id, action);
76
+ }}
77
+ >
78
+ {__('Resume')}
79
+ </Button>
80
+ <Button
81
+ className="cancel-button"
82
+ bsSize="small"
83
+ title={editActionsTitle}
84
+ data-original-title={editActionsTitle}
85
+ disabled={!canEdit || !cancellable}
86
+ onClick={() => {
87
+ if (!taskReload) {
88
+ taskReloadStart(id);
89
+ }
90
+ cancelTaskRequest(id, action);
91
+ }}
92
+ >
93
+ {__('Cancel')}
94
+ </Button>
95
+ {parentTask && (
96
+ <Button
97
+ className="parent-button"
98
+ bsSize="small"
99
+ href={`/foreman_tasks/tasks/${parentTask}`}
100
+ >
101
+ {__('Parent task')}
102
+ </Button>
103
+ )}
104
+ {hasSubTasks && (
105
+ <Button
106
+ className="subtask-button"
107
+ bsSize="small"
108
+ href={`/foreman_tasks/tasks/${id}/sub_tasks`}
109
+ >
110
+ {__('Sub tasks')}
111
+ </Button>
112
+ )}
113
+ <Button
114
+ className="unlock-button"
115
+ bsSize="small"
116
+ disabled={!canEdit || state !== 'paused'}
117
+ onClick={unlockModalActions.setModalOpen}
118
+ title={editActionsTitle}
119
+ data-original-title={editActionsTitle}
120
+ >
121
+ {__('Unlock')}
122
+ </Button>
123
+ <Button
124
+ className="force-unlock-button"
125
+ bsSize="small"
126
+ disabled={!canEdit || state === 'stopped'}
127
+ onClick={forceUnlockModalActions.setModalOpen}
128
+ title={editActionsTitle}
129
+ data-original-title={editActionsTitle}
130
+ >
131
+ {__('Force Unlock')}
132
+ </Button>
133
+ </Col>
134
+ );
135
+ };
136
+
137
+ TaskButtons.propTypes = {
138
+ canEdit: PropTypes.bool,
139
+ dynflowEnableConsole: PropTypes.bool,
140
+ taskReloadStart: PropTypes.func.isRequired,
141
+ taskProgressToggle: PropTypes.func.isRequired,
142
+ taskReload: PropTypes.bool,
143
+ externalId: PropTypes.string,
144
+ id: PropTypes.string.isRequired,
145
+ action: PropTypes.string,
146
+ state: PropTypes.string,
147
+ resumable: PropTypes.bool,
148
+ cancellable: PropTypes.bool,
149
+ hasSubTasks: PropTypes.bool,
150
+ parentTask: PropTypes.string,
151
+ cancelTaskRequest: PropTypes.func,
152
+ resumeTaskRequest: PropTypes.func,
153
+ };
154
+
155
+ TaskButtons.defaultProps = {
156
+ canEdit: false,
157
+ dynflowEnableConsole: false,
158
+ taskReload: false,
159
+ externalId: '',
160
+ action: '',
161
+ state: '',
162
+ resumable: false,
163
+ cancellable: false,
164
+ hasSubTasks: false,
165
+ parentTask: '',
166
+ cancelTaskRequest: () => null,
167
+ resumeTaskRequest: () => null,
168
+ };