foreman_patch 1.1.5 → 1.1.6.alpha4

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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/foreman_patch/concerns/hosts_controller_extensions.rb +35 -41
  3. data/app/controllers/foreman_patch/react_controller.rb +12 -0
  4. data/app/lib/actions/foreman_patch/cycle/create.rb +5 -1
  5. data/app/lib/actions/foreman_patch/cycle/initiate.rb +5 -1
  6. data/app/lib/actions/foreman_patch/invocation/action.rb +23 -29
  7. data/app/lib/actions/foreman_patch/invocation/patch.rb +16 -7
  8. data/app/lib/actions/foreman_patch/invocation/process_logging.rb +44 -0
  9. data/app/lib/actions/foreman_patch/invocation/proxy_action.rb +52 -0
  10. data/app/lib/actions/foreman_patch/invocation/wait_for_host.rb +4 -29
  11. data/app/lib/actions/foreman_patch/round/patch.rb +5 -1
  12. data/app/lib/actions/foreman_patch/window/publish.rb +5 -1
  13. data/app/lib/actions/foreman_patch/window/resolve_hosts.rb +5 -1
  14. data/app/models/foreman_patch/event.rb +13 -0
  15. data/app/models/foreman_patch/invocation.rb +2 -4
  16. data/app/views/foreman_patch/api/v2/invocations/base.json.rabl +1 -1
  17. data/app/views/foreman_patch/api/v2/invocations/event.json.rabl +3 -0
  18. data/app/views/foreman_patch/api/v2/invocations/show.json.rabl +2 -2
  19. data/app/views/foreman_patch/groups/index.html.erb +1 -1
  20. data/app/views/foreman_patch/layouts/react.html.erb +1 -1
  21. data/config/api_routes.rb +26 -28
  22. data/config/routes.rb +40 -29
  23. data/db/migrate/20230706092400_nullify_group_on_delete.rb +11 -0
  24. data/db/migrate/20230707102800_create_invocation_events.rb +16 -0
  25. data/db/seeds.d/100-assign_features_with_templates.rb +6 -12
  26. data/lib/foreman_patch/engine.rb +11 -46
  27. data/lib/foreman_patch/register.rb +119 -0
  28. data/lib/foreman_patch/version.rb +1 -1
  29. data/locale/en/foreman_patch.po +1 -1
  30. data/locale/foreman_patch.pot +1 -1
  31. data/locale/gemspec.rb +1 -1
  32. data/package.json +4 -0
  33. data/public/assets/foreman_patch/calendar-b5391efda77239c4a4894e9f03f34610f6c8e2e748b2a147febfea814b857cdc.scss.gz +0 -0
  34. data/public/assets/foreman_patch/cycle_plans-e5667e178ba389908f5c815b24ec0ea77c340849d56bc39c5ce72bb626bd446a.scss +6 -0
  35. data/public/assets/foreman_patch/cycle_plans-e5667e178ba389908f5c815b24ec0ea77c340849d56bc39c5ce72bb626bd446a.scss.gz +0 -0
  36. data/public/assets/foreman_patch/cycle_plans-ff3d252119622a68828ff70f4a97328303963002237dbf850e92d6a706e93667.scss +6 -0
  37. data/public/assets/foreman_patch/cycle_plans-ff3d252119622a68828ff70f4a97328303963002237dbf850e92d6a706e93667.scss.gz +0 -0
  38. data/public/assets/foreman_patch/foreman_patch-410cf04bf9b09e65fee034cc3f2dd74acf2524abf881c6d6e559d5c62a615faf.css +11 -0
  39. data/public/assets/foreman_patch/foreman_patch-410cf04bf9b09e65fee034cc3f2dd74acf2524abf881c6d6e559d5c62a615faf.css.gz +0 -0
  40. data/public/assets/foreman_patch/foreman_patch-84845e54f06d3a11189828e656432d587c7312358cdf694753da7b78b7dabcee.css +11 -0
  41. data/public/assets/foreman_patch/foreman_patch-84845e54f06d3a11189828e656432d587c7312358cdf694753da7b78b7dabcee.css.gz +0 -0
  42. data/public/assets/foreman_patch/foreman_patch-a76c5fd10a5795e97c5ae4c222bfdf4ab88da49d6a7659175dba79f8fc62ab47.css +82 -0
  43. data/public/assets/foreman_patch/foreman_patch-a76c5fd10a5795e97c5ae4c222bfdf4ab88da49d6a7659175dba79f8fc62ab47.css.gz +0 -0
  44. data/public/assets/foreman_patch/foreman_patch.json +1 -0
  45. data/public/assets/foreman_patch/plan_edit_windows-2eb04c7e83fa62797b0a14364d3ff5b3c4336983603fdc5a276b5464eeba7e60.js +9 -0
  46. data/public/assets/foreman_patch/plan_edit_windows-2eb04c7e83fa62797b0a14364d3ff5b3c4336983603fdc5a276b5464eeba7e60.js.gz +0 -0
  47. data/public/assets/foreman_patch/plan_edit_windows-ddedd3e70fb6ef761f636be2b7b35286e86d68e126bfc37294f9365a5171a928.js +9 -0
  48. data/public/assets/foreman_patch/plan_edit_windows-ddedd3e70fb6ef761f636be2b7b35286e86d68e126bfc37294f9365a5171a928.js.gz +0 -0
  49. data/public/webpack/foreman_patch/bundle.css +1 -0
  50. data/public/webpack/foreman_patch/bundle.js +34173 -0
  51. data/public/webpack/foreman_patch/foreman_patch.css +1 -0
  52. data/public/webpack/foreman_patch/foreman_patch.js +34366 -0
  53. data/public/webpack/foreman_patch/foreman_patch:global.css +1 -0
  54. data/public/webpack/foreman_patch/foreman_patch:global.js +32098 -0
  55. data/public/webpack/foreman_patch/manifest.json +25 -0
  56. data/public/webpack/foreman_patch/vendor.js +5201 -0
  57. data/webpack/components/Invocations/InvocationsPage.js +1 -1
  58. data/webpack/components/Invocations/index.js +6 -6
  59. data/webpack/components/common/Calendar/Calendar.css +76 -0
  60. data/webpack/components/common/Calendar/Calendar.js +5 -4
  61. data/webpack/components/common/Table/index.js +28 -0
  62. data/webpack/global_index.js +16 -0
  63. data/webpack/index.js +3 -8
  64. data/webpack/src/Components/Invocation/Invocation.js +67 -0
  65. data/webpack/src/Components/Invocation/InvocationLogFooter.js +30 -0
  66. data/webpack/src/Components/Invocation/InvocationLogToolbar.js +80 -0
  67. data/webpack/{components → src/Components}/Invocation/InvocationSelectors.js +0 -3
  68. data/webpack/src/Components/Invocation/index.js +62 -0
  69. data/webpack/src/Components/InvocationStatus.js +50 -0
  70. data/webpack/src/Components/Loading.js +51 -0
  71. data/webpack/src/Extends/index.js +15 -0
  72. data/webpack/src/Router/routes.js +4 -2
  73. data/webpack/src/reducers.js +1 -1
  74. metadata +57 -88
  75. data/app/lib/actions/foreman_patch/cycle/complete.rb +0 -41
  76. data/app/lib/actions/foreman_patch/cycle/plan.rb +0 -73
  77. data/app/lib/actions/foreman_patch/round/plan.rb +0 -33
  78. data/app/lib/actions/foreman_patch/window/plan.rb +0 -43
  79. data/app/models/setting/patching.rb +0 -57
  80. data/app/views/foreman_patch/api/v2/invocations/phase.json.rabl +0 -7
  81. data/config/routes/mount_engine.rb +0 -3
  82. data/config/routes/overrides.rb +0 -10
  83. data/lib/foreman_patch/plugin.rb +0 -47
  84. data/webpack/components/Invocation/Invocation.js +0 -47
  85. data/webpack/components/Invocation/index.js +0 -36
  86. data/webpack/components/common/Terminal/OutputLine.js +0 -26
  87. data/webpack/components/common/Terminal/Terminal.js +0 -115
  88. data/webpack/components/common/Terminal/Terminal.scss +0 -47
  89. data/webpack/src/ForemanPatch.js +0 -11
  90. data/webpack/src/Router/index.js +0 -14
  91. data/webpack/src/index.js +0 -1
  92. /data/{webpack/components/common/Calendar/Calendar.scss → public/assets/foreman_patch/calendar-b5391efda77239c4a4894e9f03f34610f6c8e2e748b2a147febfea814b857cdc.scss} +0 -0
  93. /data/webpack/components/Invocations/{Invocations.scss → Invocations.css} +0 -0
  94. /data/webpack/{components → src/Components}/Invocation/InvocationActions.js +0 -0
  95. /data/webpack/{components → src/Components}/Invocation/InvocationConsts.js +0 -0
