foreman-tasks 13.1.0 → 13.2.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 (21) hide show
  1. checksums.yaml +4 -4
  2. data/lib/foreman_tasks/version.rb +1 -1
  3. data/webpack/ForemanTasks/Components/Chart/Chart.js +3 -1
  4. data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.js +8 -1
  5. data/webpack/ForemanTasks/Components/TaskActions/TaskActionsConstants.js +1 -0
  6. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskButtons.scss +2 -2
  7. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskHelper.js +4 -1
  8. data/webpack/ForemanTasks/Components/TaskDetails/ExecutionDetails.js +13 -7
  9. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.scss +1 -0
  10. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsActions.js +6 -2
  11. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsConstants.js +2 -0
  12. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +2 -2
  13. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/ExecutionDetails.test.js +53 -6
  14. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.scss +0 -1
  15. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.scss +1 -0
  16. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.scss +1 -0
  17. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardHelper.js +9 -4
  18. data/webpack/ForemanTasks/Components/TasksTable/TasksTableHelpers.js +2 -1
  19. data/webpack/ForemanTasks/Components/common/taskResultIcon.js +39 -29
  20. data/webpack/ForemanTasks/Routes/ShowTaskDetails/TaskDetailsHeader.js +1 -1
  21. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 270e537ec64af9327b2598da48e8422a56adc1716e45ee5105cb57c9bd2d1078
4
- data.tar.gz: da3627e82bdecfcd63516cf3e973461b22762023374ee58ac105e7f9e66c2cf7
3
+ metadata.gz: a192677704918d5f7e81554fb38e70f389a795c021c4889f6f910fa992a1bed7
4
+ data.tar.gz: 000732f9d423e0bc8ba6293f7150a631de785535a94d6b5004d40922768897d1
5
5
  SHA512:
6
- metadata.gz: dc37e5ebbdb2aa62a131d2828a619f00c68b1c6c262a78df83671801b7047eeafd2bd31fd43e52fff79d7a9b2bb69267a950bd2e71babc6569c20d55a90caab2
7
- data.tar.gz: ba180fe7ad221035c14474ed9b1cc62795d2d90ff585fdc0304bda52432e43917829d016abd03e18e68ffcb9654f1a73864695d93fe1219214f7a14d43de0033
6
+ metadata.gz: d6e131b3624a79ef45aca8c87674938bcc7b2b8c1c0be6652251b6818f0bb1a4ad3843b976d2394901ffe861d7e153fdaeb764c1b44c0cdcbddb46d73d06c6f9
7
+ data.tar.gz: 95888cc143cb58b40d0485d6c67f71c95e9e8e88d08f086b31982f9cfc3693aee68cecd885984581e7fb704feee3978d6e601551b6b200413036f6cbfd0c3336
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '13.1.0'.freeze
2
+ VERSION = '13.2.0'.freeze
3
3
  end
@@ -17,6 +17,8 @@ import React from 'react';
17
17
  import PropTypes from 'prop-types';
18
18
  import { findDOMNode } from 'react-dom';
19
19
 
20
+ const DESTROY_DELAY_MS = 1000;
21
+
20
22
  let c3;
21
23
 
