foreman-tasks 5.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/foreman_tasks/api/tasks_controller.rb +4 -4
  3. data/app/controllers/foreman_tasks/tasks_controller.rb +5 -4
  4. data/app/graphql/types/recurring_logic.rb +1 -0
  5. data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +4 -1
  6. data/app/lib/actions/middleware/watch_delegated_proxy_sub_tasks.rb +2 -6
  7. data/app/lib/actions/trigger_proxy_batch.rb +79 -0
  8. data/app/models/foreman_tasks/recurring_logic.rb +8 -0
  9. data/app/models/foreman_tasks/task/dynflow_task.rb +8 -3
  10. data/app/models/foreman_tasks/task.rb +1 -0
  11. data/app/models/foreman_tasks/triggering.rb +12 -4
  12. data/app/views/foreman_tasks/api/tasks/show.json.rabl +1 -1
  13. data/app/views/foreman_tasks/layouts/react.html.erb +0 -1
  14. data/app/views/foreman_tasks/recurring_logics/index.html.erb +4 -2
  15. data/app/views/foreman_tasks/task_groups/recurring_logic_task_groups/_recurring_logic_task_group.html.erb +4 -0
  16. data/db/migrate/20210720115251_add_purpose_to_recurring_logic.rb +6 -0
  17. data/extra/foreman-tasks-cleanup.sh +127 -0
  18. data/extra/foreman-tasks-export.sh +117 -0
  19. data/lib/foreman_tasks/tasks/export_tasks.rake +92 -47
  20. data/lib/foreman_tasks/version.rb +1 -1
  21. data/test/controllers/api/tasks_controller_test.rb +29 -0
  22. data/test/controllers/tasks_controller_test.rb +19 -0
  23. data/test/unit/actions/trigger_proxy_batch_test.rb +59 -0
  24. data/test/unit/triggering_test.rb +22 -0
  25. data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.js +11 -4
  26. data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.test.js +27 -5
  27. data/webpack/ForemanTasks/Components/TasksTable/TasksTable.js +8 -0
  28. data/webpack/ForemanTasks/Components/TasksTable/TasksTableActions.js +6 -1
  29. data/webpack/ForemanTasks/Components/TasksTable/TasksTableHelpers.js +2 -1
  30. data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +22 -11
  31. data/webpack/ForemanTasks/Components/TasksTable/TasksTableReducer.js +17 -16
  32. data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +3 -0
  33. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableHelpers.test.js +1 -1
  34. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableReducer.test.js +3 -1
  35. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +12 -2
  36. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableReducer.test.js.snap +5 -0
  37. data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/selectionHeaderCellFormatter.test.js.snap +1 -0
  38. data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/selectionHeaderCellFormatter.test.js +1 -1
  39. data/webpack/ForemanTasks/Components/TasksTable/formatters/selectionHeaderCellFormatter.js +1 -0
  40. data/webpack/ForemanTasks/Components/TasksTable/index.js +2 -0
  41. metadata +8 -5
  42. data/app/services/foreman_tasks/dashboard_table_filter.rb +0 -56
  43. data/test/unit/dashboard_table_filter_test.rb +0 -77
@@ -15,22 +15,29 @@ export const convertDashboardQuery = query => {
15
15
  state,
16
16
  result,
17
17
  search,
18
+ ...rest
18
19
  } = query;
19
20
 
20
21
  const hours = timeToHoursNumber(timeHorizon);
21
22
  const timestamp = new Date(new Date() - hours * 60 * 60 * 1000);
22
23
  let dashboardTime = '';
23
24
  const stateQuery = state ? `state=${state}` : '';
24
- const resultQuery = result ? `result=${result}` : '';
25
+ let resultQuery = '';
26
+ if (result === 'other') {
27
+ resultQuery = 'result ^ (pending, cancelled)';
28
+ } else {
29
+ resultQuery = result ? `result=${result}` : '';
30
+ }
25
31
  if (timeMode === TASKS_DASHBOARD_JS_QUERY_MODES.RECENT) {
26
- dashboardTime = `(state_updated_at>${timestamp.toISOString()} or state_updated_at = NULL)`;
32
+ dashboardTime = `state_updated_at>${timestamp.toISOString()} or null? state_updated_at`;
27
33
  } else if (timeMode === TASKS_DASHBOARD_JS_QUERY_MODES.OLDER) {
28
- dashboardTime = `(state_updated_at>${timestamp.toISOString()})`;
34
+ dashboardTime = `state_updated_at<=${timestamp.toISOString()}`;
29
35
  }