@@ -4,7 +4,7 @@ import { Grid } from 'patternfly-react';
4
4
 
5
5
  import SearchBar from 'foremanReact/components/SearchBar';
6
6
  import Pagination from 'foremanReact/components/Pagination/PaginationWrapper';
7
- import { ActionButtons } form 'foremanReact/components/common/ActionButtons/ActionButtons';
7
+ import { ActionButtons } from 'foremanReact/components/common/ActionButtons/ActionButtons';
8
8
  import { getControllerSearchProps } from 'foremanReact/constants';
9
9
 
10
10
  import Invocations from './Invocations';
@@ -3,7 +3,7 @@ import { useSelector, useDispatch } from 'react-redux';
3
3
  import PropTypes from 'prop-types';
4
4
  import { Grid } from 'patternfly-react';
5
5
 
6
- import {
6
+ import {
7
7
  withInterval,
8
8
  stopInterval
9
9
  } from 'foremanReact/redux/middlewares/IntervalMiddleware';
@@ -26,7 +26,7 @@ import {
26
26
  import { getUrl } from './InvocationsHelpers';
27
27
  import { INVOCATIONS } from './InvocationsConstants';
28
28
 
29
- import './Invocations.scss';
29
+ import './Invocations.css';
30
30
 
31
31
  const WrappedInvocations = ({ round }) => {
32
32
  const dispatch = useDispatch();
@@ -67,7 +67,7 @@ const WrappedInvocations = ({ round }) => {
67
67
 
68
68
  const areAllSelected = selectedItems.length == items.length;
69
69
 
70
- const setItemSelected = (item, isSelecting) =>
70
+ const setItemSelected = (item, isSelecting) =>
71
71
  setSelectedItems(prevSelected => {
72
72
  const otherSelectedItems = prevSelected.filter(i => i !== item.id);
73
73
  return isSelecting ? [...otherSelectedItems, item.id] : otherSelectedItems;
@@ -78,8 +78,8 @@ const WrappedInvocations = ({ round }) => {
78
78
  const numberSelected = rowIndex - recentSelectedRowIndex;
79
79
  const intermediateIndexes =
80
80
  numberSelected > 0
81
- ? Array.from(new Array(numberSelected + 1), (_x, i) => i + recentSelectedRowIndex)
82
- : Array.from(new Array(Math.abs(numberSelected) + 1), (_x, i) => i + rowIndex);
81
+ ? Array.from(new Array(numberSelected + 1), (_x, i) => i + recentSelectedRowIndex)
82
+ : Array.from(new Array(Math.abs(numberSelected) + 1), (_x, i) => i + rowIndex);
83
83
  intermediateIndexes.forEach(index, setItemSelected(items[index], isSelecting));
84
84
  } else {
85
85
  setItemSelected(item, isSelecting);
@@ -95,7 +95,7 @@ const WrappedInvocations = ({ round }) => {
95
95
  }
96
96
  };
97
97
 
98
- const getData = url => withInterval(get({
98
+ const getData = url => withInterval(get({
99
99
  key: INVOCATIONS,
100
100
  url,
101
101
  handleError: () => {
@@ -0,0 +1,76 @@
1
+ .calendar {
2
+
3
+ .day {
4
+ padding: 2px;
5
+ height: 120px;
6
+ }
7
+
8
+ .wday-0 {}
9
+ .wday-1 {}
10
+ .wday-2 {}
11
+ .wday-3 {}
12
+ .wday-4 {}
13
+ .wday-5 {}
14
+ .wday-6 {}
15
+
16
+ .today {
17
+ background: #ffffc0;
18
+ }
19
+
20
+ .past {
21
+ background: #f9f9f9;
22
+ }
23
+
24
+ .future {}
25
+
26
+ .start-date {}
27
+
28
+ .disabled {
29
+ background: #e8e8e8;
30
+ }
31
+ .enabled {}
32
+ td.enabled:hover {
33
+ background: #def3ff;
34
+ }
35
+
36
+ h6 {
37
+ margin: 2px 3px;
38
+ }
39
+
40
+ div.day {
41
+ margin: 0;
42
+ height: inherit;
43
+ width: inherit;
44
+ display: block;
45
+ }
46
+
47
+ div.event {
48
+ margin: 2px, 10px;
49
+ border: 1px solid;
50
+ border-color: #d1d1d1;
51
+ padding: 2px 3px;
52
+ background: #ffffff;
53
+ }
54
+
55
+ .calendar-header {
56
+ button {
57
+ font-size: 20px;
58
+ width: 100%;
59
+ }
60
+
61
+ .calendar-title {
62
+ font-size: 24px;
63
+ text-align: center;
64
+ }
65
+
66
+ .previous {
67
+ padding-left: 0px;
68
+ padding-right: 20px;
69
+ }
70
+
71
+ .next {
72
+ padding-left: 20px;
73
+ padding-right: 0px;
74
+ }
75
+ }
76
+ }
@@ -2,12 +2,13 @@ import React, { useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { DndProvider } from 'react-dnd';
4
4
  import HTML5Backend from 'react-dnd-html5-backend';
5
- import './Calendar.scss';
5
+
6
+ import './Calendar.css';
6
7
 
7
8
  import { views, getView } from './View';
8
9
 
9
10
  const Calendar = (props) => {
10
- const {start, end, weekStartsOn, locale, onEventMoved} = props;
11
+ const { start, end, weekStartsOn, locale, onEventMoved } = props;
11
12
 
12
13
  const initialDate = () => {
13
14
  const { date } = props;
@@ -27,7 +28,7 @@ const Calendar = (props) => {
27
28
 
28
29
  const result = events.map(event => {
29
30
  if (event.id === updatedEvent.id) {
30
- return {...event, ...updatedEvent};
31
+ return { ...event, ...updatedEvent };
31
32
  }
32
33
  return event;
33
34
  });
@@ -78,7 +79,7 @@ Calendar.defaultProps = {
78
79
  date: new Date(),
79
80
  view: views.MONTH,
80
81
  events: [],
81
- onEventMoved: (event) => {},
82
+ onEventMoved: (event) => { },
82
83
  locale: 'en-US',
83
84
  weekStartsOn: 1,
84
85
  };
@@ -0,0 +1,28 @@
1
+ import React, { useCallback, useRef } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useDispatch } from 'react-redux';
4
+ import { PaginationVariant, Flex, FlexItem } from '@patternfly/react-core';
5
+
6
+ import { STATUS } from 'foremanReact/constants';
7
+ import { useForemanSettings } from 'foremanReact/Root/Context/ForemanContext';
8
+ import { translate as __ } from 'foremanReact/common/I18n';
9
+
10
+ import { SelectAllCheckbox }
11
+
12
+ const TableWrapper = ({
13
+
14
+ }) => {
15
+ const dispatch = useDispatch();
16
+
17
+ return (
18
+ <>
19
+ <Flex style={{ alignItems: 'center' }} className="margin-16-24">
20
+ { displaySelectAllCheckbox && !hideToolbar &&
21
+ <FlexItem alignSelf={{ default: 'alignSelfCenter' }}>
22
+ <SelectAllCheckbox
23
+ </FlexItem>
24
+ }
25
+ </Flex>
26
+ </>
27
+ );
28
+ }
@@ -0,0 +1,16 @@
1
+ import { registerReducer } from 'foremanReact/common/MountingService';
2
+ import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
3
+ import { registerRoutes } from 'foremanReact/routes/RoutingService';
4
+ import Routes from './src/Router/routes';
5
+ import reducers from './src/reducers';
6
+
7
+ // register reducers
8
+ Object.entries(reducers).forEach(([key, reducer]) =>
9
+ registerReducer(key, reducer)
10
+ );
11
+
12
+ // register client routes
13
+ registerRoutes('ForemanPatch', Routes);
14
+
15
+ // register fills for extending foreman core
16
+ //addGlobalFill('host-overview-cards', 'Patching Details', );
data/webpack/index.js CHANGED
@@ -1,13 +1,12 @@
1
1
  /* eslint import/no-unresolved: [2, { ignore: [foremanReact/*] }] */
2
2
  /* eslint-disable import/no-extraneous-dependencies */
3
3
  /* eslint-disable import/extensions */
4
+ /* eslint-disable import/no-unresolved */
4
5
  import componentRegistry from 'foremanReact/components/componentRegistry';
5
- import { registerReducer } from 'foremanReact/common/MountingService';
6
- import reducers from './src/reducers';
7
- import ForemanPatch from './src/ForemanPatch';
8
6
 
9
7
  import Rounds from './components/Rounds';
10
8
  import Plan from './components/Plan';
9
+ import Invocation from './src/Components/Invocation';
11
10
  import Invocations from './components/Invocations';
12
11
  import RoundProgress from './components/RoundProgress';
13
12
  import Cycle from './components/Cycle';
@@ -18,11 +17,7 @@ const components = [
18
17
  { name: 'Invocations', type: Invocations },
19
18
  { name: 'Cycle', type: Cycle },
20
19
  { name: 'RoundProgress', type: RoundProgress },
20
+ { name: 'Invocation', type: Invocation },
21
21
  ];
22
22
 
23
- Object.entries(reducers).forEach(([key, reducer]) =>
24
- registerReducer(key, reducer)
25
- );
26
-
27
23
  components.forEach(component => componentRegistry.register(component));
28
- componentRegistry.register({ name: 'ForemanPatch', type: ForemanPatch });
@@ -0,0 +1,67 @@
1
+ import React, { useRef, useState, useMemo } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { translate as __ } from 'foremanReact/common/I18n';
4
+
5
+ const Invocation = ({ events, status }) => {
6
+ const viewerRef = useRef();
7
+ const [isStderrVisible, setStderrVisible] = useState(true);
8
+ const [isStdoutVisible, setStdoutVisible] = useState(true);
9
+ const [isDebugVisible, setDebugVisible] = useState(false);
10
+ const [followStatus, setFollowStatus] = useState(status === 'running' ? 'active' : 'disabled');
11
+
12
+ const lines = useMemo(() => {
13
+ return events.flatMap(event => (
14
+ event.event.replace(/\r\n/, "\n").replace(/\n$/, '').split("\n").map(line => (
15
+ { event_type: event.event_type, event: line, timestamp: event.timestamp }
16
+ )).filter((event) => (
17
+ (event.event_type === 'stdout' && isStdoutVisible) ||
18
+ (event.event_type === 'stderr' && isStderrVisible) ||
19
+ (event.event_type === 'debug' && isDebugVisible)
20
+ ))
21
+ ));
22
+ }, [events, isStderrVisible, isStdoutVisible, isDebugVisible]);
23
+
24
+ const onScroll = ({ scrollDirection, scrollOffsetToBottom, scrollUpdateWasRequested }) => {
25
+ if (!scrollUpdateWasRequested) {
26
+ if (scrollOffsetToBottom < 1) {
27
+ setFollowStatus('active');
28
+ } else if (scrollDirection === 'backward') {
29
+ setFollowStatus('paused');
30
+ }
31
+ }
32
+ };
33
+
34
+ return (
35
+ <LogViewer
36
+ ref={viewerRef}
37
+ theme='dark'
38
+ data={lines}
39
+ toolbar={<InvocationLogToolbar
40
+ isDebugVisible={isDebugVisible}
41
+ setDebugVisible={setDebugVisible}
42
+ isStderrVisible={isStderrVisible}
43
+ setStderrVisible={setStderrVisible}
44
+ isStdoutVisible={isStderrVisible}
45
+ setStdoutVisible={setStdoutVisible}
46
+ />}
47
+ footer={<InvocationLogFooter followStatus={followStatus} setFollowStatus={setFollowStatus} />}
48
+ onScroll={onScroll}
49
+ />
50
+ );
51
+ };
52
+
53
+ Invocation.propTypes = {
54
+ events: PropTypes.arrayOf(PropTypes.shape({
55
+ event_type: PropTypes.string.isRequired,
56
+ event: PropTypes.string.isRequired,
57
+ timestamp: PropTypes.string.isRequired,
58
+ })),
59
+ status: PropTypes.string,
60
+ };
61
+
62
+ Invocation.defaultProps = {
63
+ events: [],
64
+ status: 'pending',
65
+ };
66
+
67
+ export default Invocation;
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { translate as __ } from 'foremanReact/common/I18n';
4
+ import { OutlinedPlayCircleIcon } from '@patternfly/react-icons';
5
+ import { Button } from '@patternfly/react-core';
6
+ import classNames from 'classnames';
7
+
8
+ const InvocationFooter = ({ followStatus, setFollowStatus }) => {
9
+ const resumeFollowing = () => setFollowStatus('active');
10
+
11
+ return (
12
+ <Button className={classNames('invocation__footer', {
13
+ 'invocation__footer--hidden': followStatus !== 'paused',
14
+ })} onClick={resumeFollowing} isBlock >
15
+ <OutlinedPlayCircleIcon />
16
+ &nbsp{__('Resume following')}
17
+ </Button>
18
+ );
19
+ };
20
+
21
+ InvocationFooter.propTypes = {
22
+ followStatus: PropTypes.string,
23
+ setFollowStatus: PropTypes.func.isRequired,
24
+ };
25
+
26
+ InvocationFooter.defaultProps = {
27
+ followStatus: 'disabled',
28
+ };
29
+
30
+ export default InvocationFooter;
@@ -0,0 +1,80 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import {
4
+ Toolbar,
5
+ ToolbarContent,
6
+ ToolbarGroup,
7
+ ToolbarItem,
8
+ ToggleGroup,
9
+ ToggleGroupItem
10
+ } from '@patternfly/react-core';
11
+ import { LogViewerSearch } from '@patternfly/react-log-viewer';
12
+ import { translate as __ } from 'foremanReact/common/I18n';
13
+
14
+ const InvocationLogToolbar = ({
15
+ status,
16
+ isDebugVisible,
17
+ setDebugVisible,
18
+ isStderrVisible,
19
+ setStderrVisible,
20
+ isStdoutVisible,
21
+ setStdoutVisible,
22
+ }) => {
23
+ return (
24
+ <Toolbar>
25
+ <ToolbarContent>
26
+ <ToolbarGroup alignment={{ default: 'alignLeft' }}>
27
+ <ToolbarItem>
28
+ <InvocationStatus status={status} />
29
+ </ToolbarItem>
30
+ </ToolbarGroup>
31
+ <ToolbarGroup alignement={{ default: 'alignRight' }}>
32
+ <ToolbarItem>
33
+ <LogViewerSearch placeholder={__('Search')} />
34
+ </ToolbarItem>
35
+ <ToolbarItem>
36
+ <ToggleGroup>
37
+ <ToggleGroupItem
38
+ text={__('Toggle STDERR')}
39
+ isSelected={isStderrVisible}
40
+ onChange={setStderrVisible}
41
+ />
42
+ <ToggleGroupItem
43
+ text={__('Toggle STDOUT')}
44
+ isSelected={isStdoutVisible}
45
+ onChange={setStdoutVisible}
46
+ />
47
+ <ToggleGroupItem
48
+ text={__('Toggle DEBUG')}
49
+ isSelected={isDebugVisible}
50
+ onChange={setDebugVisible}
51
+ />
52
+ </ToggleGroup>
53
+ </ToolbarItem>
54
+ </ToolbarGroup>
55
+ </ToolbarContent>
56
+ </Toolbar>
57
+ );
58
+ };
59
+
60
+ InvocationLogToolbar.propTypes = {
61
+ status: PropTypes.string,
62
+ isDebugVisible: PropTypes.bool,
63
+ setDebugVisible: PropTypes.func,
64
+ isStderrVisible: PropTypes.bool,
65
+ setStderrVisible: PropTypes.func,
66
+ isStdoutVisible: PropTypes.bool,
67
+ setStdoutVisible: PropTypes.func,
68
+ };
69
+
70
+ InvocationLogToolbar.defaultProps = {
71
+ status: 'pending',
72
+ isDebugVisible: false,
73
+ setDebugVisible: Function.prototype,
74
+ isStderrVisible: true,
75
+ setStderrVisible: Function.prototype,
76
+ isStdoutVisible: true,
77
+ setStdoutVisible: Function.prototype,
78
+ };
79
+
80
+ export default InvocationLogToolbar;
@@ -7,8 +7,5 @@ import { INVOCATION } from './InvocationConsts';
7
7
  export const selectInvocation = state =>
8
8
  selectAPIResponse(state, INVOCATION) || {};
9
9
 
10
- export const selectState = state =>
11
- selectAPIResponse(state, INVOCATION).state;
12
-
13
10
  export const selectStatus = state =>
14
11
  selectAPIStatus(state, INVOCATION);
@@ -0,0 +1,62 @@
1
+ import React, { useEffect } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useSelector, useDispatch } from 'react-redux';
4
+ import { Alert } from '@patternfly/react-core';
5
+ import { stopInterval } from 'foremanReact/redux/middlewares/IntervalMiddleware';
6
+ import Invocation from './Invocation';
7
+ import {
8
+ selectInvocation,
9
+ selectStatus,
10
+ } from './InvocationSelectors';
11
+ import { getData } from './InvocationActions';
12
+ import { INVOCATION } from './InvocationConsts';
13
+ import Loading from '../Loading';
14
+
15
+ const WrappedInvocation = ({ id }) => {
16
+ const dispatch = useDispatch();
17
+ const invocation = useSelector(selectInvocation);
18
+ const loadingStatus = useSelector(selectStatus);
19
+
20
+ const isCompleted = () => {
21
+ const status = invocation.status;
22
+ return (status === 'error' || status === 'warning' || status === 'success' || status === 'cancelled');
23
+ };
24
+
25
+ useEffect(() => {
26
+ dispatch(getData(id));
27
+
28
+ return () => {
29
+ dispatch(stopInterval(INVOCATION));
30
+ };
31
+ }, [dispatch]);
32
+
33
+ useEffect(() => {
34
+ if (isCompleted()) {
35
+ dispatch(stopInterval(INVOCATION));
36
+ }
37
+ }, [state, dispatch]);
38
+
39
+ if (loadingStatus === STATUS.PENDING) {
40
+ return <Loading />;
41
+ }
42
+
43
+ if (loadingStatus === STATUS.ERROR) {
44
+ return (
45
+ <Alert type="error">
46
+ {__(
47
+ 'There was an error while updating the status, try refreshing the page.'
48
+ )}
49
+ </Alert>
50
+ );
51
+ }
52
+
53
+ return (
54
+ <Invocation {...invocation} />
55
+ );
56
+ };
57
+
58
+ WrappedInvocation.propTypes = {
59
+ id: PropTypes.number.isRequired
60
+ };
61
+
62
+ export default WrappedInvocation;
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import {
4
+ ExclamationCircleIcon,
5
+ ExclamationTriangleIcon,
6
+ CheckCircleIcon,
7
+ BanIcon,
8
+ InProgressIcon,
9
+ PendingIcon
10
+ } from '@patternfly/react-icons';
11
+ import { Label } from '@patternfly/react-core';
12
+
13
+ const InvocationStatus = ({ status }) => {
14
+ switch (status) {
15
+ case 'error':
16
+ return (
17
+ <Label icon={<ExclamationCircleIcon />}>{__('failed')}</Label>
18
+ );
19
+ case 'warning':
20
+ return (
21
+ <Label icon={<ExclamationTriangleIcon />}>{__('warning')}</Label>
22
+ );
23
+ case 'success':
24
+ return (
25
+ <Label icon={<CheckCircleIcon />}>{__('success')}</Label>
26
+ );
27
+ case 'cancelled':
28
+ return (
29
+ <Label icon={<BanIcon />}>{__('success')}</Label>
30
+ );
31
+ case 'running':
32
+ return (
33
+ <Label icon={<InProgressIcon />}>{__('running')}</Label>
34
+ );
35
+ default:
36
+ return (
37
+ <Label icon={<PendingIcon />}>{__('pending')}</Label>
38
+ );
39
+ }
40
+ };
41
+
42
+ InvocationStatus.propTypes = {
43
+ status: PropTypes.string,
44
+ };
45
+
46
+ InvocationStatus.defaultProps = {
47
+ status: 'pending',
48
+ };
49
+
50
+ export default InvocationStatus;
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import {
4
+ Bullseye,
5
+ Title,
6
+ EmptyState,
7
+ EmptyStateIcon,
8
+ Spinner,
9
+ Skeleton,
10
+ } from '@patternfly/react-core';
11
+ import { translate as __ } from 'foremanReact/common/I18n';
12
+
13
+ const Loading = ({
14
+ size,
15
+ showText,
16
+ loadingText,
17
+ skeleton,
18
+ }) => {
19
+ if (skeleton) {
20
+ return <Skeleton height="100%" />;
21
+ }
22
+
23
+ return (
24
+ <Bullseye>
25
+ <EmptyState>
26
+ <EmptyStateIcon size={size} variant="container" component={Spinner} />
27
+ {showText && (
28
+ <Title size={size} headingLevel="h4" ouiaId="loading-title">
29
+ {loadingText || __('Loading')}
30
+ </Title>
31
+ )}
32
+ </EmptyState>
33
+ </Bullseye>
34
+ );
35
+ };
36
+
37
+ Loading.propTypes = {
38
+ size: PropTypes.string,
39
+ showText: PropTypes.bool,
40
+ loadingText: PropTypes.string,
41
+ skeleton: PropTypes.bool,
42
+ };
43
+
44
+ Loading.defaultProps = {
45
+ size: 'lg',
46
+ showText: true,
47
+ loadingText: null,
48
+ skeleton: false,
49
+ };
50
+
51
+ export default Loading;
@@ -0,0 +1,15 @@
1
+ // This is an example of extending foreman-core's component via slot&fill
2
+ // http://foreman.surge.sh/?path=/docs/introduction-slot-and-fill--page
3
+ /*
4
+ import React from 'react';
5
+ import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
6
+
7
+ addGlobalFill('slotId', 'fillId', <SomeComponent key="some-key" />, 300);
8
+
9
+ addGlobalFill(
10
+ 'slotId',
11
+ 'fillId',
12
+ { someProp: 'this is an override prop' },
13
+ 300
14
+ );
15
+ */
@@ -1,3 +1,5 @@
1
- const routes = {};
1
+ import React from 'react';
2
2
 
3
- export default routes;
3
+ const routes = [];
4
+
5
+ export default routes;
@@ -4,4 +4,4 @@ const reducers = {
4
4
  foremanPatch: combineReducers({}),
5
5
  };
6
6
 
7
- export default reducers;
7
+ export default reducers;