22
24
  class C3Chart extends React.Component {
@@ -44,7 +46,7 @@ class C3Chart extends React.Component {
44
46
  // Delaying the destroy a bit seems to resolve the issue.
45
47
  // The chart API methods are already bind explicitly, therefore we don't need
46
48
  // any special handling when passing the function.
47
- setTimeout(this.chart.destroy, 1000);
49
+ setTimeout(this.chart.destroy, DESTROY_DELAY_MS);
48
50
  this.chart = null;
49
51
  } catch (err) {
50
52
  throw new Error('Internal C3 error', err);
@@ -1,6 +1,11 @@
1
1
  import { translate as __, sprintf } from 'foremanReact/common/I18n';
2
2
  import { addToast } from 'foremanReact/components/ToastsList';
3
3
  import { getURIQuery } from 'foremanReact/common/helpers';
4
+ import {
5
+ MINUTES_PER_HOUR,
6
+ SECONDS_PER_MINUTE,
7
+ MS_PER_SECOND,
8
+ } from 'foremanReact/constants';
4
9
  import { TASKS_DASHBOARD_JS_QUERY_MODES } from '../TasksDashboard/TasksDashboardConstants';
5
10
  import { timeToHoursNumber } from '../TasksDashboard/TasksDashboardHelper';
6
11
  import {
@@ -25,7 +30,9 @@ export const convertDashboardQuery = () => {
25
30
  } = getTasksQuery();
26
31
 
27
32
  const hours = timeToHoursNumber(timeHorizon);
28
- const timestamp = new Date(new Date() - hours * 60 * 60 * 1000);
33
+ const timestamp = new Date(
34
+ new Date() - hours * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MS_PER_SECOND
35
+ );
29
36
  let dashboardTime = '';
30
37
  const stateQuery = state ? `state=${state}` : '';
31
38
  let resultQuery = '';
@@ -14,3 +14,4 @@ export const TASKS_FORCE_CANCEL_FAILURE = 'TASKS_FORCE_CANCEL_FAILURE';
14
14
  export const TASKS_UNLOCK_REQUEST = 'TASKS_UNLOCK_REQUEST';
15
15
  export const TASKS_UNLOCK_SUCCESS = 'TASKS_UNLOCK_SUCCESS';
16
16
  export const TASKS_UNLOCK_FAILURE = 'TASKS_UNLOCK_FAILURE';
17
+
@@ -9,7 +9,7 @@
9
9
  -ms-transform-origin: 50% 50%; /* IE 9 */
10
10
  }
11
11
 
12
- @-moz-keyframes spin {
12
+ @keyframes spin {
13
13
  from {
14
14
  -moz-transform: rotate(0deg);
15
15
  }
@@ -19,7 +19,7 @@
19
19
  }
20
20
  }
21
21
 
22
- @-webkit-keyframes spin {
22
+ @keyframes spin {
23
23
  from {
24
24
  -webkit-transform: rotate(0deg);
25
25
  }
@@ -1,6 +1,7 @@
1
1
  import { translate as __, documentLocale } from 'foremanReact/common/I18n';
2
2
  import { isoCompatibleDate } from 'foremanReact/common/helpers';
3
3
  import humanizeDuration from 'humanize-duration';
4
+ import { MS_PER_SECOND } from 'foremanReact/constants';
4
5
 
5
6
  export const durationInWords = (
6
7
  start,
@@ -16,7 +17,9 @@ export const durationInWords = (
16
17
  language: selectedLocale,
17
18
  fallbacks: ['en'],
18
19
  }),
19
- tooltip: `${numberWithDelimiter((finish - start) / 1000)} ${__('seconds')}`,
20
+ tooltip: `${numberWithDelimiter((finish - start) / MS_PER_SECOND)} ${__(
21
+ 'seconds'
22
+ )}`,
20
23
  };
21
24
  };
22
25
 
@@ -14,18 +14,23 @@ const ExecutionDetails = ({
14
14
  failedSteps,
15
15
  result,
16
16
  }) => {
17
- const showingRunningSteps =
18
- state === 'running' ||
19
- state === 'pending' ||
20
- state === 'paused' ||
21
- runningSteps.length > 0;
17
+ const hasFailedSteps = failedSteps.length > 0;
18
+ const hasRunningSteps = runningSteps.length > 0;
19
+ const isActiveExecutionState =
20
+ state === 'running' || state === 'pending' || state === 'paused';
21
+
22
+ // Prefer Errors when failed steps exist (e.g. paused-after-error). Only fall
23
+ // back to the RunningSteps empty state for active tasks with no failures.
24
+ const showRunningSteps =
25
+ hasRunningSteps || (isActiveExecutionState && !hasFailedSteps);
26
+ const showErrors = hasFailedSteps || !showRunningSteps;
22
27
 
23
28
  return (
24
29
  <div
25
30
  id="execution-details-panel"
26
31
  data-ouia-component-id="execution-details-panel"
27
32
  >
28
- {showingRunningSteps ? (
33
+ {showRunningSteps && (
29
34
  <RunningSteps
30
35
  executionPlan={executionPlan}
31
36
  result={result}
@@ -35,7 +40,8 @@ const ExecutionDetails = ({
35
40
  taskReload={taskReload}
36
41
  taskReloadStart={taskReloadStart}
37
42
  />
38
- ) : (
43
+ )}
44
+ {showErrors && (
39
45
  <Errors executionPlan={executionPlan} failedSteps={failedSteps} />
40
46
  )}
41
47
  </div>
@@ -3,6 +3,7 @@
3
3
  #pf-tab-section-1-task-details-tabs > button {
4
4
  margin-right: 3px;
5
5
  }
6
+
6
7
  .container {
7
8
  margin: 0;
8
9
  }
@@ -6,7 +6,11 @@ import {
6
6
  stopInterval,
7
7
  } from 'foremanReact/redux/middlewares/IntervalMiddleware';
8
8
  import { foremanTasksApiPath, foremanTasksPath } from '../common/urlHelpers';
9
- import { TASK_STEP_CANCEL, FOREMAN_TASK_DETAILS } from './TaskDetailsConstants';
9
+ import {
10
+ TASK_STEP_CANCEL,
11
+ FOREMAN_TASK_DETAILS,
12
+ TASK_RELOAD_INTERVAL_MS,
13
+ } from './TaskDetailsConstants';
10
14
  import {
11
15
  errorToastData,
12
16
  infoToastData,
@@ -30,7 +34,7 @@ export const taskReloadStart = id => dispatch => {
30
34
  dispatch(stopInterval(FOREMAN_TASK_DETAILS));
31
35
  },
32
36
  }),
33
- 5000
37
+ TASK_RELOAD_INTERVAL_MS
34
38
  )
35
39
  );
36
40
  };
@@ -3,3 +3,5 @@ export const FOREMAN_TASK_DETAILS_SUCCESS = 'FOREMAN_TASK_DETAILS_SUCCESS';
3
3
  export const TASKS_PATH = '/foreman_tasks/tasks';
4
4
  export const TASK_STEP_CANCEL = 'TASK_STEP_CANCEL';
5
5
  export const VIEW_FOREMAN_TASKS = 'view_foreman_tasks';
6
+
7
+ export const TASK_RELOAD_INTERVAL_MS = 5000;
@@ -5,7 +5,7 @@ import {
5
5
  selectAPIError as selectAPIErrorByKey,
6
6
  } from 'foremanReact/redux/API/APISelectors';
7
7
  import { selectDoesIntervalExist } from 'foremanReact/redux/middlewares/IntervalMiddleware/IntervalSelectors';
8
- import { STATUS } from 'foremanReact/constants';
8
+ import { STATUS, PERCENT_MULTIPLIER } from 'foremanReact/constants';
9
9
  import { selectForemanTasks } from '../../ForemanTasksSelectors';
10
10
  import { FOREMAN_TASK_DETAILS } from './TaskDetailsConstants';
11
11
 
@@ -41,7 +41,7 @@ export const selectCancellable = state =>
41
41
 
42
42
  export const selectProgress = state =>
43
43
  selectTaskDetailsResponse(state).progress
44
- ? Math.trunc(selectTaskDetailsResponse(state).progress * 100)
44
+ ? Math.trunc(selectTaskDetailsResponse(state).progress * PERCENT_MULTIPLIER)
45
45
  : 0;
46
46
 
47
47
  export const selectUsername = state =>
@@ -28,9 +28,13 @@ describe('ExecutionDetails', () => {
28
28
  );
29
29
 
30
30
  expect(
31
- document.querySelector('[data-ouia-component-id="execution-details-panel"]')
31
+ document.querySelector(
32
+ '[data-ouia-component-id="execution-details-panel"]'
33
+ )
34
+ ).toBeInTheDocument();
35
+ expect(
36
+ document.getElementById('execution-details-panel')
32
37
  ).toBeInTheDocument();
33
- expect(document.getElementById('execution-details-panel')).toBeInTheDocument();
34
38
  });
35
39
 
36
40
  it('shows Errors pane when stopped with no running steps', () => {
@@ -44,7 +48,9 @@ describe('ExecutionDetails', () => {
44
48
  />
45
49
  );
46
50
 
47
- expect(screen.getByRole('heading', { name: /^no errors found$/i })).toBeInTheDocument();
51
+ expect(
52
+ screen.getByRole('heading', { name: /^no errors found$/i })
53
+ ).toBeInTheDocument();
48
54
  expect(
49
55
  screen.getByText(/the task finished with no errors or warnings/i)
50
56
  ).toBeInTheDocument();
@@ -127,9 +133,7 @@ describe('ExecutionDetails', () => {
127
133
  name: /action actions::katello::eventqueue::monitor is already active/i,
128
134
  })
129
135
  ).toBeInTheDocument();
130
- expect(
131
- screen.getByLabelText(/failed task errors/i)
132
- ).toBeInTheDocument();
136
+ expect(screen.getByLabelText(/failed task errors/i)).toBeInTheDocument();
133
137
  expect(screen.getByText('Exception:')).toBeInTheDocument();
134
138
  });
135
139
 
@@ -191,4 +195,47 @@ describe('ExecutionDetails', () => {
191
195
  ).toBeInTheDocument();
192
196
  expect(screen.getByText('paused')).toBeInTheDocument();
193
197
  });
198
+
199
+ it('shows Errors when state is paused with failed steps', () => {
200
+ render(
201
+ <ExecutionDetails
202
+ {...rtlBaseProps}
203
+ {...fixtureFailedExecutionDetail}
204
+ state="paused"
205
+ executionPlan={{ state: 'paused', cancellable: false }}
206
+ runningSteps={[]}
207
+ />
208
+ );
209
+
210
+ expect(
211
+ screen.getByRole('tab', {
212
+ name: /action actions::katello::eventqueue::monitor is already active/i,
213
+ })
214
+ ).toBeInTheDocument();
215
+ expect(screen.getByLabelText(/failed task errors/i)).toBeInTheDocument();
216
+ expect(screen.getByText('Exception:')).toBeInTheDocument();
217
+ expect(screen.queryByText(/no running steps/i)).not.toBeInTheDocument();
218
+ });
219
+
220
+ it('shows Errors and RunningSteps when paused with both failed and running steps', () => {
221
+ render(
222
+ <ExecutionDetails
223
+ {...rtlBaseProps}
224
+ {...fixtureFailedExecutionDetail}
225
+ state="paused"
226
+ executionPlan={{ state: 'paused', cancellable: false }}
227
+ runningSteps={[
228
+ {
229
+ ...fixtureRunningExecutionDetail.runningSteps[0],
230
+ state: 'suspended',
231
+ },
232
+ ]}
233
+ />
234
+ );
235
+
236
+ expect(
237
+ screen.getByRole('heading', { name: 'Warning alert: Running step 1' })
238
+ ).toBeInTheDocument();
239
+ expect(screen.getByLabelText(/failed task errors/i)).toBeInTheDocument();
240
+ });
194
241
  });
