foreman-tasks 2.0.3 → 3.0.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.
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
+ };