foreman-tasks 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/foreman_tasks/api/tasks_controller.rb +29 -1
  3. data/app/controllers/foreman_tasks/tasks_controller.rb +3 -11
  4. data/app/models/foreman_tasks/task.rb +4 -0
  5. data/app/models/setting/foreman_tasks.rb +1 -1
  6. data/app/services/ui_notifications/tasks/task_bulk_stop.rb +36 -0
  7. data/app/views/foreman_tasks/api/tasks/details.json.rabl +0 -1
  8. data/config/routes.rb +1 -0
  9. data/db/migrate/20200517215015_rename_bookmarks_controller.rb +35 -0
  10. data/db/migrate/20200519093217_drop_dynflow_allow_dangerous_actions_setting.foreman_tasks.rb +5 -0
  11. data/db/seeds.d/30-notification_blueprints.rb +7 -0
  12. data/lib/foreman_tasks/engine.rb +1 -1
  13. data/lib/foreman_tasks/tasks/cleanup.rake +2 -2
  14. data/lib/foreman_tasks/tasks/export_tasks.rake +1 -1
  15. data/lib/foreman_tasks/version.rb +1 -1
  16. data/locale/action_names.rb +1 -1
  17. data/locale/en/foreman_tasks.po +227 -41
  18. data/locale/foreman_tasks.pot +579 -288
  19. data/webpack/ForemanTasks/Components/TaskActions/TaskAction.test.js +60 -0
  20. data/webpack/ForemanTasks/Components/{TasksTable/TasksTableActionHelpers.js → TaskActions/TaskActionHelpers.js} +21 -6
  21. data/webpack/ForemanTasks/Components/{TasksTable/__tests__/TasksTableActionHelpers.test.js → TaskActions/TaskActionHelpers.test.js} +2 -2
  22. data/webpack/ForemanTasks/Components/TaskActions/TaskActionsConstants.js +16 -0
  23. data/webpack/ForemanTasks/Components/TaskActions/UnlockModals.js +60 -0
  24. data/webpack/ForemanTasks/Components/TaskActions/UnlockModals.test.js +14 -0
  25. data/webpack/ForemanTasks/Components/TaskActions/__snapshots__/TaskAction.test.js.snap +233 -0
  26. data/webpack/ForemanTasks/Components/TaskActions/__snapshots__/UnlockModals.test.js.snap +25 -0
  27. data/webpack/ForemanTasks/Components/TaskActions/index.js +115 -0
  28. data/webpack/ForemanTasks/Components/TaskDetails/Components/Task.js +130 -152
  29. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Task.test.js +67 -3
  30. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskInfo.test.js +0 -1
  31. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Task.test.js.snap +101 -70
  32. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsActions.js +0 -15
  33. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsConstants.js +0 -5
  34. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsReducer.js +0 -6
  35. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +0 -9
  36. data/webpack/ForemanTasks/Components/TaskDetails/TasksDetailsHelper.js +6 -1
  37. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetails.test.js +5 -0
  38. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetails.test.js.snap +2 -7
  39. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/integration.test.js +4 -0
  40. data/webpack/ForemanTasks/Components/TaskDetails/index.js +4 -8
  41. data/webpack/ForemanTasks/Components/TasksTable/Components/ActionSelectButton.js +14 -1
  42. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModal.js +83 -0
  43. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModalActions.js +106 -0
  44. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModalReducer.js +38 -0
  45. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModalSelectors.js +45 -0
  46. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModal.test.js +36 -0
  47. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModalActions.test.js +205 -0
  48. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModalReducer.test.js +27 -0
  49. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModalSelectors.test.js +54 -0
  50. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/__snapshots__/ConfirmModal.test.js.snap +41 -0
  51. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/__snapshots__/ConfirmModalReducer.test.js.snap +19 -0
  52. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/__snapshots__/ConfirmModalSelectors.test.js.snap +30 -0
  53. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/index.js +29 -0
  54. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/ActionSelectButton.test.js +1 -0
  55. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/ActionSelectButton.test.js.snap +11 -0
  56. data/webpack/ForemanTasks/Components/TasksTable/TasksBulkActions.js +113 -30
  57. data/webpack/ForemanTasks/Components/TasksTable/TasksTable.js +13 -9
  58. data/webpack/ForemanTasks/Components/TasksTable/TasksTableActions.js +23 -63
  59. data/webpack/ForemanTasks/Components/TasksTable/TasksTableConstants.js +10 -12
  60. data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +24 -94
  61. data/webpack/ForemanTasks/Components/TasksTable/TasksTableSchema.js +2 -2
  62. data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +7 -4
  63. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksBulkActions.test.js +35 -0
  64. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTable.fixtures.js +2 -12
  65. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableActions.test.js +22 -26
  66. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/SubTasksPage.test.js.snap +2 -14
  67. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksBulkActions.test.js.snap +107 -0
  68. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksIndexPage.test.js.snap +2 -14
  69. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableActions.test.js.snap +17 -124
  70. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +24 -128
  71. data/webpack/ForemanTasks/Components/TasksTable/index.js +2 -2
  72. data/webpack/ForemanTasks/Components/common/ActionButtons/ActionButton.js +47 -19
  73. data/webpack/ForemanTasks/Components/common/ActionButtons/ActionButton.test.js +61 -14
  74. data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/ActionButton.test.js.snap +80 -21
  75. data/webpack/ForemanTasks/Components/common/ClickConfirmation/ClickConfirmation.scss +9 -0
  76. data/webpack/ForemanTasks/Components/common/ClickConfirmation/ClickConfirmation.test.js +44 -0
  77. data/webpack/ForemanTasks/Components/common/ClickConfirmation/__snapshots__/ClickConfirmation.test.js.snap +52 -0
  78. data/webpack/ForemanTasks/Components/common/ClickConfirmation/index.js +59 -66
  79. data/webpack/ForemanTasks/Components/common/{ToastTypesConstants.js → ToastsHelpers/ToastTypesConstants.js} +0 -0
  80. data/webpack/ForemanTasks/Components/common/ToastsHelpers/index.js +15 -0
  81. data/webpack/ForemanTasks/ForemanTasksReducers.js +2 -0
  82. data/webpack/ForemanTasks/Routes/ForemanTasksRoutes.test.js +2 -1
  83. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalHooks.js +2 -2
  84. data/webpack/__mocks__/foremanReact/components/ForemanModal/index.js +17 -3
  85. data/webpack/__mocks__/foremanReact/components/common/ActionButtons/ActionButtons.js +3 -0
  86. metadata +32 -22
  87. data/db/migrate/20200611090846_add_task_lock_index_on_resource_type_and_task_id.rb +0 -9
  88. data/lib/foreman_tasks/tasks/dynflow.rake +0 -6
  89. data/webpack/ForemanTasks/Components/TasksTable/Components/CancelConfirm.js +0 -53
  90. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmationModals.js +0 -56
  91. data/webpack/ForemanTasks/Components/TasksTable/Components/ResumeConfirm.js +0 -52
  92. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/CancelConfirm.test.js +0 -26
  93. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/ConfirmationModals.test.js +0 -24
  94. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/ResumeConfirm.test.js +0 -26
  95. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/CancelConfirm.test.js.snap +0 -65
  96. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/ConfirmationModals.test.js.snap +0 -30
  97. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/ResumeConfirm.test.js.snap +0 -63
  98. data/webpack/ForemanTasks/Components/common/ActionButtons/CancelButton.js +0 -23
  99. data/webpack/ForemanTasks/Components/common/ActionButtons/CancelButton.test.js +0 -26
  100. data/webpack/ForemanTasks/Components/common/ActionButtons/ResumeButton.js +0 -23
  101. data/webpack/ForemanTasks/Components/common/ActionButtons/ResumeButton.test.js +0 -27
  102. data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/CancelButton.test.js.snap +0 -15
  103. data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/ResumeButton.test.js.snap +0 -15
@@ -0,0 +1,205 @@
1
+ import taskActions from '../ConfirmModalActions';
2
+ import {
3
+ CANCEL_MODAL,
4
+ RESUME_MODAL,
5
+ CANCEL_SELECTED_MODAL,
6
+ RESUME_SELECTED_MODAL,
7
+ FORCE_UNLOCK_SELECTED_MODAL,
8
+ } from '../../../TasksTableConstants';
9
+
10
+ import { FORCE_UNLOCK_MODAL } from '../../../../TaskActions/TaskActionsConstants';
11
+
12
+ import {
13
+ resumeTask,
14
+ cancelTask,
15
+ forceCancelTask,
16
+ } from '../../../TasksTableActions';
17
+ import {
18
+ bulkCancelBySearch,
19
+ bulkCancelById,
20
+ bulkResumeBySearch,
21
+ bulkResumeById,
22
+ bulkForceCancelBySearch,
23
+ bulkForceCancelById,
24
+ } from '../../../TasksBulkActions';
25
+
26
+ jest.mock('../../../TasksBulkActions');
27
+ jest.mock('../../../TasksTableActions');
28
+
29
+ const bulkCancelBySearchMock = 'bulkCancelBySearchMock';
30
+ const bulkCancelByIdMock = 'bulkCancelByIdMock';
31
+ const bulkResumeBySearchMock = 'bulkResumeBySearchMock';
32
+ const bulkResumeByIdMock = 'bulkResumeByIdMock';
33
+ const bulkForceCancelBySearchMock = 'bulkForceCancelBySearchMock';
34
+ const bulkForceCancelByIdMock = 'bulkForceCancelByIdMock';
35
+ const resumeTaskMock = 'resumeTaskMock';
36
+ const cancelTaskMock = 'cancelTaskMock';
37
+ const forceCancelTaskMock = 'forceCancelTaskMock';
38
+
39
+ bulkCancelBySearch.mockImplementation(() => bulkCancelBySearchMock);
40
+ bulkCancelById.mockImplementation(() => bulkCancelByIdMock);
41
+ bulkResumeBySearch.mockImplementation(() => bulkResumeBySearchMock);
42
+ bulkResumeById.mockImplementation(() => bulkResumeByIdMock);
43
+ bulkForceCancelBySearch.mockImplementation(() => bulkForceCancelBySearchMock);
44
+ bulkForceCancelById.mockImplementation(() => bulkForceCancelByIdMock);
45
+ resumeTask.mockImplementation(() => resumeTaskMock);
46
+ cancelTask.mockImplementation(() => cancelTaskMock);
47
+ forceCancelTask.mockImplementation(() => forceCancelTaskMock);
48
+
49
+ const url = 'some-url';
50
+ const query = 'some-query';
51
+ const parentTaskID = 'some-parentTaskID';
52
+
53
+ const runWithGetState = (state, action, dispatch, ...params) => {
54
+ const getState = () => state;
55
+ action(...params)(dispatch, getState);
56
+ };
57
+
58
+ const clickedState = {
59
+ foremanTasks: {
60
+ tasksTable: {
61
+ tasksTableQuery: {
62
+ clicked: { taskId: 'some-id', taskName: 'some-name' },
63
+ },
64
+ },
65
+ },
66
+ };
67
+
68
+ const selectedState = {
69
+ foremanTasks: {
70
+ tasksTable: {
71
+ tasksTableQuery: {
72
+ allRowsSelected: false,
73
+ selectedRows: [1, 2, 3],
74
+ },
75
+ tasksTableContent: {
76
+ results: [
77
+ {
78
+ id: 1,
79
+ action: 'action1',
80
+ available_actions: { cancellable: true },
81
+ },
82
+ {
83
+ id: 2,
84
+ action: 'action2',
85
+ available_actions: { resumable: true },
86
+ },
87
+ ],
88
+ },
89
+ },
90
+ },
91
+ };
92
+ const allRowsState = {
93
+ foremanTasks: {
94
+ tasksTable: {
95
+ tasksTableQuery: {
96
+ allRowsSelected: true,
97
+ },
98
+ },
99
+ },
100
+ };
101
+ describe('ConfirmModalActions', () => {
102
+ const dispatch = jest.fn();
103
+
104
+ beforeEach(() => dispatch.mockClear());
105
+ it('run CANCEL_MODAL', () => {
106
+ runWithGetState(clickedState, taskActions[CANCEL_MODAL], dispatch, {
107
+ url,
108
+ parentTaskID,
109
+ });
110
+ expect(dispatch).toBeCalledWith(cancelTaskMock);
111
+ });
112
+ it('run RESUME_MODAL', () => {
113
+ runWithGetState(clickedState, taskActions[RESUME_MODAL], dispatch, {
114
+ url,
115
+ parentTaskID,
116
+ });
117
+ expect(dispatch).toBeCalledWith(resumeTaskMock);
118
+ });
119
+ it('run FORCE_UNLOCK_MODAL', () => {
120
+ runWithGetState(clickedState, taskActions[FORCE_UNLOCK_MODAL], dispatch, {
121
+ url,
122
+ parentTaskID,
123
+ });
124
+ expect(dispatch).toBeCalledWith(forceCancelTaskMock);
125
+ });
126
+ it('run CANCEL_SELECTED_MODAL by id', () => {
127
+ runWithGetState(
128
+ selectedState,
129
+ taskActions[CANCEL_SELECTED_MODAL],
130
+ dispatch,
131
+ {
132
+ url,
133
+ query,
134
+ parentTaskID,
135
+ }
136
+ );
137
+ expect(dispatch).toBeCalledWith(bulkCancelByIdMock);
138
+ });
139
+
140
+ it('run CANCEL_SELECTED_MODAL by search', () => {
141
+ runWithGetState(
142
+ allRowsState,
143
+ taskActions[CANCEL_SELECTED_MODAL],
144
+ dispatch,
145
+ {
146
+ url,
147
+ query,
148
+ parentTaskID,
149
+ }
150
+ );
151
+ expect(dispatch).toBeCalledWith(bulkCancelBySearchMock);
152
+ });
153
+ it('run RESUME_SELECTED_MODAL by id', () => {
154
+ runWithGetState(
155
+ selectedState,
156
+ taskActions[RESUME_SELECTED_MODAL],
157
+ dispatch,
158
+ {
159
+ url,
160
+ query,
161
+ parentTaskID,
162
+ }
163
+ );
164
+ expect(dispatch).toBeCalledWith(bulkResumeByIdMock);
165
+ });
166
+ it('run RESUME_SELECTED_MODAL by search', () => {
167
+ runWithGetState(
168
+ allRowsState,
169
+ taskActions[RESUME_SELECTED_MODAL],
170
+ dispatch,
171
+ {
172
+ url,
173
+ query,
174
+ parentTaskID,
175
+ }
176
+ );
177
+ expect(dispatch).toBeCalledWith(bulkResumeBySearchMock);
178
+ });
179
+ it('run FORCE_UNLOCK_SELECTED_MODAL by id', () => {
180
+ runWithGetState(
181
+ selectedState,
182
+ taskActions[FORCE_UNLOCK_SELECTED_MODAL],
183
+ dispatch,
184
+ {
185
+ url,
186
+ query,
187
+ parentTaskID,
188
+ }
189
+ );
190
+ expect(dispatch).toBeCalledWith(bulkForceCancelByIdMock);
191
+ });
192
+ it('run FORCE_UNLOCK_SELECTED_MODAL by search', () => {
193
+ runWithGetState(
194
+ allRowsState,
195
+ taskActions[FORCE_UNLOCK_SELECTED_MODAL],
196
+ dispatch,
197
+ {
198
+ url,
199
+ query,
200
+ parentTaskID,
201
+ }
202
+ );
203
+ expect(dispatch).toBeCalledWith(bulkForceCancelBySearchMock);
204
+ });
205
+ });
@@ -0,0 +1,27 @@
1
+ import { testReducerSnapshotWithFixtures } from '@theforeman/test';
2
+ import {
3
+ UPDATE_MODAL,
4
+ CANCEL_MODAL,
5
+ RESUME_SELECTED_MODAL,
6
+ } from '../../../TasksTableConstants';
7
+
8
+ import reducer from '../ConfirmModalReducer';
9
+
10
+ const fixtures = {
11
+ 'should return the initial state': {},
12
+ 'should handle UPDATE_MODAL to cancel': {
13
+ action: {
14
+ type: UPDATE_MODAL,
15
+ payload: { modalID: CANCEL_MODAL },
16
+ },
17
+ },
18
+ 'should handle UPDATE_MODAL to resume': {
19
+ action: {
20
+ type: UPDATE_MODAL,
21
+ payload: { modalID: RESUME_SELECTED_MODAL },
22
+ },
23
+ },
24
+ };
25
+
26
+ describe('ConfirmModalReducer reducer', () =>
27
+ testReducerSnapshotWithFixtures(reducer, fixtures));
@@ -0,0 +1,54 @@
1
+ import { testSelectorsSnapshotWithFixtures } from '@theforeman/test';
2
+ import {
3
+ selectActionText,
4
+ selectActionState,
5
+ selectActionType,
6
+ selectSelectedTasks,
7
+ selectSelectedRowsLen,
8
+ } from '../ConfirmModalSelectors';
9
+ import { CANCEL_MODAL } from '../../../TasksTableConstants';
10
+
11
+ const state = {
12
+ foremanTasks: {
13
+ confirmModal: {
14
+ actionText: 'some-text',
15
+ actionState: 'some-state',
16
+ actionType: 'some-type',
17
+ },
18
+ tasksTable: {
19
+ tasksTableContent: {
20
+ results: [
21
+ {
22
+ id: 1,
23
+ action: 'action1',
24
+ available_actions: { cancellable: true },
25
+ },
26
+ { id: 2, action: 'action2', available_actions: { resumable: true } },
27
+ ],
28
+ itemCount: 10,
29
+ },
30
+ tasksTableQuery: { selectedRows: [1, 2, 3] },
31
+ },
32
+ },
33
+ };
34
+
35
+ const fixtures = {
36
+ 'should select actionText': () => selectActionText(state),
37
+ 'should select actionState': () => selectActionState(state),
38
+ 'should select actionType': () => selectActionType(state),
39
+ 'should select selectedTasks': () => selectSelectedTasks(state),
40
+ 'should select selectedRowsLen 1': () =>
41
+ selectSelectedRowsLen({
42
+ ...state,
43
+ foremanTasks: { confirmModal: { actionType: CANCEL_MODAL } },
44
+ }),
45
+ 'should select selectedRowsLen all': () => selectSelectedRowsLen(state),
46
+ 'should select selectedRowsLen some': () =>
47
+ selectSelectedRowsLen({
48
+ ...state,
49
+ tasksTable: { tasksTableQuery: { allRowsSelected: true } },
50
+ }),
51
+ };
52
+
53
+ describe('TasksDashboard - Selectors', () =>
54
+ testSelectorsSnapshotWithFixtures(fixtures));
@@ -0,0 +1,41 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ConfirmModal renders ConfirmModal 1`] = `
4
+ <ForemanModal
5
+ id="modalID"
6
+ title="Some Text Selected Tasks"
7
+ >
8
+ This will some text 1 task(s), putting them in the some state state. Are you sure?
9
+ <Component>
10
+ <Button
11
+ active={false}
12
+ block={false}
13
+ bsClass="btn"
14
+ bsStyle="default"
15
+ disabled={false}
16
+ onClick={[MockFunction]}
17
+ >
18
+ No
19
+ </Button>
20
+ <Button
21
+ active={false}
22
+ block={false}
23
+ bsClass="btn"
24
+ bsStyle="primary"
25
+ className="confirm-button"
26
+ disabled={false}
27
+ onClick={[Function]}
28
+ >
29
+ Yes
30
+ </Button>
31
+ </Component>
32
+ </ForemanModal>
33
+ `;
34
+
35
+ exports[`ConfirmModal renders ConfirmModal for unlock 1`] = `
36
+ <ForceUnlockModal
37
+ id="modalID"
38
+ onClick={[Function]}
39
+ selectedRowsLen={1}
40
+ />
41
+ `;
@@ -0,0 +1,19 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ConfirmModalReducer reducer should handle UPDATE_MODAL to cancel 1`] = `
4
+ Object {
5
+ "actionState": "stopped",
6
+ "actionText": "cancel",
7
+ "actionType": "cancelConfirmModal",
8
+ }
9
+ `;
10
+
11
+ exports[`ConfirmModalReducer reducer should handle UPDATE_MODAL to resume 1`] = `
12
+ Object {
13
+ "actionState": "running",
14
+ "actionText": "resume",
15
+ "actionType": "resumeSelectedConfirmModal",
16
+ }
17
+ `;
18
+
19
+ exports[`ConfirmModalReducer reducer should return the initial state 1`] = `Object {}`;
@@ -0,0 +1,30 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`TasksDashboard - Selectors should select actionState 1`] = `"some-state"`;
4
+
5
+ exports[`TasksDashboard - Selectors should select actionText 1`] = `"some-text"`;
6
+
7
+ exports[`TasksDashboard - Selectors should select actionType 1`] = `"some-type"`;
8
+
9
+ exports[`TasksDashboard - Selectors should select selectedRowsLen 1 1`] = `1`;
10
+
11
+ exports[`TasksDashboard - Selectors should select selectedRowsLen all 1`] = `3`;
12
+
13
+ exports[`TasksDashboard - Selectors should select selectedRowsLen some 1`] = `3`;
14
+
15
+ exports[`TasksDashboard - Selectors should select selectedTasks 1`] = `
16
+ Array [
17
+ Object {
18
+ "id": 1,
19
+ "isCancellable": true,
20
+ "isResumable": undefined,
21
+ "name": "action1",
22
+ },
23
+ Object {
24
+ "id": 2,
25
+ "isCancellable": undefined,
26
+ "isResumable": true,
27
+ "name": "action2",
28
+ },
29
+ ]
30
+ `;
@@ -0,0 +1,29 @@
1
+ import { connect } from 'react-redux';
2
+ import { bindActionCreators } from 'redux';
3
+ import { ConfirmModal } from './ConfirmModal';
4
+ import reducer from './ConfirmModalReducer';
5
+ import tasksActions from './ConfirmModalActions';
6
+ import {
7
+ selectActionText,
8
+ selectActionState,
9
+ selectActionType,
10
+ selectClicked,
11
+ selectSelectedRowsLen,
12
+ } from './ConfirmModalSelectors';
13
+ import { selectAllRowsSelected } from '../../TasksTableSelectors';
14
+
15
+ const mapStateToProps = state => ({
16
+ actionText: selectActionText(state),
17
+ actionType: selectActionType(state),
18
+ actionState: selectActionState(state),
19
+ allRowsSelected: selectAllRowsSelected(state),
20
+ clicked: selectClicked(state),
21
+ selectedRowsLen: selectSelectedRowsLen(state),
22
+ });
23
+
24
+ const mapDispatchToProps = dispatch =>
25
+ bindActionCreators(tasksActions, dispatch);
26
+
27
+ export const reducers = { confirmModal: reducer };
28
+
29
+ export default connect(mapStateToProps, mapDispatchToProps)(ConfirmModal);
@@ -6,6 +6,7 @@ const fixtures = {
6
6
  'renders with minimal props': {
7
7
  onCancel: jest.fn(),
8
8
  onResume: jest.fn(),
9
+ onForceCancel: jest.fn(),
9
10
  },
10
11
  };
11
12
 
@@ -28,5 +28,16 @@ exports[`ActionSelectButton renders with minimal props 1`] = `
28
28
  >
29
29
  Resume Selected
30
30
  </MenuItem>
31
+ <MenuItem
32
+ bsClass="dropdown"
33
+ disabled={false}
34
+ divider={false}
35
+ eventKey="3"
36
+ header={false}
37
+ onClick={[MockFunction]}
38
+ title="Force Cancel selected tasks"
39
+ >
40
+ Force Cancel Selected
41
+ </MenuItem>
31
42
  </DropdownButton>
32
43
  `;
@@ -1,7 +1,11 @@
1
1
  import API from 'foremanReact/API';
2
2
  import { addToast } from 'foremanReact/redux/actions/toasts';
3
- import { translate as __ } from 'foremanReact/common/I18n';
4
- import { TOAST_TYPES } from '../common/ToastTypesConstants';
3
+ import { translate as __, sprintf } from 'foremanReact/common/I18n';
4
+ import {
5
+ BULK_CANCEL_PATH,
6
+ BULK_RESUME_PATH,
7
+ BULK_FORCE_CANCEL_PATH,
8
+ } from './TasksTableConstants';
5
9
  import {
6
10
  TASKS_RESUME_REQUEST,
7
11
  TASKS_RESUME_SUCCESS,
@@ -9,16 +13,23 @@ import {
9
13
  TASKS_CANCEL_REQUEST,
10
14
  TASKS_CANCEL_SUCCESS,
11
15
  TASKS_CANCEL_FAILURE,
12
- BULK_CANCEL_PATH,
13
- BULK_RESUME_PATH,
14
- } from './TasksTableConstants';
16
+ TASKS_FORCE_CANCEL_REQUEST,
17
+ TASKS_FORCE_CANCEL_SUCCESS,
18
+ TASKS_FORCE_CANCEL_FAILURE,
19
+ } from '../TaskActions/TaskActionsConstants';
15
20
  import { reloadPage } from './TasksTableActions';