@@ -1,6 +1,5 @@
1
1
  .stopped-tasks-card {
2
2
  .stopped-table {
3
-
4
3
  td,
5
4
  th {
6
5
  text-align: center;
@@ -5,6 +5,7 @@
5
5
  opacity: 0.6;
6
6
  }
7
7
  }
8
+
8
9
  .pf-v5-c-card__title-text {
9
10
  text-align: center;
10
11
  cursor: pointer;
@@ -1,5 +1,6 @@
1
1
  .tasks-labels-row {
2
2
  align-items: center;
3
+
3
4
  ul {
4
5
  margin: 0;
5
6
  }
@@ -1,5 +1,10 @@
1
1
  import { getURIQuery } from 'foremanReact/common/helpers';
2
2
 
3
+ import {
4
+ HOURS_PER_HALF_DAY,
5
+ HOURS_PER_DAY,
6
+ DAYS_PER_WEEK,
7
+ } from 'foremanReact/constants';
3
8
  import {
4
9
  TASKS_DASHBOARD_AVAILABLE_TIMES,
5
10
  TASKS_DASHBOARD_QUERY_KEYS_TEXT,
@@ -16,13 +21,13 @@ export const getQueryValueText = value =>
16
21
  export const timeToHoursNumber = time => {
17
22
  switch (time) {
18
23
  case TASKS_DASHBOARD_AVAILABLE_TIMES.H12:
19
- return 12;
24
+ return HOURS_PER_HALF_DAY;
20
25
  case TASKS_DASHBOARD_AVAILABLE_TIMES.H24:
21
- return 24;
26
+ return HOURS_PER_DAY;
22
27
  case TASKS_DASHBOARD_AVAILABLE_TIMES.WEEK:
23
- return 24 * 7;
28
+ return HOURS_PER_DAY * DAYS_PER_WEEK;
24
29
  default:
25
- return 24;
30
+ return HOURS_PER_DAY;
26
31
  }
27
32
  };
28
33
 
@@ -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 { MS_PER_SECOND } from 'foremanReact/constants';
5
6
  import { convertDashboardQuery } from '../TaskActions/TaskActionHelpers';
6
7
 
7
8
  export const updateURlQuery = (query, history) => {
@@ -51,7 +52,7 @@ export const getDuration = (start, finish) => {
51
52
  const duration = finishDate - startDate;
52
53
  return {
53
54
  text:
54
- duration > 0 && duration < 1000
55
+ duration > 0 && duration < MS_PER_SECOND
55
56
  ? __('Less than a second')
56
57
  : humanizeDuration(duration, dateOptions),
57
58
  };
@@ -5,49 +5,59 @@ import {
5
5
  ExclamationCircleIcon,
6
6
  ExclamationTriangleIcon,
7
7
  QuestionCircleIcon,
8
+ InProgressIcon,
8
9
  } from '@patternfly/react-icons';
9
10
  import { translate as __ } from 'foremanReact/common/I18n';
10
11
 
12
+ const RESULT_ICONS = {
13
+ success: {
14
+ status: 'success',
15
+ title: () => __('Success'),
16
+ IconComponent: CheckCircleIcon,
17
+ },
18
+ error: {
19
+ status: 'danger',
20
+ title: () => __('Error'),
21
+ IconComponent: ExclamationCircleIcon,
22
+ },
23
+ warning: {
24
+ status: 'warning',
25
+ title: () => __('Warning'),
26
+ IconComponent: ExclamationTriangleIcon,
27
+ },
28
+ };
29
+
30
+ const UNKNOWN_RESULT_ICON = {
31
+ status: undefined,
32
+ title: () => __('Unknown'),
33
+ IconComponent: QuestionCircleIcon,
34
+ };
35
+
11
36
  /**
12
37
  * Icon reflecting task state/result (aligned with TaskInfo / tasks table).
13
38
  *
14
39
  * @param {string} state Dynflow task state (e.g. stopped, running).
15
40
  * @param {string} [result] Result when stopped (success, error, warning, …).
41
+ * @param {boolean} isTitleIcon Whether the icon should be used as a title icon.
16
42
  * @returns {React.ReactElement}
17
43
  */
18
- export const taskResultIconEl = (state, result) => {
44
+ export const taskResultIconEl = (state, result, isTitleIcon = false) => {
45
+ const size = isTitleIcon ? 'lg' : 'md';
46
+
19
47
  if (state && state !== 'stopped') {
20
48
  return (
21
- <Icon title={__('Running')}>
22
- <QuestionCircleIcon />
49
+ <Icon title={__('Running')} status="info" isInline size={size}>
50
+ <InProgressIcon />
23
51
  </Icon>
24
52
  );
25
53
  }
26
54
 
27
- switch (result) {
28
- case 'success':
29
- return (
30
- <Icon status="success" title={__('Success')}>
31
- <CheckCircleIcon />
32
- </Icon>
33
- );
34
- case 'error':
35
- return (
36
- <Icon status="danger" title={__('Error')}>
37
- <ExclamationCircleIcon />
38
- </Icon>
39
- );
40
- case 'warning':
41
- return (
42
- <Icon status="warning" title={__('Warning')}>
43
- <ExclamationTriangleIcon />
44
- </Icon>
45
- );
46
- default:
47
- return (
48
- <Icon title={__('Unknown')}>
49
- <QuestionCircleIcon />
50
- </Icon>
51
- );
52
- }
55
+ const { status, title, IconComponent } =
56
+ RESULT_ICONS[result] || UNKNOWN_RESULT_ICON;
57
+
58
+ return (
59
+ <Icon title={title()} status={status} isInline size={size}>
60
+ <IconComponent />
61
+ </Icon>
62
+ );
53
63
  };
@@ -33,7 +33,7 @@ const TitleComponent = ({ action, state, result }) => (
33
33
  {action || __('Task Details')}
34
34
  </Title>
35
35
  </FlexItem>
36
- <FlexItem>{taskResultIconEl(state, result)}</FlexItem>
36
+ <FlexItem>{taskResultIconEl(state, result, true)}</FlexItem>
37
37
  </Flex>
38
38
  );
39
39
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman-tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 13.1.0
4
+ version: 13.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Nečas
@@ -511,7 +511,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
511
511
  - !ruby/object:Gem::Version
512
512
  version: '0'
513
513
  requirements: []
514
- rubygems_version: 4.0.10
514
+ rubygems_version: 4.0.16
515
515
  specification_version: 4
516
516
  summary: Foreman plugin for showing tasks information for resources and users
517
517
  test_files: