foreman-tasks 2.0.1 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/app/controllers/foreman_tasks/api/tasks_controller.rb +4 -0
- data/app/lib/actions/proxy_action.rb +1 -1
- data/app/models/foreman_tasks/task/dynflow_task.rb +2 -1
- data/app/views/foreman_tasks/api/tasks/show.json.rabl +2 -0
- data/db/migrate/20200611090846_add_task_lock_index_on_resource_type_and_task_id.rb +3 -3
- data/lib/foreman_tasks/tasks/cleanup.rake +2 -2
- data/lib/foreman_tasks/tasks/dynflow.rake +6 -0
- data/lib/foreman_tasks/tasks/export_tasks.rake +1 -1
- data/lib/foreman_tasks/version.rb +1 -1
- data/package.json +1 -0
- data/script/npm_link_foreman_js.sh +26 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/Task.js +24 -5
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Task.test.js +1 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Task.test.js.snap +20 -3
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.scss +3 -14
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsActions.js +6 -2
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +4 -1
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetails.test.js.snap +1 -0
- data/webpack/ForemanTasks/Components/TaskDetails/index.js +2 -0
- data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModalSelectors.js +1 -0
- data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModalSelectors.test.js +1 -0
- data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/__snapshots__/ConfirmModalSelectors.test.js.snap +2 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksBulkActions.js +24 -7
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableActions.js +3 -3
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +6 -2
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +1 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksBulkActions.test.js +13 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTable.fixtures.js +1 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTablePage.test.js +2 -1
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/SubTasksPage.test.js.snap +1 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksBulkActions.test.js.snap +48 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksIndexPage.test.js.snap +1 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +39 -5
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/actionCellFormatter.test.js.snap +1 -0
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/selectionCellFormatter.test.js.snap +2 -0
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/actionCellFormatter.test.js +1 -1
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/selectionCellFormatter.test.js +1 -1
- data/webpack/ForemanTasks/Components/TasksTable/formatters/actionCellFormatter.js +10 -7
- data/webpack/ForemanTasks/Components/TasksTable/formatters/selectionCellFormatter.js +7 -0
- data/webpack/ForemanTasks/Components/common/ActionButtons/ActionButton.js +39 -31
- data/webpack/ForemanTasks/Components/common/ActionButtons/ActionButton.test.js +17 -8
- data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/ActionButton.test.js.snap +8 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62a10715bd5b194c1cb750c2f100b60a2f3bca155e0df6d303b284cf157a87d9
|
4
|
+
data.tar.gz: fd24e269912731dd0e2cee07e99e694dbf98547cab3a3d3dd89963f87d2dc3b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 011ae333df38537c17b1593a06f41828aa600e45d653c4231644b7d86e0a0ea92016de9d36abb067c432eb4e04466bec72e9ceb9f6ad87be3142b9896d7f18e4
|
7
|
+
data.tar.gz: 0c06ac795bc95787682262e42e8330b5b1e10d0923bc68581ba19623be01cac63ab6bab70d67a168a51c61b2f34f383f19c95276eee9fd564fb7a8c32edec16f
|
data/.travis.yml
CHANGED
@@ -315,6 +315,10 @@ module ForemanTasks
|
|
315
315
|
@resource_scope ||= ForemanTasks::Task.authorized("#{action_permission}_foreman_tasks")
|
316
316
|
end
|
317
317
|
|
318
|
+
def controller_permission
|
319
|
+
'foreman_tasks'
|
320
|
+
end
|
321
|
+
|
318
322
|
def action_permission
|
319
323
|
case params[:action]
|
320
324
|
when 'bulk_search', 'summary', 'details', 'sub_tasks'
|
@@ -257,7 +257,7 @@ module Actions
|
|
257
257
|
if failed_proxy_tasks.count < options[:retry_count]
|
258
258
|
suspend do |suspended_action|
|
259
259
|
@world.clock.ping suspended_action,
|
260
|
-
Time.
|
260
|
+
Time.now.getlocal + options[:retry_interval],
|
261
261
|
event
|
262
262
|
end
|
263
263
|
else
|
@@ -2,8 +2,8 @@ class AddTaskLockIndexOnResourceTypeAndTaskId < ActiveRecord::Migration[6.0]
|
|
2
2
|
def change
|
3
3
|
add_index :foreman_tasks_locks, [:task_id, :resource_type, :resource_id], name: 'index_tasks_locks_on_task_id_resource_type_and_resource_id'
|
4
4
|
# These indexes are not needed as they can be gained from partial index lookups
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
[:task_id, :name, :resource_type].each do |index|
|
6
|
+
remove_index :foreman_tasks_locks, index if index_exists?(:foreman_tasks_locks, index)
|
7
|
+
end
|
8
8
|
end
|
9
9
|
end
|
@@ -14,7 +14,7 @@ namespace :foreman_tasks do
|
|
14
14
|
If TASK_SEARCH is set then AFTER, STATES can be set and it's used for cleanup. If TASK_SEARCH is not set then
|
15
15
|
the cleanup respects the configuration file and setting AFTER or STATES will throw exception.
|
16
16
|
DESC
|
17
|
-
task :run => 'environment' do
|
17
|
+
task :run => ['environment', 'dynflow:client'] do
|
18
18
|
options = {}
|
19
19
|
|
20
20
|
options[:filter] = ENV['TASK_SEARCH'] if ENV['TASK_SEARCH']
|
@@ -38,7 +38,7 @@ namespace :foreman_tasks do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
desc 'Show the current configuration for auto-cleanup'
|
41
|
-
task :config => 'environment' do
|
41
|
+
task :config => ['environment', 'dynflow:client'] do
|
42
42
|
if ForemanTasks::Cleaner.cleanup_settings[:after]
|
43
43
|
puts _('The tasks will be deleted after %{after}') % { :after => ForemanTasks::Cleaner.cleanup_settings[:after] }
|
44
44
|
else
|
@@ -19,7 +19,7 @@ namespace :foreman_tasks do
|
|
19
19
|
all unsuccessful tasks in the past 60 days. The default TASK_FORMAT is html
|
20
20
|
which requires a tar.gz file extension.
|
21
21
|
DESC
|
22
|
-
task :export_tasks => :environment do
|
22
|
+
task :export_tasks => [:environment, 'dynflow:client'] do
|
23
23
|
deprecated_options = { :tasks => 'TASK_SEARCH',
|
24
24
|
:days => 'TASK_DAYS',
|
25
25
|
:export => 'TASK_FILE' }
|
data/package.json
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# This script replace the npm installation of `foreman-js`
|
4
|
+
# with your local version. Usefull when developing `foreman-js`
|
5
|
+
# Read more about foreman-js: https://github.com/theforeman/foreman-js
|
6
|
+
#
|
7
|
+
# This script designed to run using `npm run foreman-js:link` in foreman root
|
8
|
+
|
9
|
+
set -e
|
10
|
+
|
11
|
+
if [[ -z "${FOREMAN_JS_LOCATION}" ]]; then # FOREMAN_JS_LOCATION is empty
|
12
|
+
FOREMAN_JS_LOCATION="../foreman-js"
|
13
|
+
echo "FOREMAN_JS_LOCATION is not defined, using \"${FOREMAN_JS_LOCATION}\" instead"
|
14
|
+
elif [ ! -d "${FOREMAN_JS_LOCATION}" ]; then
|
15
|
+
echo "Can't find folder ${FOREMAN_JS_LOCATION}"
|
16
|
+
exit 1
|
17
|
+
fi
|
18
|
+
|
19
|
+
FOREMAN_JS_LOCATION="../${FOREMAN_JS_LOCATION}"
|
20
|
+
FOREMAN_JS_PACKAGES_LOCATION="${FOREMAN_JS_LOCATION}/packages"
|
21
|
+
FOREMAN_JS_INSTALL_LOCATION="./node_modules/@theforeman"
|
22
|
+
|
23
|
+
set -x
|
24
|
+
|
25
|
+
rm -rf $FOREMAN_JS_INSTALL_LOCATION
|
26
|
+
ln -s $FOREMAN_JS_PACKAGES_LOCATION $FOREMAN_JS_INSTALL_LOCATION
|
@@ -34,6 +34,7 @@ const Task = props => {
|
|
34
34
|
action,
|
35
35
|
dynflowEnableConsole,
|
36
36
|
taskProgressToggle,
|
37
|
+
canEdit,
|
37
38
|
} = props;
|
38
39
|
const forceUnlock = () => {
|
39
40
|
if (!taskReload) {
|
@@ -47,6 +48,12 @@ const Task = props => {
|
|
47
48
|
}
|
48
49
|
unlockTaskRequest(id, action);
|
49
50
|
};
|
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')}`;
|
50
57
|
return (
|
51
58
|
<React.Fragment>
|
52
59
|
<UnlockModal onClick={unlock} />
|
@@ -74,12 +81,16 @@ const Task = props => {
|
|
74
81
|
rel="noopener noreferrer"
|
75
82
|
target="_blank"
|
76
83
|
>
|
77
|
-
{
|
84
|
+
<span title={dynflowTitle} data-original-title={dynflowTitle}>
|
85
|
+
{__('Dynflow console')}
|
86
|
+
</span>
|
78
87
|
</Button>
|
79
88
|
<Button
|
80
89
|
className="resume-button"
|
81
90
|
bsSize="small"
|
82
|
-
|
91
|
+
title={editActionsTitle}
|
92
|
+
data-original-title={editActionsTitle}
|
93
|
+
disabled={!canEdit || !resumable}
|
83
94
|
onClick={() => {
|
84
95
|
if (!taskReload) {
|
85
96
|
taskProgressToggle();
|
@@ -92,7 +103,9 @@ const Task = props => {
|
|
92
103
|
<Button
|
93
104
|
className="cancel-button"
|
94
105
|
bsSize="small"
|
95
|
-
|
106
|
+
title={editActionsTitle}
|
107
|
+
data-original-title={editActionsTitle}
|
108
|
+
disabled={!canEdit || !cancellable}
|
96
109
|
onClick={() => {
|
97
110
|
if (!taskReload) {
|
98
111
|
taskProgressToggle();
|
@@ -123,16 +136,20 @@ const Task = props => {
|
|
123
136
|
<Button
|
124
137
|
className="unlock-button"
|
125
138
|
bsSize="small"
|
126
|
-
disabled={state !== 'paused'}
|
139
|
+
disabled={!canEdit || state !== 'paused'}
|
127
140
|
onClick={unlockModalActions.setModalOpen}
|
141
|
+
title={editActionsTitle}
|
142
|
+
data-original-title={editActionsTitle}
|
128
143
|
>
|
129
144
|
{__('Unlock')}
|
130
145
|
</Button>
|
131
146
|
<Button
|
132
147
|
className="force-unlock-button"
|
133
148
|
bsSize="small"
|
134
|
-
disabled={state === 'stopped'}
|
149
|
+
disabled={!canEdit || state === 'stopped'}
|
135
150
|
onClick={forceUnlockModalActions.setModalOpen}
|
151
|
+
title={editActionsTitle}
|
152
|
+
data-original-title={editActionsTitle}
|
136
153
|
>
|
137
154
|
{__('Force Unlock')}
|
138
155
|
</Button>
|
@@ -160,6 +177,7 @@ Task.propTypes = {
|
|
160
177
|
cancelTaskRequest: PropTypes.func,
|
161
178
|
resumeTaskRequest: PropTypes.func,
|
162
179
|
dynflowEnableConsole: PropTypes.bool,
|
180
|
+
canEdit: PropTypes.bool,
|
163
181
|
};
|
164
182
|
|
165
183
|
Task.defaultProps = {
|
@@ -177,6 +195,7 @@ Task.defaultProps = {
|
|
177
195
|
cancelTaskRequest: () => null,
|
178
196
|
resumeTaskRequest: () => null,
|
179
197
|
dynflowEnableConsole: false,
|
198
|
+
canEdit: false,
|
180
199
|
};
|
181
200
|
|
182
201
|
export default Task;
|
@@ -51,7 +51,9 @@ exports[`Task rendering render with some Props 1`] = `
|
|
51
51
|
rel="noopener noreferrer"
|
52
52
|
target="_blank"
|
53
53
|
>
|
54
|
-
|
54
|
+
<span>
|
55
|
+
Dynflow console
|
56
|
+
</span>
|
55
57
|
</Button>
|
56
58
|
<Button
|
57
59
|
active={false}
|
@@ -129,6 +131,7 @@ exports[`Task rendering render with some Props 1`] = `
|
|
129
131
|
</Row>
|
130
132
|
<TaskInfo
|
131
133
|
action=""
|
134
|
+
canEdit={true}
|
132
135
|
cancelTaskRequest={[Function]}
|
133
136
|
cancellable={false}
|
134
137
|
dynflowEnableConsole={true}
|
@@ -210,7 +213,12 @@ exports[`Task rendering render without Props 1`] = `
|
|
210
213
|
rel="noopener noreferrer"
|
211
214
|
target="_blank"
|
212
215
|
>
|
213
|
-
|
216
|
+
<span
|
217
|
+
data-original-title="dynflow_enable_console Setting is off"
|
218
|
+
title="dynflow_enable_console Setting is off"
|
219
|
+
>
|
220
|
+
Dynflow console
|
221
|
+
</span>
|
214
222
|
</Button>
|
215
223
|
<Button
|
216
224
|
active={false}
|
@@ -219,8 +227,10 @@ exports[`Task rendering render without Props 1`] = `
|
|
219
227
|
bsSize="small"
|
220
228
|
bsStyle="default"
|
221
229
|
className="resume-button"
|
230
|
+
data-original-title="You do not have permission"
|
222
231
|
disabled={true}
|
223
232
|
onClick={[Function]}
|
233
|
+
title="You do not have permission"
|
224
234
|
>
|
225
235
|
Resume
|
226
236
|
</Button>
|
@@ -231,8 +241,10 @@ exports[`Task rendering render without Props 1`] = `
|
|
231
241
|
bsSize="small"
|
232
242
|
bsStyle="default"
|
233
243
|
className="cancel-button"
|
244
|
+
data-original-title="You do not have permission"
|
234
245
|
disabled={true}
|
235
246
|
onClick={[Function]}
|
247
|
+
title="You do not have permission"
|
236
248
|
>
|
237
249
|
Cancel
|
238
250
|
</Button>
|
@@ -243,8 +255,10 @@ exports[`Task rendering render without Props 1`] = `
|
|
243
255
|
bsSize="small"
|
244
256
|
bsStyle="default"
|
245
257
|
className="unlock-button"
|
258
|
+
data-original-title="You do not have permission"
|
246
259
|
disabled={true}
|
247
260
|
onClick={[MockFunction]}
|
261
|
+
title="You do not have permission"
|
248
262
|
>
|
249
263
|
Unlock
|
250
264
|
</Button>
|
@@ -255,8 +269,10 @@ exports[`Task rendering render without Props 1`] = `
|
|
255
269
|
bsSize="small"
|
256
270
|
bsStyle="default"
|
257
271
|
className="force-unlock-button"
|
258
|
-
|
272
|
+
data-original-title="You do not have permission"
|
273
|
+
disabled={true}
|
259
274
|
onClick={[MockFunction]}
|
275
|
+
title="You do not have permission"
|
260
276
|
>
|
261
277
|
Force Unlock
|
262
278
|
</Button>
|
@@ -264,6 +280,7 @@ exports[`Task rendering render without Props 1`] = `
|
|
264
280
|
</Row>
|
265
281
|
<TaskInfo
|
266
282
|
action=""
|
283
|
+
canEdit={false}
|
267
284
|
cancelTaskRequest={[Function]}
|
268
285
|
cancellable={false}
|
269
286
|
dynflowEnableConsole={false}
|
@@ -10,20 +10,6 @@
|
|
10
10
|
.container {
|
11
11
|
margin: 0;
|
12
12
|
}
|
13
|
-
/*
|
14
|
-
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
15
|
-
* listed below.
|
16
|
-
*
|
17
|
-
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
18
|
-
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
19
|
-
*
|
20
|
-
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
21
|
-
* compiled file, but it's generally better to create a new file per style scope.
|
22
|
-
*
|
23
|
-
*= require_self
|
24
|
-
*= require_tree .
|
25
|
-
*/
|
26
|
-
|
27
13
|
.spin {
|
28
14
|
-webkit-animation: spin 1s infinite linear;
|
29
15
|
-moz-animation: spin 1s infinite linear;
|
@@ -60,4 +46,7 @@
|
|
60
46
|
transform: rotate(360deg);
|
61
47
|
}
|
62
48
|
}
|
49
|
+
.dynflow-button > span {
|
50
|
+
pointer-events: auto;
|
51
|
+
}
|
63
52
|
}
|
@@ -56,7 +56,9 @@ export const refetchTaskDetails = (id, loading) => dispatch => {
|
|
56
56
|
|
57
57
|
const reloadTasksDetails = async (id, dispatch) => {
|
58
58
|
try {
|
59
|
-
const { data } = await API.get(
|
59
|
+
const { data } = await API.get(
|
60
|
+
`/foreman_tasks/api/tasks/${id}/details?include_permissions`
|
61
|
+
);
|
60
62
|
dispatch(requestSuccess(data));
|
61
63
|
} catch (error) {
|
62
64
|
dispatch(requestFailure(error));
|
@@ -83,7 +85,9 @@ const getTasksDetails = async (
|
|
83
85
|
refetchTaskDetailsAction
|
84
86
|
) => {
|
85
87
|
try {
|
86
|
-
const { data } = await API.get(
|
88
|
+
const { data } = await API.get(
|
89
|
+
`/foreman_tasks/api/tasks/${id}/details?include_permissions`
|
90
|
+
);
|
87
91
|
dispatch(requestSuccess(data));
|
88
92
|
if (data.state !== 'stopped') {
|
89
93
|
dispatch(taskReloadStart(timeoutId, refetchTaskDetailsAction, id));
|
@@ -30,7 +30,7 @@ export const selectErrors = state => {
|
|
30
30
|
|
31
31
|
export const selectProgress = state =>
|
32
32
|
selectTaskDetails(state).progress
|
33
|
-
?
|
33
|
+
? Math.trunc(selectTaskDetails(state).progress * 100)
|
34
34
|
: 0;
|
35
35
|
|
36
36
|
export const selectUsername = state =>
|
@@ -77,3 +77,6 @@ export const selectExternalId = state =>
|
|
77
77
|
|
78
78
|
export const selectDynflowEnableConsole = state =>
|
79
79
|
selectTaskDetails(state).dynflow_enable_console || false;
|
80
|
+
|
81
|
+
export const selectCanEdit = state =>
|
82
|
+
selectTaskDetails(state).can_edit || false;
|
@@ -32,6 +32,7 @@ import {
|
|
32
32
|
selectParentTask,
|
33
33
|
selectExternalId,
|
34
34
|
selectDynflowEnableConsole,
|
35
|
+
selectCanEdit,
|
35
36
|
} from './TaskDetailsSelectors';
|
36
37
|
|
37
38
|
const mapStateToProps = state => ({
|
@@ -62,6 +63,7 @@ const mapStateToProps = state => ({
|
|
62
63
|
parentTask: selectParentTask(state),
|
63
64
|
externalId: selectExternalId(state),
|
64
65
|
dynflowEnableConsole: selectDynflowEnableConsole(state),
|
66
|
+
canEdit: selectCanEdit(state),
|
65
67
|
});
|
66
68
|
|
67
69
|
const mapDispatchToProps = dispatch =>
|
@@ -15,12 +15,14 @@ exports[`TasksDashboard - Selectors should select selectedRowsLen some 1`] = `3`
|
|
15
15
|
exports[`TasksDashboard - Selectors should select selectedTasks 1`] = `
|
16
16
|
Array [
|
17
17
|
Object {
|
18
|
+
"canEdit": true,
|
18
19
|
"id": 1,
|
19
20
|
"isCancellable": true,
|
20
21
|
"isResumable": undefined,
|
21
22
|
"name": "action1",
|
22
23
|
},
|
23
24
|
Object {
|
25
|
+
"canEdit": undefined,
|
24
26
|
"id": 2,
|
25
27
|
"isCancellable": undefined,
|
26
28
|
"isResumable": true,
|
@@ -63,7 +63,7 @@ export const bulkResumeById = ({
|
|
63
63
|
url,
|
64
64
|
parentTaskID,
|
65
65
|
}) => async dispatch => {
|
66
|
-
const resumeTasks = selected.filter(task => task.isResumable);
|
66
|
+
const resumeTasks = selected.filter(task => task.isResumable && task.canEdit);
|
67
67
|
if (resumeTasks.length < selected.length)
|
68
68
|
dispatch(
|
69
69
|
addToast(
|
@@ -87,7 +87,7 @@ export const bulkResumeById = ({
|
|
87
87
|
});
|
88
88
|
});
|
89
89
|
if (data.resumed) {
|
90
|
-
reloadPage(url, parentTaskID
|
90
|
+
reloadPage(url, parentTaskID)(dispatch);
|
91
91
|
}
|
92
92
|
} catch (error) {
|
93
93
|
handleErrorResume(error, dispatch);
|
@@ -135,7 +135,9 @@ export const bulkCancelById = ({
|
|
135
135
|
url,
|
136
136
|
parentTaskID,
|
137
137
|
}) => async dispatch => {
|
138
|
-
const cancelTasks = selected.filter(
|
138
|
+
const cancelTasks = selected.filter(
|
139
|
+
task => task.isCancellable && task.canEdit
|
140
|
+
);
|
139
141
|
if (cancelTasks.length < selected.length)
|
140
142
|
dispatch(
|
141
143
|
addToast(
|
@@ -160,7 +162,7 @@ export const bulkCancelById = ({
|
|
160
162
|
});
|
161
163
|
});
|
162
164
|
if (data.cancelled) {
|
163
|
-
reloadPage(url, parentTaskID
|
165
|
+
reloadPage(url, parentTaskID)(dispatch);
|
164
166
|
}
|
165
167
|
} catch (error) {
|
166
168
|
handleErrorCancel(error, dispatch);
|
@@ -185,6 +187,18 @@ export const bulkForceCancelById = ({
|
|
185
187
|
parentTaskID,
|
186
188
|
}) => async dispatch => {
|
187
189
|
const stopTasks = selected.filter(task => task.state !== 'stopped');
|
190
|
+
const authorisedTasks = stopTasks.filter(task => task.canEdit);
|
191
|
+
if (authorisedTasks.length < stopTasks.length)
|
192
|
+
dispatch(
|
193
|
+
addToast(
|
194
|
+
warningToastData(
|
195
|
+
sprintf(
|
196
|
+
'User has no permission for %s task(s)',
|
197
|
+
stopTasks.length - authorisedTasks.length
|
198
|
+
)
|
199
|
+
)
|
200
|
+
)
|
201
|
+
);
|
188
202
|
if (stopTasks.length < selected.length)
|
189
203
|
dispatch(
|
190
204
|
addToast(
|
@@ -196,10 +210,13 @@ export const bulkForceCancelById = ({
|
|
196
210
|
)
|
197
211
|
)
|
198
212
|
);
|
199
|
-
if (
|
213
|
+
if (authorisedTasks.length > 0) {
|
200
214
|
dispatch({ type: TASKS_FORCE_CANCEL_REQUEST });
|
201
215
|
try {
|
202
|
-
const { data } = await bulkByIdRequest(
|
216
|
+
const { data } = await bulkByIdRequest(
|
217
|
+
authorisedTasks,
|
218
|
+
BULK_FORCE_CANCEL_PATH
|
219
|
+
);
|
203
220
|
dispatch({ type: TASKS_FORCE_CANCEL_SUCCESS });
|
204
221
|
if (data.stopped_length) {
|
205
222
|
dispatch(
|
@@ -219,7 +236,7 @@ export const bulkForceCancelById = ({
|
|
219
236
|
)
|
220
237
|
);
|
221
238
|
if (data.stopped_length > 0) {
|
222
|
-
reloadPage(url, parentTaskID
|
239
|
+
reloadPage(url, parentTaskID)(dispatch);
|
223
240
|
}
|
224
241
|
} catch (error) {
|
225
242
|
handleErrorForceCancel(error, dispatch);
|
@@ -22,7 +22,7 @@ import {
|
|
22
22
|
export const getTableItems = url =>
|
23
23
|
getTableItemsAction(TASKS_TABLE_ID, getURIQuery(url), getApiPathname(url));
|
24
24
|
|
25
|
-
export const reloadPage = (url, parentTaskID
|
25
|
+
export const reloadPage = (url, parentTaskID) => dispatch => {
|
26
26
|
dispatch(getTableItems(url));
|
27
27
|
dispatch(fetchTasksSummary(getURIQuery(url).time, parentTaskID));
|
28
28
|
};
|
@@ -34,7 +34,7 @@ export const cancelTask = ({
|
|
34
34
|
parentTaskID,
|
35
35
|
}) => async dispatch => {
|
36
36
|
await dispatch(cancelTaskRequest(taskId, taskName));
|
37
|
-
reloadPage(url, parentTaskID
|
37
|
+
reloadPage(url, parentTaskID)(dispatch);
|
38
38
|
};
|
39
39
|
|
40
40
|
export const resumeTask = ({
|
@@ -44,7 +44,7 @@ export const resumeTask = ({
|
|
44
44
|
parentTaskID,
|
45
45
|
}) => async dispatch => {
|
46
46
|
await dispatch(resumeTaskRequest(taskId, taskName));
|
47
|
-
reloadPage(url, parentTaskID
|
47
|
+
reloadPage(url, parentTaskID)(dispatch);
|
48
48
|
};
|
49
49
|
|
50
50
|
export const forceCancelTask = ({
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import PropTypes from 'prop-types';
|
3
3
|
import { getURIsearch } from 'foremanReact/common/urlHelpers';
|
4
|
-
import { Spinner } from 'patternfly-react';
|
4
|
+
import { Spinner, Button, Icon } from 'patternfly-react';
|
5
5
|
import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
|
6
6
|
import { translate as __ } from 'foremanReact/common/I18n';
|
7
7
|
import { getURIQuery } from 'foremanReact/common/helpers';
|
@@ -63,7 +63,10 @@ const TasksTablePage = ({
|
|
63
63
|
toastNotifications="foreman-tasks-cancel"
|
64
64
|
toolbarButtons={
|
65
65
|
<React.Fragment>
|
66
|
-
{props.
|
66
|
+
<Button onClick={() => props.reloadPage(url, props.parentTaskID)}>
|
67
|
+
<Icon type="fa" name="refresh" /> {__('Refresh Data')}
|
68
|
+
</Button>
|
69
|
+
{props.status === STATUS.PENDING && <Spinner size="md" loading />}
|
67
70
|
<ExportButton
|
68
71
|
url={getCSVurl(url, uriQuery)}
|
69
72
|
title={__('Export All')}
|
@@ -118,6 +121,7 @@ TasksTablePage.propTypes = {
|
|
118
121
|
openModalAction: PropTypes.func.isRequired,
|
119
122
|
showSelectAll: PropTypes.bool,
|
120
123
|
unselectAllRows: PropTypes.func.isRequired,
|
124
|
+
reloadPage: PropTypes.func.isRequired,
|
121
125
|
};
|
122
126
|
|
123
127
|
TasksTablePage.defaultProps = {
|
@@ -18,6 +18,7 @@ jest.mock('foremanReact/redux/API');
|
|
18
18
|
const task = {
|
19
19
|
id: 'some-id',
|
20
20
|
name: 'some-name',
|
21
|
+
canEdit: true,
|
21
22
|
};
|
22
23
|
|
23
24
|
const fixtures = {
|
@@ -140,6 +141,18 @@ const fixtures = {
|
|
140
141
|
url: 'some-url',
|
141
142
|
parentTaskID: 'parent',
|
142
143
|
}),
|
144
|
+
'handles bulkCancelById requests with canEdit false': () => {
|
145
|
+
const selected = [{ ...task, isCancellable: true, canEdit: false }];
|
146
|
+
return bulkCancelById({ selected, url: 'some-url' });
|
147
|
+
},
|
148
|
+
'handles bulkResumeById requests with canEdit false': () => {
|
149
|
+
const selected = [{ ...task, isResumable: false, canEdit: false }];
|
150
|
+
return bulkResumeById({ selected, url: 'some-url' });
|
151
|
+
},
|
152
|
+
'handles bulkForceCancelById requests with canEdit false': () => {
|
153
|
+
const selected = [{ ...task, isResumable: false, canEdit: false }];
|
154
|
+
return bulkForceCancelById({ selected, url: 'some-url' });
|
155
|
+
},
|
143
156
|
};
|
144
157
|
|
145
158
|
describe('TasksTable bulk actions', () => {
|
@@ -12,9 +12,10 @@ const history = {
|
|
12
12
|
const fixtures = {
|
13
13
|
'render with minimal props': { ...minProps, history },
|
14
14
|
|
15
|
-
'render with Breadcrubs': {
|
15
|
+
'render with Breadcrubs and edit permissions': {
|
16
16
|
...minProps,
|
17
17
|
history,
|
18
|
+
results: [{ action: 'a', canEdit: true }],
|
18
19
|
getBreadcrumbs: () => ({
|
19
20
|
breadcrumbItems: [
|
20
21
|
{ caption: 'Tasks', url: `/foreman_tasks/tasks` },
|
@@ -48,6 +48,22 @@ Array [
|
|
48
48
|
]
|
49
49
|
`;
|
50
50
|
|
51
|
+
exports[`TasksTable bulk actions handles bulkCancelById requests with canEdit false 1`] = `
|
52
|
+
Array [
|
53
|
+
Array [
|
54
|
+
Object {
|
55
|
+
"payload": Object {
|
56
|
+
"message": Object {
|
57
|
+
"message": "Not all the selected tasks can be cancelled",
|
58
|
+
"type": "warning",
|
59
|
+
},
|
60
|
+
},
|
61
|
+
"type": "TOASTS_ADD",
|
62
|
+
},
|
63
|
+
],
|
64
|
+
]
|
65
|
+
`;
|
66
|
+
|
51
67
|
exports[`TasksTable bulk actions handles bulkCancelBySearch requests 1`] = `
|
52
68
|
Array [
|
53
69
|
Array [
|
@@ -155,6 +171,22 @@ Array [
|
|
155
171
|
]
|
156
172
|
`;
|
157
173
|
|
174
|
+
exports[`TasksTable bulk actions handles bulkForceCancelById requests with canEdit false 1`] = `
|
175
|
+
Array [
|
176
|
+
Array [
|
177
|
+
Object {
|
178
|
+
"payload": Object {
|
179
|
+
"message": Object {
|
180
|
+
"message": "User has no permission for 1 task(s)",
|
181
|
+
"type": "warning",
|
182
|
+
},
|
183
|
+
},
|
184
|
+
"type": "TOASTS_ADD",
|
185
|
+
},
|
186
|
+
],
|
187
|
+
]
|
188
|
+
`;
|
189
|
+
|
158
190
|
exports[`TasksTable bulk actions handles bulkForceCancelBySearch requests 1`] = `
|
159
191
|
Array [
|
160
192
|
Array [
|
@@ -219,6 +251,22 @@ Array [
|
|
219
251
|
]
|
220
252
|
`;
|
221
253
|
|
254
|
+
exports[`TasksTable bulk actions handles bulkResumeById requests with canEdit false 1`] = `
|
255
|
+
Array [
|
256
|
+
Array [
|
257
|
+
Object {
|
258
|
+
"payload": Object {
|
259
|
+
"message": Object {
|
260
|
+
"message": "Not all the selected tasks can be resumed",
|
261
|
+
"type": "warning",
|
262
|
+
},
|
263
|
+
},
|
264
|
+
"type": "TOASTS_ADD",
|
265
|
+
},
|
266
|
+
],
|
267
|
+
]
|
268
|
+
`;
|
269
|
+
|
222
270
|
exports[`TasksTable bulk actions handles bulkResumeBySearch requests 1`] = `
|
223
271
|
Array [
|
224
272
|
Array [
|
data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
2
|
|
3
|
-
exports[`TasksTablePage rendering render with Breadcrubs 1`] = `
|
3
|
+
exports[`TasksTablePage rendering render with Breadcrubs and edit permissions 1`] = `
|
4
4
|
<div
|
5
5
|
className="tasks-table-wrapper"
|
6
6
|
>
|
@@ -69,12 +69,27 @@ exports[`TasksTablePage rendering render with Breadcrubs 1`] = `
|
|
69
69
|
toastNotifications="foreman-tasks-cancel"
|
70
70
|
toolbarButtons={
|
71
71
|
<React.Fragment>
|
72
|
+
<Button
|
73
|
+
active={false}
|
74
|
+
block={false}
|
75
|
+
bsClass="btn"
|
76
|
+
bsStyle="default"
|
77
|
+
disabled={false}
|
78
|
+
onClick={[Function]}
|
79
|
+
>
|
80
|
+
<Icon
|
81
|
+
name="refresh"
|
82
|
+
type="fa"
|
83
|
+
/>
|
84
|
+
|
85
|
+
Refresh Data
|
86
|
+
</Button>
|
72
87
|
<Spinner
|
73
88
|
className=""
|
74
89
|
inline={false}
|
75
90
|
inverse={false}
|
76
91
|
loading={true}
|
77
|
-
size="
|
92
|
+
size="md"
|
78
93
|
/>
|
79
94
|
<ExportButton
|
80
95
|
title="Export All"
|
@@ -112,10 +127,13 @@ exports[`TasksTablePage rendering render with Breadcrubs 1`] = `
|
|
112
127
|
}
|
113
128
|
}
|
114
129
|
parentTaskID={null}
|
130
|
+
reloadPage={[MockFunction]}
|
115
131
|
results={
|
116
132
|
Array [
|
117
|
-
|
118
|
-
|
133
|
+
Object {
|
134
|
+
"action": "a",
|
135
|
+
"canEdit": true,
|
136
|
+
},
|
119
137
|
]
|
120
138
|
}
|
121
139
|
selectPage={[MockFunction]}
|
@@ -187,12 +205,27 @@ exports[`TasksTablePage rendering render with minimal props 1`] = `
|
|
187
205
|
toastNotifications="foreman-tasks-cancel"
|
188
206
|
toolbarButtons={
|
189
207
|
<React.Fragment>
|
208
|
+
<Button
|
209
|
+
active={false}
|
210
|
+
block={false}
|
211
|
+
bsClass="btn"
|
212
|
+
bsStyle="default"
|
213
|
+
disabled={false}
|
214
|
+
onClick={[Function]}
|
215
|
+
>
|
216
|
+
<Icon
|
217
|
+
name="refresh"
|
218
|
+
type="fa"
|
219
|
+
/>
|
220
|
+
|
221
|
+
Refresh Data
|
222
|
+
</Button>
|
190
223
|
<Spinner
|
191
224
|
className=""
|
192
225
|
inline={false}
|
193
226
|
inverse={false}
|
194
227
|
loading={true}
|
195
|
-
size="
|
228
|
+
size="md"
|
196
229
|
/>
|
197
230
|
<ExportButton
|
198
231
|
title="Export All"
|
@@ -230,6 +263,7 @@ exports[`TasksTablePage rendering render with minimal props 1`] = `
|
|
230
263
|
}
|
231
264
|
}
|
232
265
|
parentTaskID={null}
|
266
|
+
reloadPage={[MockFunction]}
|
233
267
|
results={
|
234
268
|
Array [
|
235
269
|
"a",
|
data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/actionCellFormatter.test.js
CHANGED
@@ -4,7 +4,7 @@ describe('actionCellFormatter', () => {
|
|
4
4
|
it('render', () => {
|
5
5
|
const data = [
|
6
6
|
{ cancellable: true },
|
7
|
-
{ rowData: { action: 'some-name', id: 'some-id' } },
|
7
|
+
{ rowData: { action: 'some-name', id: 'some-id', canEdit: true } },
|
8
8
|
];
|
9
9
|
expect(actionCellFormatter({})(...data)).toMatchSnapshot();
|
10
10
|
});
|
@@ -4,13 +4,16 @@ import { ActionButton } from '../../common/ActionButtons/ActionButton';
|
|
4
4
|
|
5
5
|
export const actionCellFormatter = taskActions => (
|
6
6
|
value,
|
7
|
-
{ rowData: { action, id } }
|
7
|
+
{ rowData: { action, id, canEdit } }
|
8
8
|
) =>
|
9
9
|
cellFormatter(
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
canEdit && (
|
11
|
+
<ActionButton
|
12
|
+
canEdit={canEdit}
|
13
|
+
id={id}
|
14
|
+
name={action}
|
15
|
+
taskActions={taskActions}
|
16
|
+
availableActions={value}
|
17
|
+
/>
|
18
|
+
)
|
16
19
|
);
|
@@ -1,9 +1,16 @@
|
|
1
1
|
import React from 'react';
|
2
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
2
3
|
import TableSelectionCell from '../Components/TableSelectionCell';
|
3
4
|
|
4
5
|
export default (selectionController, additionalData) => (
|
5
6
|
<TableSelectionCell
|
6
7
|
id={`select${additionalData.rowIndex}`}
|
8
|
+
disabled={!additionalData.rowData.canEdit}
|
9
|
+
title={
|
10
|
+
additionalData.rowData.canEdit
|
11
|
+
? undefined
|
12
|
+
: __('You do not have permission')
|
13
|
+
}
|
7
14
|
checked={selectionController.isSelected(additionalData)}
|
8
15
|
onChange={() => selectionController.selectRow(additionalData)}
|
9
16
|
/>
|
@@ -4,45 +4,48 @@ import { translate as __ } from 'foremanReact/common/I18n';
|
|
4
4
|
import { ActionButtons } from 'foremanReact/components/common/ActionButtons/ActionButtons';
|
5
5
|
|
6
6
|
export const ActionButton = ({
|
7
|
+
canEdit,
|
7
8
|
id,
|
8
9
|
name,
|
9
10
|
availableActions: { resumable, cancellable, stoppable },
|
10
11
|
taskActions,
|
11
12
|
}) => {
|
12
13
|
const buttons = [];
|
13
|
-
const isTitle = !(resumable || cancellable || stoppable);
|
14
|
+
const isTitle = canEdit && !(resumable || cancellable || stoppable);
|
14
15
|
const title = isTitle ? __('Task cannot be canceled') : undefined;
|
15
|
-
if (
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
16
|
+
if (canEdit) {
|
17
|
+
if (resumable) {
|
18
|
+
buttons.push({
|
19
|
+
title: __('Resume'),
|
20
|
+
action: {
|
21
|
+
disabled: !resumable,
|
22
|
+
onClick: () => taskActions.resumeTask(id, name),
|
23
|
+
id: `task-resume-button-${id}`,
|
24
|
+
},
|
25
|
+
});
|
26
|
+
}
|
27
|
+
if (cancellable || (!stoppable && !resumable)) {
|
28
|
+
// Cancel is the default button that should be shown if no task action can be done
|
29
|
+
buttons.push({
|
30
|
+
title: __('Cancel'),
|
31
|
+
action: {
|
32
|
+
disabled: !cancellable,
|
33
|
+
onClick: () => taskActions.cancelTask(id, name),
|
34
|
+
id: `task-cancel-button-${id}`,
|
35
|
+
},
|
36
|
+
});
|
37
|
+
}
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
if (stoppable) {
|
40
|
+
buttons.push({
|
41
|
+
title: __('Force Cancel'),
|
42
|
+
action: {
|
43
|
+
disabled: !stoppable,
|
44
|
+
onClick: () => taskActions.forceCancelTask(id, name),
|
45
|
+
id: `task-force-cancel-button-${id}`,
|
46
|
+
},
|
47
|
+
});
|
48
|
+
}
|
46
49
|
}
|
47
50
|
return (
|
48
51
|
<span title={title}>
|
@@ -52,6 +55,7 @@ export const ActionButton = ({
|
|
52
55
|
};
|
53
56
|
|
54
57
|
ActionButton.propTypes = {
|
58
|
+
canEdit: PropTypes.bool,
|
55
59
|
id: PropTypes.string.isRequired,
|
56
60
|
name: PropTypes.string.isRequired,
|
57
61
|
availableActions: PropTypes.shape({
|
@@ -65,3 +69,7 @@ ActionButton.propTypes = {
|
|
65
69
|
forceCancelTask: PropTypes.func,
|
66
70
|
}).isRequired,
|
67
71
|
};
|
72
|
+
|
73
|
+
ActionButton.defaultProps = {
|
74
|
+
canEdit: false,
|
75
|
+
};
|
@@ -7,6 +7,7 @@ const resumeTask = jest.fn();
|
|
7
7
|
const cancelTask = jest.fn();
|
8
8
|
const forceCancelTask = jest.fn();
|
9
9
|
const taskActions = { resumeTask, cancelTask, forceCancelTask };
|
10
|
+
const minProps = { canEdit: true, id: 'id', name: 'some-name' };
|
10
11
|
const fixtures = {
|
11
12
|
'render with cancellable true props': {
|
12
13
|
availableActions: {
|
@@ -14,8 +15,7 @@ const fixtures = {
|
|
14
15
|
resumable: false,
|
15
16
|
},
|
16
17
|
taskActions,
|
17
|
-
|
18
|
-
name: 'some-name',
|
18
|
+
...minProps,
|
19
19
|
},
|
20
20
|
'render with resumable true props': {
|
21
21
|
availableActions: {
|
@@ -23,8 +23,7 @@ const fixtures = {
|
|
23
23
|
resumable: true,
|
24
24
|
},
|
25
25
|
taskActions,
|
26
|
-
|
27
|
-
name: 'some-name',
|
26
|
+
...minProps,
|
28
27
|
},
|
29
28
|
'render with stoppable and cancellable true props': {
|
30
29
|
availableActions: {
|
@@ -32,8 +31,7 @@ const fixtures = {
|
|
32
31
|
stoppable: true,
|
33
32
|
},
|
34
33
|
taskActions,
|
35
|
-
|
36
|
-
name: 'some-name',
|
34
|
+
...minProps,
|
37
35
|
},
|
38
36
|
'render with cancellable false props': {
|
39
37
|
availableActions: {
|
@@ -41,8 +39,16 @@ const fixtures = {
|
|
41
39
|
resumable: false,
|
42
40
|
},
|
43
41
|
taskActions,
|
44
|
-
|
45
|
-
|
42
|
+
...minProps,
|
43
|
+
},
|
44
|
+
'render with canEdit false': {
|
45
|
+
availableActions: {
|
46
|
+
cancellable: false,
|
47
|
+
resumable: false,
|
48
|
+
},
|
49
|
+
taskActions,
|
50
|
+
...minProps,
|
51
|
+
canEdit: false,
|
46
52
|
},
|
47
53
|
};
|
48
54
|
|
@@ -57,6 +63,7 @@ describe('ActionButton', () => {
|
|
57
63
|
<ActionButton
|
58
64
|
id={id}
|
59
65
|
name={name}
|
66
|
+
canEdit
|
60
67
|
availableActions={{ cancellable: true }}
|
61
68
|
taskActions={taskActions}
|
62
69
|
/>
|
@@ -69,6 +76,7 @@ describe('ActionButton', () => {
|
|
69
76
|
<ActionButton
|
70
77
|
id={id}
|
71
78
|
name={name}
|
79
|
+
canEdit
|
72
80
|
availableActions={{ resumable: true }}
|
73
81
|
taskActions={taskActions}
|
74
82
|
/>
|
@@ -81,6 +89,7 @@ describe('ActionButton', () => {
|
|
81
89
|
<ActionButton
|
82
90
|
id={id}
|
83
91
|
name={name}
|
92
|
+
canEdit
|
84
93
|
availableActions={{ stoppable: true }}
|
85
94
|
taskActions={taskActions}
|
86
95
|
/>
|
data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/ActionButton.test.js.snap
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
2
|
|
3
|
+
exports[`ActionButton snapshot test render with canEdit false 1`] = `
|
4
|
+
<span>
|
5
|
+
<ActionButtons
|
6
|
+
buttons={Array []}
|
7
|
+
/>
|
8
|
+
</span>
|
9
|
+
`;
|
10
|
+
|
3
11
|
exports[`ActionButton snapshot test render with cancellable false props 1`] = `
|
4
12
|
<span
|
5
13
|
title="Task cannot be canceled"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman-tasks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Nečas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dynflow
|
@@ -270,6 +270,7 @@ files:
|
|
270
270
|
- lib/foreman_tasks/engine.rb
|
271
271
|
- lib/foreman_tasks/task_error.rb
|
272
272
|
- lib/foreman_tasks/tasks/cleanup.rake
|
273
|
+
- lib/foreman_tasks/tasks/dynflow.rake
|
273
274
|
- lib/foreman_tasks/tasks/export_tasks.rake
|
274
275
|
- lib/foreman_tasks/tasks/generate_task_actions.rake
|
275
276
|
- lib/foreman_tasks/test_extensions.rb
|
@@ -289,6 +290,7 @@ files:
|
|
289
290
|
- locale/zh_CN/LC_MESSAGES/foreman_tasks.mo
|
290
291
|
- locale/zh_CN/foreman_tasks.po
|
291
292
|
- package.json
|
293
|
+
- script/npm_link_foreman_js.sh
|
292
294
|
- script/rails
|
293
295
|
- script/travis_run_js_tests.sh
|
294
296
|
- test/controllers/api/recurring_logics_controller_test.rb
|
@@ -593,7 +595,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
593
595
|
- !ruby/object:Gem::Version
|
594
596
|
version: '0'
|
595
597
|
requirements: []
|
596
|
-
rubygems_version: 3.0.
|
598
|
+
rubygems_version: 3.0.3
|
597
599
|
signing_key:
|
598
600
|
specification_version: 4
|
599
601
|
summary: Foreman plugin for showing tasks information for resources and users
|