30
36
  const newQuery = [stateQuery, resultQuery, search, dashboardTime]
31
37
  .filter(Boolean)
38
+ .map(q => `(${q})`)
32
39
  .join(' and ');
33
- return newQuery;
40
+ return newQuery ? { search: newQuery, ...rest } : rest;
34
41
  };
35
42
 
36
43
  export const resumeToastInfo = {
@@ -27,9 +27,18 @@ describe('convertDashboardQuery', () => {
27
27
  result: 'error',
28
28
  search: 'action~job',
29
29
  };
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
- );
30
+ const expected =
31
+ '(state=stopped) and (result=error) and (action~job) and (state_updated_at>2020-05-01T11:01:58.135Z or null? state_updated_at)';
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 });
33
42
  // Cleanup
34
43
  global.Date = realDate;
35
44
  });
@@ -37,10 +46,23 @@ describe('convertDashboardQuery', () => {
37
46
  const query = {
38
47
  search: 'action~job',
39
48
  };
40
- expect(convertDashboardQuery(query)).toEqual('action~job');
49
+ expect(convertDashboardQuery(query)).toEqual({ search: '(action~job)' });
41
50
  });
42
51
  it('convertDashboardQuery should work with no query', () => {
43
52
  const query = {};
44
- expect(convertDashboardQuery(query)).toEqual('');
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
+ });
45
67
  });
46
68
  });
@@ -27,6 +27,7 @@ const TasksTable = ({
27
27
  openClickedModal,
28
28
  openModal,
29
29
  allRowsSelected,
30
+ permissions,
30
31
  }) => {
31
32
  const { search, pathname } = history.location;
32
33
  const url = pathname + search;
@@ -59,6 +60,7 @@ const TasksTable = ({
59
60
  },
60
61
  isSelected: ({ rowData }) =>
61
62
  allRowsSelected || selectedRows.includes(rowData.id),
63
+ permissions,
62
64
  };
63
65
  };
64
66
 
@@ -162,6 +164,9 @@ TasksTable.propTypes = {
162
164
  unselectRow: PropTypes.func.isRequired,
163
165
  openModal: PropTypes.func.isRequired,
164
166
  allRowsSelected: PropTypes.bool,
167
+ permissions: PropTypes.shape({
168
+ edit: PropTypes.bool,
169
+ }),
165
170
  };
166
171
 
167
172
  TasksTable.defaultProps = {
@@ -173,6 +178,9 @@ TasksTable.defaultProps = {
173
178
  },
174
179
  selectedRows: [],
175
180
  allRowsSelected: false,
181
+ permissions: {
182
+ edit: false,
183
+ },
176
184
  };
177
185
 
178
186
  export default TasksTable;
@@ -18,9 +18,14 @@ import {
18
18
  resumeTaskRequest,
19
19
  forceCancelTaskRequest,
20
20
  } from '../TaskActions';
21
+ import { convertDashboardQuery } from '../TaskActions/TaskActionHelpers';
21
22
 
22
23
  export const getTableItems = url =>
23
- getTableItemsAction(TASKS_TABLE_ID, getURIQuery(url), getApiPathname(url));
24
+ getTableItemsAction(
25
+ TASKS_TABLE_ID,
26
+ convertDashboardQuery(getURIQuery(url)),
27
+ getApiPathname(url)
28
+ );
24
29
 
25
30
  export const reloadPage = (url, parentTaskID) => dispatch => {
26
31
  dispatch(getTableItems(url));
@@ -2,6 +2,7 @@ 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';
5
6
 
6
7
  export const updateURlQuery = (query, history) => {
7
8
  const uri = new URI(history.location.pathname + history.location.search);
@@ -17,7 +18,7 @@ export const getApiPathname = url => {
17
18
  export const getCSVurl = (path, query) => {
18
19
  let url = new URI(path);
19
20
  url = url.pathname(`${url.pathname()}.csv`);
20
- url.addSearch(query);
21
+ url.addSearch(convertDashboardQuery(query));
21
22
  return url.toString();
22
23
  };
23
24
 
@@ -77,11 +77,14 @@ const TasksTablePage = ({
77
77
  </Button>
78
78
  {props.status === STATUS.PENDING && <Spinner size="md" loading />}
79
79
  <ExportButton
80
- url={getCSVurl(url, uriQuery)}
80
+ url={getCSVurl(history.location.pathname, uriQuery)}
81
81
  title={__('Export All')}
82
82
  />
83
83
  <ActionSelectButton
84
- disabled={!(props.selectedRows.length || props.allRowsSelected)}
84
+ disabled={
85
+ !props.permissions.edit ||
86
+ !(props.selectedRows.length || props.allRowsSelected)
87
+ }
85
88
  onCancel={() => openModal(CANCEL_SELECTED_MODAL)}
86
89
  onResume={() => openModal(RESUME_SELECTED_MODAL)}
87
90
  onForceCancel={() => openModal(FORCE_UNLOCK_SELECTED_MODAL)}
@@ -94,15 +97,17 @@ const TasksTablePage = ({
94
97
  }
95
98
  >
96
99
  <React.Fragment>
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
- )}
100
+ {props.permissions.edit &&
101
+ showSelectAll &&
102
+ props.itemCount >= props.pagination.perPage && (
103
+ <SelectAllAlert
104
+ itemCount={props.itemCount}
105
+ perPage={props.pagination.perPage}
106
+ selectAllRows={selectAllRows}
107
+ unselectAllRows={props.unselectAllRows}
108
+ allRowsSelected={props.allRowsSelected}
109
+ />
110
+ )}
106
111
  <TasksTable history={history} {...props} openModal={openModal} />
107
112
  </React.Fragment>
108
113
  </PageLayout>
@@ -131,6 +136,9 @@ TasksTablePage.propTypes = {
131
136
  showSelectAll: PropTypes.bool,
132
137
  unselectAllRows: PropTypes.func.isRequired,
133
138
  reloadPage: PropTypes.func.isRequired,
139
+ permissions: PropTypes.shape({
140
+ edit: PropTypes.bool,
141
+ }),
134
142
  };
135
143
 
136
144
  TasksTablePage.defaultProps = {
@@ -146,6 +154,9 @@ TasksTablePage.defaultProps = {
146
154
  createHeader: () => __('Tasks'),
147
155
  showSelectAll: false,
148
156
  modalID: '',
157
+ permissions: {
158
+ edit: false,
159
+ },
149
160
  };
150
161
 
151
162
  export default TasksTablePage;
@@ -19,8 +19,13 @@ const initialState = Immutable({
19
19
 
20
20
  export const TasksTableQueryReducer = (state = initialState, action) => {
21
21
  const { type, payload, response } = action;
22
- const { subtotal, page, per_page: perPageString, action_name: actionName } =
23
- response || {};
22
+ const {
23
+ subtotal,
24
+ page,
25
+ per_page: perPageString,
26
+ action_name: actionName,
27
+ can_edit: canEdit,
28
+ } = response || {};
24
29
  const ACTION_TYPES = createTableActionTypes(TASKS_TABLE_ID);
25
30
  switch (type) {
26
31
  case SELECT_ALL_ROWS:
@@ -34,26 +39,22 @@ export const TasksTableQueryReducer = (state = initialState, action) => {
34
39
  perPage: Number(perPageString),
35
40
  },
36
41
  selectedRows: [],
42
+ permissions: {
43
+ edit: canEdit,
44
+ },
37
45
  });
38
46
  case SELECT_ROWS:
39
47
  return state.set('selectedRows', union(payload, state.selectedRows));
40
48
  case OPEN_SELECT_ALL:
41
49
  return state.set('showSelectAll', true);
42
50
  case UNSELECT_ROWS:
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
- );
51
+ return state
52
+ .set(
53
+ 'selectedRows',
54
+ state.selectedRows.filter(row => row !== payload.id)
55
+ )
56
+ .set('showSelectAll', false)
57
+ .set('allRowsSelected', false);
57
58
  case UNSELECT_ALL_ROWS:
58
59
  return state
59
60
  .set('selectedRows', [])
@@ -24,6 +24,9 @@ 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
+
27
30
  export const selectResults = createSelector(
28
31
  selectTasksTableContent,
29
32
  ({ 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?state=stopped'
35
+ '/foreman_tasks/tasks.csv?search=%28state%3Dstopped%29'
36
36
  );
37
37
  });
38
38
  it('should return currect url for subtasks', () => {
@@ -73,7 +73,9 @@ const fixtures = {
73
73
  },
74
74
  },
75
75
  'should handle UNSELECT_ROWS with all rows selected': {
76
- state: Immutable({ tasksTableQuery: { allRowsSelected: true } }),
76
+ state: Immutable({
77
+ tasksTableQuery: { allRowsSelected: true, selectedRows: [3, 4, 5] },
78
+ }),
77
79
  action: {
78
80
  type: UNSELECT_ROWS,
79
81
  payload: { id: [4], results: [{ id: 3 }, { id: 4 }, { id: 5 }] },
@@ -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?action=%22some-name%22&state=stopped"
95
+ url="/foreman_tasks/tasks.csv?search=%28state%3Dstopped%29"
96
96
  />
97
97
  <ActionSelectButton
98
98
  disabled={true}
@@ -126,6 +126,11 @@ 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
+ }
129
134
  reloadPage={[MockFunction]}
130
135
  results={
131
136
  Array [
@@ -227,7 +232,7 @@ exports[`TasksTablePage rendering render with minimal props 1`] = `
227
232
  />
228
233
  <ExportButton
229
234
  title="Export All"
230
- url="/foreman_tasks/tasks.csv?action=%22some-name%22&state=stopped"
235
+ url="/foreman_tasks/tasks.csv?search=%28state%3Dstopped%29"
231
236
  />
232
237
  <ActionSelectButton
233
238
  disabled={true}
@@ -261,6 +266,11 @@ exports[`TasksTablePage rendering render with minimal props 1`] = `
261
266
  }
262
267
  }
263
268
  parentTaskID={null}
269
+ permissions={
270
+ Object {
271
+ "edit": false,
272
+ }
273
+ }
264
274
  reloadPage={[MockFunction]}
265
275
  results={
266
276
  Array [
@@ -59,6 +59,9 @@ Object {
59
59
  "page": 3,
60
60
  "perPage": 12,
61
61
  },
62
+ "permissions": Object {
63
+ "edit": undefined,
64
+ },
62
65
  "selectedRows": Array [],
63
66
  },
64
67
  }
@@ -77,7 +80,9 @@ Object {
77
80
  exports[`TasksTableReducer reducer should handle UNSELECT_ROWS 1`] = `
78
81
  Object {
79
82
  "tasksTableQuery": Object {
83
+ "allRowsSelected": false,
80
84
  "selectedRows": Array [],
85
+ "showSelectAll": false,
81
86
  },
82
87
  }
83
88
  `;
@@ -3,6 +3,7 @@
3
3
  exports[`selectionHeaderCellFormatter render 1`] = `
4
4
  <TableSelectionHeaderCell
5
5
  checked={true}
6
+ disabled={false}
6
7
  id="selectAll"
7
8
  label="some-label"
8
9
  onChange={[Function]}
@@ -4,7 +4,7 @@ describe('selectionHeaderCellFormatter', () => {
4
4
  it('render', () => {
5
5
  expect(
6
6
  selectionHeaderCellFormatter(
7
- { allPageSelected: () => true },
7
+ { allPageSelected: () => true, permissions: { edit: true } },
8
8
  'some-label'
9
9
  )
10
10
  ).toMatchSnapshot();
@@ -5,6 +5,7 @@ export default (selectionController, label) => (
5
5
  <TableSelectionHeaderCell
6
6
  label={label}
7
7
  checked={selectionController.allPageSelected()}
8
+ disabled={!selectionController.permissions.edit}
8
9
  onChange={selectionController.selectPage}
9
10
  />
10
11
  );
@@ -16,6 +16,7 @@ import {
16
16
  selectAllRowsSelected,
17
17
  selectShowSelectAll,
18
18
  selectModalID,
19
+ selectPermissions,
19
20
  } from './TasksTableSelectors';
20
21
 
21
22
  const mapStateToProps = state => ({
@@ -30,6 +31,7 @@ const mapStateToProps = state => ({
30
31
  allRowsSelected: selectAllRowsSelected(state),
31
32
  showSelectAll: selectShowSelectAll(state),
32
33
  modalID: selectModalID(state),
34
+ permissions: selectPermissions(state),
33
35
  });
34
36
 
35
37
  const mapDispatchToProps = dispatch =>
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: 5.0.0
4
+ version: 5.1.0
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: 2021-06-15 00:00:00.000000000 Z
11
+ date: 2021-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dynflow
@@ -165,6 +165,7 @@ files:
165
165
  - app/lib/actions/proxy_action.rb
166
166
  - app/lib/actions/recurring_action.rb
167
167
  - app/lib/actions/serializers/active_record_serializer.rb
168
+ - app/lib/actions/trigger_proxy_batch.rb
168
169
  - app/lib/foreman_tasks/concerns/polling_action_extensions.rb
169
170
  - app/lib/proxy_api/foreman_dynflow/dynflow_proxy.rb
170
171
  - app/models/foreman_tasks/concerns/action_subject.rb
@@ -187,7 +188,6 @@ files:
187
188
  - app/models/foreman_tasks/task_groups/recurring_logic_task_group.rb
188
189
  - app/models/foreman_tasks/triggering.rb
189
190
  - app/models/setting/foreman_tasks.rb
190
- - app/services/foreman_tasks/dashboard_table_filter.rb
191
191
  - app/services/foreman_tasks/proxy_selector.rb
192
192
  - app/services/foreman_tasks/troubleshooting_help_generator.rb
193
193
  - app/services/ui_notifications/tasks.rb
@@ -249,6 +249,7 @@ files:
249
249
  - db/migrate/20200517215015_rename_bookmarks_controller.rb
250
250
  - db/migrate/20200519093217_drop_dynflow_allow_dangerous_actions_setting.foreman_tasks.rb
251
251
  - db/migrate/20200611090846_add_task_lock_index_on_resource_type_and_task_id.rb
252
+ - db/migrate/20210720115251_add_purpose_to_recurring_logic.rb
252
253
  - db/seeds.d/20-foreman_tasks_permissions.rb
253
254
  - db/seeds.d/30-notification_blueprints.rb
254
255
  - db/seeds.d/60-dynflow_proxy_feature.rb
@@ -257,6 +258,8 @@ files:
257
258
  - deploy/foreman-tasks.sysconfig
258
259
  - extra/dynflow-debug.sh
259
260
  - extra/dynflow-executor.example
261
+ - extra/foreman-tasks-cleanup.sh
262
+ - extra/foreman-tasks-export.sh
260
263
  - foreman-tasks.gemspec
261
264
  - gemfile.d/foreman-tasks.rb
262
265
  - lib/foreman-tasks.rb
@@ -323,9 +326,9 @@ files:
323
326
  - test/unit/actions/bulk_action_test.rb
324
327
  - test/unit/actions/proxy_action_test.rb
325
328
  - test/unit/actions/recurring_action_test.rb
329
+ - test/unit/actions/trigger_proxy_batch_test.rb
326
330
  - test/unit/cleaner_test.rb
327
331
  - test/unit/config/environment.rb
328
- - test/unit/dashboard_table_filter_test.rb
329
332
  - test/unit/dynflow_console_authorizer_test.rb
330
333
  - test/unit/locking_test.rb
331
334
  - test/unit/proxy_selector_test.rb
@@ -637,9 +640,9 @@ test_files:
637
640
  - test/unit/actions/bulk_action_test.rb
638
641
  - test/unit/actions/proxy_action_test.rb
639
642
  - test/unit/actions/recurring_action_test.rb
643
+ - test/unit/actions/trigger_proxy_batch_test.rb
640
644
  - test/unit/cleaner_test.rb
641
645
  - test/unit/config/environment.rb
642
- - test/unit/dashboard_table_filter_test.rb
643
646
  - test/unit/dynflow_console_authorizer_test.rb
644
647
  - test/unit/locking_test.rb
645
648
  - test/unit/proxy_selector_test.rb