foreman-tasks 2.0.1 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|