foreman-tasks 4.1.6 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby_tests.yml +0 -1
- data/app/controllers/foreman_tasks/api/tasks_controller.rb +2 -2
- data/app/controllers/foreman_tasks/tasks_controller.rb +7 -6
- data/app/graphql/types/recurring_logic.rb +18 -0
- data/app/graphql/types/task.rb +25 -0
- data/app/graphql/types/triggering.rb +16 -0
- data/app/lib/actions/helpers/with_continuous_output.rb +1 -1
- data/app/lib/actions/middleware/watch_delegated_proxy_sub_tasks.rb +6 -2
- data/app/models/foreman_tasks/recurring_logic.rb +2 -0
- data/app/models/foreman_tasks/task/dynflow_task.rb +3 -8
- data/app/models/foreman_tasks/task.rb +28 -0
- data/app/models/foreman_tasks/triggering.rb +2 -0
- data/app/services/foreman_tasks/dashboard_table_filter.rb +56 -0
- data/foreman-tasks.gemspec +0 -1
- data/lib/foreman_tasks/continuous_output.rb +50 -0
- data/lib/foreman_tasks/engine.rb +6 -15
- data/lib/foreman_tasks/tasks/export_tasks.rake +46 -90
- data/lib/foreman_tasks/version.rb +1 -1
- data/lib/foreman_tasks.rb +2 -5
- data/test/controllers/api/tasks_controller_test.rb +0 -11
- data/test/graphql/queries/recurring_logic_test.rb +28 -0
- data/test/graphql/queries/recurring_logics_query_test.rb +30 -0
- data/test/graphql/queries/task_query_test.rb +33 -0
- data/test/graphql/queries/tasks_query_test.rb +31 -0
- data/test/unit/dashboard_table_filter_test.rb +77 -0
- data/test/unit/task_test.rb +39 -8
- data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.js +4 -11
- data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.test.js +5 -27
- data/webpack/ForemanTasks/Components/TasksTable/TasksTable.js +0 -8
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableActions.js +1 -6
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableHelpers.js +1 -2
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +11 -22
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableReducer.js +16 -17
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +0 -3
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableHelpers.test.js +1 -1
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableReducer.test.js +1 -3
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +2 -12
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableReducer.test.js.snap +0 -5
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/selectionHeaderCellFormatter.test.js.snap +0 -1
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/selectionHeaderCellFormatter.test.js +1 -1
- data/webpack/ForemanTasks/Components/TasksTable/formatters/selectionHeaderCellFormatter.js +0 -1
- data/webpack/ForemanTasks/Components/TasksTable/index.js +0 -2
- metadata +18 -27
- data/test/core/unit/dispatcher_test.rb +0 -43
- data/test/core/unit/runner_test.rb +0 -116
- data/test/core/unit/task_launcher_test.rb +0 -56
- data/test/foreman_tasks_core_test_helper.rb +0 -4
- data/test/unit/otp_manager_test.rb +0 -77
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'foreman_tasks_test_helper'
|
2
|
+
|
3
|
+
module Queries
|
4
|
+
class RecurringLogicsTest < GraphQLQueryTestCase
|
5
|
+
let(:query) do
|
6
|
+
<<-GRAPHQL
|
7
|
+
query {
|
8
|
+
recurringLogics {
|
9
|
+
nodes {
|
10
|
+
id
|
11
|
+
cronLine
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
15
|
+
GRAPHQL
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:data) { result['data']['recurringLogics'] }
|
19
|
+
|
20
|
+
setup do
|
21
|
+
FactoryBot.create_list(:recurring_logic, 2)
|
22
|
+
end
|
23
|
+
|
24
|
+
test "should fetch recurring logics" do
|
25
|
+
assert_empty result['errors']
|
26
|
+
expected_count = ::ForemanTasks::RecurringLogic.count
|
27
|
+
assert_not_equal 0, expected_count
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'foreman_tasks_test_helper'
|
2
|
+
|
3
|
+
module Queries
|
4
|
+
class TaskQueryTest < GraphQLQueryTestCase
|
5
|
+
let(:query) do
|
6
|
+
<<-GRAPHQL
|
7
|
+
query (
|
8
|
+
$id: String!
|
9
|
+
) {
|
10
|
+
task(id: $id) {
|
11
|
+
id
|
12
|
+
action
|
13
|
+
result
|
14
|
+
}
|
15
|
+
}
|
16
|
+
GRAPHQL
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:res) { 'inconclusive' }
|
20
|
+
let(:task) { FactoryBot.create(:some_task, :result => res) }
|
21
|
+
|
22
|
+
let(:global_id) { Foreman::GlobalId.for(task) }
|
23
|
+
let(:variables) { { id: global_id } }
|
24
|
+
let(:data) { result['data']['task'] }
|
25
|
+
|
26
|
+
test 'should fetch task data' do
|
27
|
+
assert_empty result['errors']
|
28
|
+
|
29
|
+
assert_equal global_id, data['id']
|
30
|
+
assert_equal task.result, data['result']
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'foreman_tasks_test_helper'
|
2
|
+
|
3
|
+
module Queries
|
4
|
+
class TasksQueryTest < GraphQLQueryTestCase
|
5
|
+
let(:query) do
|
6
|
+
<<-GRAPHQL
|
7
|
+
query {
|
8
|
+
tasks {
|
9
|
+
nodes {
|
10
|
+
id
|
11
|
+
action
|
12
|
+
result
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
16
|
+
GRAPHQL
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:data) { result['data']['tasks'] }
|
20
|
+
|
21
|
+
setup do
|
22
|
+
FactoryBot.create_list(:some_task, 2)
|
23
|
+
end
|
24
|
+
|
25
|
+
test "should fetch recurring logics" do
|
26
|
+
assert_empty result['errors']
|
27
|
+
expected_count = ::ForemanTasks::Task.count
|
28
|
+
assert_not_equal 0, expected_count
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'foreman_tasks_test_helper'
|
2
|
+
|
3
|
+
class DashboardTableFilterTest < ActiveSupport::TestCase
|
4
|
+
before do
|
5
|
+
::ForemanTasks::Task.delete_all
|
6
|
+
end
|
7
|
+
|
8
|
+
describe ForemanTasks::DashboardTableFilter do
|
9
|
+
before do
|
10
|
+
@tasks_builder = HistoryTasksBuilder.new
|
11
|
+
@scope = ForemanTasks::Task.all
|
12
|
+
@tasks_builder.build
|
13
|
+
end
|
14
|
+
|
15
|
+
let :subject do
|
16
|
+
ForemanTasks::DashboardTableFilter.new(@scope, params)
|
17
|
+
end
|
18
|
+
|
19
|
+
let :filtered_scope do
|
20
|
+
subject.scope
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'by result' do
|
24
|
+
let(:params) { { result: 'warning' } }
|
25
|
+
|
26
|
+
it 'filters' do
|
27
|
+
_(filtered_scope.count).must_equal @tasks_builder.distribution['stopped'][:by_result]['warning'][:total]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'by state' do
|
32
|
+
let(:params) { { state: 'running' } }
|
33
|
+
|
34
|
+
it 'filters' do
|
35
|
+
_(filtered_scope.count).must_equal @tasks_builder.distribution['running'][:total]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'recent' do
|
40
|
+
let(:params) do
|
41
|
+
{ state: 'running',
|
42
|
+
time_horizon: 'H24',
|
43
|
+
time_mode: 'recent' }
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'filters' do
|
47
|
+
_(filtered_scope.count).must_equal @tasks_builder.distribution['running'][:recent]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'recent week time horizon' do
|
52
|
+
let(:params) do
|
53
|
+
{ state: 'running',
|
54
|
+
time_horizon: 'week',
|
55
|
+
time_mode: 'recent' }
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'filters' do
|
59
|
+
_(filtered_scope.count).must_equal @tasks_builder.distribution['running'][:recent]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe 'older' do
|
64
|
+
let(:params) do
|
65
|
+
{ state: 'running',
|
66
|
+
time_horizon: 'H24',
|
67
|
+
time_mode: 'older' }
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'filters' do
|
71
|
+
old_tasks_count = @tasks_builder.distribution['running'][:total] -
|
72
|
+
@tasks_builder.distribution['running'][:recent]
|
73
|
+
_(filtered_scope.count).must_equal old_tasks_count
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/test/unit/task_test.rb
CHANGED
@@ -296,12 +296,12 @@ class TasksTest < ActiveSupport::TestCase
|
|
296
296
|
end
|
297
297
|
|
298
298
|
describe 'search for resource_ids' do
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
resource_type = 'restype1'
|
299
|
+
label = 'label1'
|
300
|
+
resource_ids = [1, 2]
|
301
|
+
resource_type = 'restype1'
|
303
302
|
|
304
|
-
|
303
|
+
let(:task1_old) do
|
304
|
+
FactoryBot.create(
|
305
305
|
:task_with_links,
|
306
306
|
started_at: '2019-10-01 11:15:55',
|
307
307
|
ended_at: '2019-10-01 11:15:57',
|
@@ -309,7 +309,9 @@ class TasksTest < ActiveSupport::TestCase
|
|
309
309
|
label: label,
|
310
310
|
resource_type: resource_type
|
311
311
|
)
|
312
|
-
|
312
|
+
end
|
313
|
+
let(:task1_new) do
|
314
|
+
FactoryBot.create(
|
313
315
|
:task_with_links,
|
314
316
|
started_at: '2019-10-02 11:15:55',
|
315
317
|
ended_at: '2019-10-02 11:15:57',
|
@@ -317,7 +319,9 @@ class TasksTest < ActiveSupport::TestCase
|
|
317
319
|
label: label,
|
318
320
|
resource_type: resource_type
|
319
321
|
)
|
320
|
-
|
322
|
+
end
|
323
|
+
let(:task2) do
|
324
|
+
FactoryBot.create(
|
321
325
|
:task_with_links,
|
322
326
|
started_at: '2019-10-03 11:15:55',
|
323
327
|
ended_at: '2019-10-03 11:15:57',
|
@@ -325,7 +329,9 @@ class TasksTest < ActiveSupport::TestCase
|
|
325
329
|
label: label,
|
326
330
|
resource_type: resource_type
|
327
331
|
)
|
328
|
-
|
332
|
+
end
|
333
|
+
let(:task3) do
|
334
|
+
FactoryBot.create(
|
329
335
|
:task_with_links,
|
330
336
|
started_at: '2019-10-03 11:15:55',
|
331
337
|
ended_at: '2019-10-03 11:15:57',
|
@@ -333,6 +339,13 @@ class TasksTest < ActiveSupport::TestCase
|
|
333
339
|
label: label,
|
334
340
|
resource_type: 'another_type'
|
335
341
|
)
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'finds tasks' do
|
345
|
+
task1_old
|
346
|
+
task1_new
|
347
|
+
task2
|
348
|
+
task3
|
336
349
|
|
337
350
|
result = ForemanTasks::Task.search_for(
|
338
351
|
"resource_id ^ (#{resource_ids.join(',')}) and resource_type = #{resource_type}"
|
@@ -343,5 +356,23 @@ class TasksTest < ActiveSupport::TestCase
|
|
343
356
|
assert_includes result, task2
|
344
357
|
assert_not_includes result, task3
|
345
358
|
end
|
359
|
+
|
360
|
+
it 'finds latest task for each resource_id' do
|
361
|
+
task1_old
|
362
|
+
task1_new
|
363
|
+
task2
|
364
|
+
task3
|
365
|
+
|
366
|
+
result = ForemanTasks::Task.latest_tasks_by_resource_ids(
|
367
|
+
label,
|
368
|
+
resource_type,
|
369
|
+
resource_ids
|
370
|
+
)
|
371
|
+
assert_equal 2, result.length
|
372
|
+
assert_equal resource_ids, result.keys.sort
|
373
|
+
assert_equal task1_new, result[1]
|
374
|
+
assert_equal task2, result[2]
|
375
|
+
assert_not_includes result.values, task3
|
376
|
+
end
|
346
377
|
end
|
347
378
|
end
|
@@ -15,29 +15,22 @@ export const convertDashboardQuery = query => {
|
|
15
15
|
state,
|
16
16
|
result,
|
17
17
|
search,
|
18
|
-
...rest
|
19
18
|
} = query;
|
20
19
|
|
21
20
|
const hours = timeToHoursNumber(timeHorizon);
|
22
21
|
const timestamp = new Date(new Date() - hours * 60 * 60 * 1000);
|
23
22
|
let dashboardTime = '';
|
24
23
|
const stateQuery = state ? `state=${state}` : '';
|
25
|
-
|
26
|
-
if (result === 'other') {
|
27
|
-
resultQuery = 'result ^ (pending, cancelled)';
|
28
|
-
} else {
|
29
|
-
resultQuery = result ? `result=${result}` : '';
|
30
|
-
}
|
24
|
+
const resultQuery = result ? `result=${result}` : '';
|
31
25
|
if (timeMode === TASKS_DASHBOARD_JS_QUERY_MODES.RECENT) {
|
32
|
-
dashboardTime = `state_updated_at>${timestamp.toISOString()} or
|
26
|
+
dashboardTime = `(state_updated_at>${timestamp.toISOString()} or state_updated_at = NULL)`;
|
33
27
|
} else if (timeMode === TASKS_DASHBOARD_JS_QUERY_MODES.OLDER) {
|
34
|
-
dashboardTime = `state_updated_at
|
28
|
+
dashboardTime = `(state_updated_at>${timestamp.toISOString()})`;
|
35
29
|
}
|
36
30
|
const newQuery = [stateQuery, resultQuery, search, dashboardTime]
|
37
31
|
.filter(Boolean)
|
38
|
-
.map(q => `(${q})`)
|
39
32
|
.join(' and ');
|
40
|
-
return newQuery
|
33
|
+
return newQuery;
|
41
34
|
};
|
42
35
|
|
43
36
|
export const resumeToastInfo = {
|
@@ -27,18 +27,9 @@ describe('convertDashboardQuery', () => {
|
|
27
27
|
result: 'error',
|
28
28
|
search: 'action~job',
|
29
29
|
};
|
30
|
-
|
31
|
-
'
|
32
|
-
|
33
|
-
expect(convertDashboardQuery(query)).toEqual({ search: expected });
|
34
|
-
|
35
|
-
const query2 = {
|
36
|
-
...query,
|
37
|
-
time_mode: TASKS_DASHBOARD_JS_QUERY_MODES.OLDER,
|
38
|
-
};
|
39
|
-
const expected2 =
|
40
|
-
'(state=stopped) and (result=error) and (action~job) and (state_updated_at<=2020-05-01T11:01:58.135Z)';
|
41
|
-
expect(convertDashboardQuery(query2)).toEqual({ search: expected2 });
|
30
|
+
expect(convertDashboardQuery(query)).toEqual(
|
31
|
+
'state=stopped and result=error and action~job and (state_updated_at>2020-05-01T11:01:58.135Z or state_updated_at = NULL)'
|
32
|
+
);
|
42
33
|
// Cleanup
|
43
34
|
global.Date = realDate;
|
44
35
|
});
|
@@ -46,23 +37,10 @@ describe('convertDashboardQuery', () => {
|
|
46
37
|
const query = {
|
47
38
|
search: 'action~job',
|
48
39
|
};
|
49
|
-
expect(convertDashboardQuery(query)).toEqual(
|
40
|
+
expect(convertDashboardQuery(query)).toEqual('action~job');
|
50
41
|
});
|
51
42
|
it('convertDashboardQuery should work with no query', () => {
|
52
43
|
const query = {};
|
53
|
-
expect(convertDashboardQuery(query)).toEqual(
|
54
|
-
});
|
55
|
-
it('convertDashboardQuery should not override unknown keys', () => {
|
56
|
-
const query = { weather: 'nice', search: 'okay', number: 7 };
|
57
|
-
expect(convertDashboardQuery(query)).toEqual({
|
58
|
-
...query,
|
59
|
-
search: '(okay)',
|
60
|
-
});
|
61
|
-
});
|
62
|
-
it('convertDashboardQuery should expand other result', () => {
|
63
|
-
const query = { result: 'other' };
|
64
|
-
expect(convertDashboardQuery(query)).toEqual({
|
65
|
-
search: '(result ^ (pending, cancelled))',
|
66
|
-
});
|
44
|
+
expect(convertDashboardQuery(query)).toEqual('');
|
67
45
|
});
|
68
46
|
});
|
@@ -27,7 +27,6 @@ const TasksTable = ({
|
|
27
27
|
openClickedModal,
|
28
28
|
openModal,
|
29
29
|
allRowsSelected,
|
30
|
-
permissions,
|
31
30
|
}) => {
|
32
31
|
const { search, pathname } = history.location;
|
33
32
|
const url = pathname + search;
|
@@ -60,7 +59,6 @@ const TasksTable = ({
|
|
60
59
|
},
|
61
60
|
isSelected: ({ rowData }) =>
|
62
61
|
allRowsSelected || selectedRows.includes(rowData.id),
|
63
|
-
permissions,
|
64
62
|
};
|
65
63
|
};
|
66
64
|
|
@@ -164,9 +162,6 @@ TasksTable.propTypes = {
|
|
164
162
|
unselectRow: PropTypes.func.isRequired,
|
165
163
|
openModal: PropTypes.func.isRequired,
|
166
164
|
allRowsSelected: PropTypes.bool,
|
167
|
-
permissions: PropTypes.shape({
|
168
|
-
edit: PropTypes.bool,
|
169
|
-
}),
|
170
165
|
};
|
171
166
|
|
172
167
|
TasksTable.defaultProps = {
|
@@ -178,9 +173,6 @@ TasksTable.defaultProps = {
|
|
178
173
|
},
|
179
174
|
selectedRows: [],
|
180
175
|
allRowsSelected: false,
|
181
|
-
permissions: {
|
182
|
-
edit: false,
|
183
|
-
},
|
184
176
|
};
|
185
177
|
|
186
178
|
export default TasksTable;
|
@@ -18,14 +18,9 @@ import {
|
|
18
18
|
resumeTaskRequest,
|
19
19
|
forceCancelTaskRequest,
|
20
20
|
} from '../TaskActions';
|
21
|
-
import { convertDashboardQuery } from '../TaskActions/TaskActionHelpers';
|
22
21
|
|
23
22
|
export const getTableItems = url =>
|
24
|
-
getTableItemsAction(
|
25
|
-
TASKS_TABLE_ID,
|
26
|
-
convertDashboardQuery(getURIQuery(url)),
|
27
|
-
getApiPathname(url)
|
28
|
-
);
|
23
|
+
getTableItemsAction(TASKS_TABLE_ID, getURIQuery(url), getApiPathname(url));
|
29
24
|
|
30
25
|
export const reloadPage = (url, parentTaskID) => dispatch => {
|
31
26
|
dispatch(getTableItems(url));
|
@@ -2,7 +2,6 @@ import URI from 'urijs';
|
|
2
2
|
import { translate as __, documentLocale } from 'foremanReact/common/I18n';
|
3
3
|
import humanizeDuration from 'humanize-duration';
|
4
4
|
import { isoCompatibleDate } from 'foremanReact/common/helpers';
|
5
|
-
import { convertDashboardQuery } from '../TaskActions/TaskActionHelpers';
|
6
5
|
|
7
6
|
export const updateURlQuery = (query, history) => {
|
8
7
|
const uri = new URI(history.location.pathname + history.location.search);
|
@@ -18,7 +17,7 @@ export const getApiPathname = url => {
|
|
18
17
|
export const getCSVurl = (path, query) => {
|
19
18
|
let url = new URI(path);
|
20
19
|
url = url.pathname(`${url.pathname()}.csv`);
|
21
|
-
url.addSearch(
|
20
|
+
url.addSearch(query);
|
22
21
|
return url.toString();
|
23
22
|
};
|
24
23
|
|
@@ -77,14 +77,11 @@ const TasksTablePage = ({
|
|
77
77
|
</Button>
|
78
78
|
{props.status === STATUS.PENDING && <Spinner size="md" loading />}
|
79
79
|
<ExportButton
|
80
|
-
url={getCSVurl(
|
80
|
+
url={getCSVurl(url, uriQuery)}
|
81
81
|
title={__('Export All')}
|
82
82
|
/>
|
83
83
|
<ActionSelectButton
|
84
|
-
disabled={
|
85
|
-
!props.permissions.edit ||
|
86
|
-
!(props.selectedRows.length || props.allRowsSelected)
|
87
|
-
}
|
84
|
+
disabled={!(props.selectedRows.length || props.allRowsSelected)}
|
88
85
|
onCancel={() => openModal(CANCEL_SELECTED_MODAL)}
|
89
86
|
onResume={() => openModal(RESUME_SELECTED_MODAL)}
|
90
87
|
onForceCancel={() => openModal(FORCE_UNLOCK_SELECTED_MODAL)}
|
@@ -97,17 +94,15 @@ const TasksTablePage = ({
|
|
97
94
|
}
|
98
95
|
>
|
99
96
|
<React.Fragment>
|
100
|
-
{props.
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
/>
|
110
|
-
)}
|
97
|
+
{showSelectAll && props.itemCount >= props.pagination.perPage && (
|
98
|
+
<SelectAllAlert
|
99
|
+
itemCount={props.itemCount}
|
100
|
+
perPage={props.pagination.perPage}
|
101
|
+
selectAllRows={selectAllRows}
|
102
|
+
unselectAllRows={props.unselectAllRows}
|
103
|
+
allRowsSelected={props.allRowsSelected}
|
104
|
+
/>
|
105
|
+
)}
|
111
106
|
<TasksTable history={history} {...props} openModal={openModal} />
|
112
107
|
</React.Fragment>
|
113
108
|
</PageLayout>
|
@@ -136,9 +131,6 @@ TasksTablePage.propTypes = {
|
|
136
131
|
showSelectAll: PropTypes.bool,
|
137
132
|
unselectAllRows: PropTypes.func.isRequired,
|
138
133
|
reloadPage: PropTypes.func.isRequired,
|
139
|
-
permissions: PropTypes.shape({
|
140
|
-
edit: PropTypes.bool,
|
141
|
-
}),
|
142
134
|
};
|
143
135
|
|
144
136
|
TasksTablePage.defaultProps = {
|
@@ -154,9 +146,6 @@ TasksTablePage.defaultProps = {
|
|
154
146
|
createHeader: () => __('Tasks'),
|
155
147
|
showSelectAll: false,
|
156
148
|
modalID: '',
|
157
|
-
permissions: {
|
158
|
-
edit: false,
|
159
|
-
},
|
160
149
|
};
|
161
150
|
|
162
151
|
export default TasksTablePage;
|
@@ -19,13 +19,8 @@ const initialState = Immutable({
|
|
19
19
|
|
20
20
|
export const TasksTableQueryReducer = (state = initialState, action) => {
|
21
21
|
const { type, payload, response } = action;
|
22
|
-
const {
|
23
|
-
|
24
|
-
page,
|
25
|
-
per_page: perPageString,
|
26
|
-
action_name: actionName,
|
27
|
-
can_edit: canEdit,
|
28
|
-
} = response || {};
|
22
|
+
const { subtotal, page, per_page: perPageString, action_name: actionName } =
|
23
|
+
response || {};
|
29
24
|
const ACTION_TYPES = createTableActionTypes(TASKS_TABLE_ID);
|
30
25
|
switch (type) {
|
31
26
|
case SELECT_ALL_ROWS:
|
@@ -39,22 +34,26 @@ export const TasksTableQueryReducer = (state = initialState, action) => {
|
|
39
34
|
perPage: Number(perPageString),
|
40
35
|
},
|
41
36
|
selectedRows: [],
|
42
|
-
permissions: {
|
43
|
-
edit: canEdit,
|
44
|
-
},
|
45
37
|
});
|
46
38
|
case SELECT_ROWS:
|
47
39
|
return state.set('selectedRows', union(payload, state.selectedRows));
|
48
40
|
case OPEN_SELECT_ALL:
|
49
41
|
return state.set('showSelectAll', true);
|
50
42
|
case UNSELECT_ROWS:
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
43
|
+
if (state.allRowsSelected) {
|
44
|
+
// User can unselect rows if only the page rows are selected
|
45
|
+
return state
|
46
|
+
.set(
|
47
|
+
'selectedRows',
|
48
|
+
payload.results.map(row => row.id).filter(row => row !== payload.id)
|
49
|
+
)
|
50
|
+
.set('allRowsSelected', false)
|
51
|
+
.set('showSelectAll', false);
|
52
|
+
}
|
53
|
+
return state.set(
|
54
|
+
'selectedRows',
|
55
|
+
state.selectedRows.filter(row => row !== payload.id)
|
56
|
+
);
|
58
57
|
case UNSELECT_ALL_ROWS:
|
59
58
|
return state
|
60
59
|
.set('selectedRows', [])
|
@@ -24,9 +24,6 @@ export const selectActionName = state =>
|
|
24
24
|
export const selectSelectedRows = state =>
|
25
25
|
selectTasksTableQuery(state).selectedRows || [];
|
26
26
|
|
27
|
-
export const selectPermissions = state =>
|
28
|
-
selectTasksTableQuery(state).permissions || { edit: false };
|
29
|
-
|
30
27
|
export const selectResults = createSelector(
|
31
28
|
selectTasksTableContent,
|
32
29
|
({ results }) =>
|
@@ -32,7 +32,7 @@ describe('getCSVurl', () => {
|
|
32
32
|
const url = '/foreman_tasks/tasks';
|
33
33
|
const query = { state: 'stopped' };
|
34
34
|
expect(getCSVurl(url, query)).toEqual(
|
35
|
-
'/foreman_tasks/tasks.csv?
|
35
|
+
'/foreman_tasks/tasks.csv?state=stopped'
|
36
36
|
);
|
37
37
|
});
|
38
38
|
it('should return currect url for subtasks', () => {
|
@@ -73,9 +73,7 @@ const fixtures = {
|
|
73
73
|
},
|
74
74
|
},
|
75
75
|
'should handle UNSELECT_ROWS with all rows selected': {
|
76
|
-
state: Immutable({
|
77
|
-
tasksTableQuery: { allRowsSelected: true, selectedRows: [3, 4, 5] },
|
78
|
-
}),
|
76
|
+
state: Immutable({ tasksTableQuery: { allRowsSelected: true } }),
|
79
77
|
action: {
|
80
78
|
type: UNSELECT_ROWS,
|
81
79
|
payload: { id: [4], results: [{ id: 3 }, { id: 4 }, { id: 5 }] },
|
data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap
CHANGED
@@ -92,7 +92,7 @@ exports[`TasksTablePage rendering render with Breadcrubs and edit permissions 1`
|
|
92
92
|
/>
|
93
93
|
<ExportButton
|
94
94
|
title="Export All"
|
95
|
-
url="/foreman_tasks/tasks.csv?
|
95
|
+
url="/foreman_tasks/tasks.csv?action=%22some-name%22&state=stopped"
|
96
96
|
/>
|
97
97
|
<ActionSelectButton
|
98
98
|
disabled={true}
|
@@ -126,11 +126,6 @@ exports[`TasksTablePage rendering render with Breadcrubs and edit permissions 1`
|
|
126
126
|
}
|
127
127
|
}
|
128
128
|
parentTaskID={null}
|
129
|
-
permissions={
|
130
|
-
Object {
|
131
|
-
"edit": false,
|
132
|
-
}
|
133
|
-
}
|
134
129
|
reloadPage={[MockFunction]}
|
135
130
|
results={
|
136
131
|
Array [
|
@@ -232,7 +227,7 @@ exports[`TasksTablePage rendering render with minimal props 1`] = `
|
|
232
227
|
/>
|
233
228
|
<ExportButton
|
234
229
|
title="Export All"
|
235
|
-
url="/foreman_tasks/tasks.csv?
|
230
|
+
url="/foreman_tasks/tasks.csv?action=%22some-name%22&state=stopped"
|
236
231
|
/>
|
237
232
|
<ActionSelectButton
|
238
233
|
disabled={true}
|
@@ -266,11 +261,6 @@ exports[`TasksTablePage rendering render with minimal props 1`] = `
|
|
266
261
|
}
|
267
262
|
}
|
268
263
|
parentTaskID={null}
|
269
|
-
permissions={
|
270
|
-
Object {
|
271
|
-
"edit": false,
|
272
|
-
}
|
273
|
-
}
|
274
264
|
reloadPage={[MockFunction]}
|
275
265
|
results={
|
276
266
|
Array [
|
@@ -59,9 +59,6 @@ Object {
|
|
59
59
|
"page": 3,
|
60
60
|
"perPage": 12,
|
61
61
|
},
|
62
|
-
"permissions": Object {
|
63
|
-
"edit": undefined,
|
64
|
-
},
|
65
62
|
"selectedRows": Array [],
|
66
63
|
},
|
67
64
|
}
|
@@ -80,9 +77,7 @@ Object {
|
|
80
77
|
exports[`TasksTableReducer reducer should handle UNSELECT_ROWS 1`] = `
|
81
78
|
Object {
|
82
79
|
"tasksTableQuery": Object {
|
83
|
-
"allRowsSelected": false,
|
84
80
|
"selectedRows": Array [],
|
85
|
-
"showSelectAll": false,
|
86
81
|
},
|
87
82
|
}
|
88
83
|
`;
|
@@ -16,7 +16,6 @@ import {
|
|
16
16
|
selectAllRowsSelected,
|
17
17
|
selectShowSelectAll,
|
18
18
|
selectModalID,
|
19
|
-
selectPermissions,
|
20
19
|
} from './TasksTableSelectors';
|
21
20
|
|
22
21
|
const mapStateToProps = state => ({
|
@@ -31,7 +30,6 @@ const mapStateToProps = state => ({
|
|
31
30
|
allRowsSelected: selectAllRowsSelected(state),
|
32
31
|
showSelectAll: selectShowSelectAll(state),
|
33
32
|
modalID: selectModalID(state),
|
34
|
-
permissions: selectPermissions(state),
|
35
33
|
});
|
36
34
|
|
37
35
|
const mapDispatchToProps = dispatch =>
|