16
21
  import {
17
22
  convertDashboardQuery,
18
23
  resumeToastInfo,
19
24
  cancelToastInfo,
20
25
  toastDispatch,
21
- } from './TasksTableActionHelpers';
26
+ } from '../TaskActions/TaskActionHelpers';
27
+ import {
28
+ successToastData,
29
+ errorToastData,
30
+ warningToastData,
31
+ infoToastData,
32
+ } from '../common/ToastsHelpers';
22
33
 
23
34
  export const bulkByIdRequest = (resumeTasks, path) => {
24
35
  const ids = resumeTasks.map(task => task.id);
@@ -41,10 +52,9 @@ export const bulkBySearchRequest = ({ query, parentTaskID, path }) => {
41
52
  const handleErrorResume = (error, dispatch) => {
42
53
  dispatch({ type: TASKS_RESUME_FAILURE, error });
43
54
  dispatch(
44
- addToast({
45
- type: TOAST_TYPES.ERROR,
46
- message: `${__(`Cannot resume tasks at the moment`)} ${error}`,
47
- })
55
+ addToast(
56
+ errorToastData(`${__(`Cannot resume tasks at the moment`)} ${error}`)
57
+ )
48
58
  );
49
59
  };
50
60
 
@@ -56,10 +66,9 @@ export const bulkResumeById = ({
56
66
  const resumeTasks = selected.filter(task => task.isResumable);
57
67
  if (resumeTasks.length < selected.length)
58
68
  dispatch(
59
- addToast({
60
- type: TOAST_TYPES.WARNING,
61
- message: __('Not all the selected tasks can be resumed'),
62
- })
69
+ addToast(
70
+ warningToastData(__('Not all the selected tasks can be resumed'))
71
+ )
63
72
  );
64
73
  if (resumeTasks.length) {
65
74
  dispatch({ type: TASKS_RESUME_REQUEST });
@@ -92,10 +101,9 @@ export const bulkResumeBySearch = ({
92
101
  }) => async dispatch => {
93
102
  dispatch({ type: TASKS_RESUME_REQUEST });
94
103
  dispatch(
95
- addToast({
96
- type: 'info',
97
- message: __('Resuming selected tasks, this might take a while'),
98
- })
104
+ addToast(
105
+ infoToastData(__('Resuming selected tasks, this might take a while'))
106
+ )
99
107
  );
100
108
  await bulkBySearchRequest({ query, path: BULK_RESUME_PATH, parentTaskID });
101
109
  };
@@ -103,10 +111,9 @@ export const bulkResumeBySearch = ({
103
111
  const handleErrorCancel = (error, dispatch) => {
104
112
  dispatch({ type: TASKS_CANCEL_FAILURE, error });
105
113
  dispatch(
106
- addToast({
107
- type: TOAST_TYPES.ERROR,
108
- message: `${__(`Cannot cancel tasks at the moment`)} ${error}`,
109
- })
114
+ addToast(
115
+ errorToastData(`${__(`Cannot cancel tasks at the moment`)} ${error}`)
116
+ )
110
117
  );
111
118
  };
112
119
 
@@ -116,10 +123,9 @@ export const bulkCancelBySearch = ({
116
123
  }) => async dispatch => {
117
124
  dispatch({ type: TASKS_CANCEL_REQUEST });
118
125
  dispatch(
119
- addToast({
120
- type: 'info',
121
- message: __('Canceling selected tasks, this might take a while'),
122
- })
126
+ addToast(
127
+ infoToastData(__('Canceling selected tasks, this might take a while'))
128
+ )
123
129
  );
124
130
  await bulkBySearchRequest({ query, path: BULK_CANCEL_PATH, parentTaskID });
125
131
  };
@@ -132,10 +138,9 @@ export const bulkCancelById = ({
132
138
  const cancelTasks = selected.filter(task => task.isCancellable);
133
139
  if (cancelTasks.length < selected.length)
134
140
  dispatch(
135
- addToast({
136
- type: TOAST_TYPES.WARNING,
137
- message: __('Not all the selected tasks can be cancelled'),
138
- })
141
+ addToast(
142
+ warningToastData(__('Not all the selected tasks can be cancelled'))
143
+ )
139
144
  );
140
145
  if (cancelTasks.length) {
141
146
  dispatch({ type: TASKS_CANCEL_REQUEST });
@@ -162,3 +167,81 @@ export const bulkCancelById = ({
162
167
  }
163
168
  }
164
169
  };
170
+
171
+ const handleErrorForceCancel = (error, dispatch) => {
172
+ dispatch({ type: TASKS_FORCE_CANCEL_FAILURE, error });
173
+ dispatch(
174
+ addToast(
175
+ errorToastData(
176
+ `${__(`Cannot force cancel tasks at the moment`)} ${error}`
177
+ )
178
+ )
179
+ );
180
+ };
181
+
182
+ export const bulkForceCancelById = ({
183
+ selected,
184
+ url,
185
+ parentTaskID,
186
+ }) => async dispatch => {
187
+ const stopTasks = selected.filter(task => task.state !== 'stopped');
188
+ if (stopTasks.length < selected.length)
189
+ dispatch(
190
+ addToast(
191
+ warningToastData(
192
+ sprintf(
193
+ '%s task(s) are already stopped',
194
+ selected.length - stopTasks.length
195
+ )
196
+ )
197
+ )
198
+ );
199
+ if (stopTasks.length > 0) {
200
+ dispatch({ type: TASKS_FORCE_CANCEL_REQUEST });
201
+ try {
202
+ const { data } = await bulkByIdRequest(stopTasks, BULK_FORCE_CANCEL_PATH);
203
+ dispatch({ type: TASKS_FORCE_CANCEL_SUCCESS });
204
+ if (data.stopped_length) {
205
+ dispatch(
206
+ addToast(
207
+ successToastData(
208
+ sprintf('%s task(s) cancelled with force', data.stopped_length)
209
+ )
210
+ )
211
+ );
212
+ }
213
+ if (data.skipped_length > 0)
214
+ dispatch(
215
+ addToast(
216
+ warningToastData(
217
+ sprintf('%s task(s) are already stopped', data.skipped_length)
218
+ )
219
+ )
220
+ );
221
+ if (data.stopped_length > 0) {
222
+ reloadPage(url, parentTaskID, dispatch);
223
+ }
224
+ } catch (error) {
225
+ handleErrorForceCancel(error, dispatch);
226
+ }
227
+ }
228
+ };
229
+
230
+ export const bulkForceCancelBySearch = ({
231
+ query,
232
+ parentTaskID,
233
+ }) => async dispatch => {
234
+ dispatch({ type: TASKS_FORCE_CANCEL_REQUEST });
235
+ dispatch(
236
+ addToast(
237
+ infoToastData(
238
+ __('Canceling with force selected tasks, this might take a while')
239
+ )
240
+ )
241
+ );
242
+ await bulkBySearchRequest({
243
+ query,
244
+ path: BULK_FORCE_CANCEL_PATH,
245
+ parentTaskID,
246
+ });
247
+ };