foreman_remote_execution 18.0.1 → 18.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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_remote_execution/locale/fr/foreman_remote_execution.js +28 -28
  3. data/app/assets/javascripts/foreman_remote_execution/locale/ja/foreman_remote_execution.js +48 -48
  4. data/app/assets/javascripts/foreman_remote_execution/locale/ko/foreman_remote_execution.js +29 -29
  5. data/app/assets/javascripts/foreman_remote_execution/locale/zh_CN/foreman_remote_execution.js +22 -22
  6. data/app/assets/javascripts/foreman_remote_execution/template_invocation.js +0 -170
  7. data/app/assets/stylesheets/foreman_remote_execution/job_invocations.scss +0 -6
  8. data/app/controllers/job_invocations_controller.rb +1 -98
  9. data/app/helpers/job_invocations_helper.rb +0 -49
  10. data/app/helpers/remote_execution_helper.rb +23 -123
  11. data/config/routes.rb +0 -6
  12. data/lib/foreman_remote_execution/plugin.rb +2 -2
  13. data/lib/foreman_remote_execution/version.rb +1 -1
  14. data/locale/foreman_remote_execution.pot +2 -2
  15. data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  16. data/locale/fr/foreman_remote_execution.po +28 -28
  17. data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  18. data/locale/ja/foreman_remote_execution.po +48 -48
  19. data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  20. data/locale/ko/foreman_remote_execution.po +29 -29
  21. data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  22. data/locale/zh_CN/foreman_remote_execution.po +22 -22
  23. data/package.json +1 -14
  24. data/test/functional/job_invocations_controller_test.rb +0 -77
  25. data/webpack/JobInvocationDetail/JobInvocationConstants.js +5 -0
  26. data/webpack/JobInvocationDetail/JobInvocationToolbarButtons.js +0 -10
  27. data/webpack/JobInvocationDetail/TemplateInvocation.js +12 -69
  28. data/webpack/JobInvocationDetail/TemplateInvocationComponents/OutputCodeBlock.js +94 -72
  29. data/webpack/JobInvocationDetail/TemplateInvocationHelpers.js +33 -0
  30. data/webpack/JobInvocationDetail/__tests__/JobInvocationDetailEmptyState.test.js +1 -1
  31. data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +0 -6
  32. data/webpack/JobInvocationDetail/__tests__/OutputCodeBlock.test.js +1 -1
  33. data/webpack/JobInvocationDetail/__tests__/TableToolbarActions.test.js +1 -1
  34. data/webpack/JobInvocationDetail/__tests__/TemplateInvocation.test.js +5 -1
  35. data/webpack/JobInvocationDetail/__tests__/TemplateInvocationHelpers.test.js +94 -0
  36. data/webpack/JobInvocationDetail/__tests__/TemplateInvocationPolling.test.js +338 -139
  37. data/webpack/JobInvocationDetail/useTemplateInvocationOutputPolling.js +185 -0
  38. data/webpack/JobWizard/JobWizardPageRerun.js +0 -10
  39. data/webpack/JobWizard/__tests__/autofill.test.js +23 -0
  40. data/webpack/JobWizard/__tests__/integration.test.js +1 -1
  41. data/webpack/JobWizard/autofill.js +18 -4
  42. data/webpack/JobWizard/index.js +1 -18
  43. data/webpack/JobWizard/steps/HostsAndInputs/__tests__/HostsAndInputs.test.js +49 -1
  44. data/webpack/global_index.js +0 -2
  45. data/webpack/index.js +0 -15
  46. data/webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js +1 -1
  47. metadata +6 -53
  48. data/app/assets/stylesheets/foreman_remote_execution/modal_window.scss +0 -4
  49. data/app/helpers/job_invocations_chart_helper.rb +0 -80
  50. data/app/views/job_invocations/_card_results.html.erb +0 -12
  51. data/app/views/job_invocations/_card_schedule.html.erb +0 -33
  52. data/app/views/job_invocations/_card_target_hosts.html.erb +0 -57
  53. data/app/views/job_invocations/_card_user_input.html.erb +0 -16
  54. data/app/views/job_invocations/_description_fields.html.erb +0 -4
  55. data/app/views/job_invocations/_form.html.erb +0 -144
  56. data/app/views/job_invocations/_preview_hosts_modal.html.erb +0 -15
  57. data/app/views/job_invocations/_rerun_taxonomies.html.erb +0 -22
  58. data/app/views/job_invocations/_tab_hosts.html.erb +0 -11
  59. data/app/views/job_invocations/_tab_overview.html.erb +0 -41
  60. data/app/views/job_invocations/_tab_preview_templates.html.erb +0 -20
  61. data/app/views/job_invocations/_user_input.html.erb +0 -21
  62. data/app/views/job_invocations/new.html.erb +0 -9
  63. data/app/views/job_invocations/refresh.js.erb +0 -4
  64. data/app/views/job_invocations/show.html.erb +0 -59
  65. data/app/views/job_invocations/show.js.erb +0 -12
  66. data/app/views/job_invocations/show.json.erb +0 -5
  67. data/test/support/remote_execution_helper.rb +0 -5
  68. data/webpack/react_app/components/TargetingHosts/TargetingHosts.js +0 -62
  69. data/webpack/react_app/components/TargetingHosts/TargetingHostsConsts.js +0 -2
  70. data/webpack/react_app/components/TargetingHosts/TargetingHostsHelpers.js +0 -10
  71. data/webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.js +0 -45
  72. data/webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.scss +0 -30
  73. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +0 -78
  74. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss +0 -3
  75. data/webpack/react_app/components/TargetingHosts/TargetingHostsSelectors.js +0 -25
  76. data/webpack/react_app/components/TargetingHosts/__tests__/HostItem.test.js +0 -76
  77. data/webpack/react_app/components/TargetingHosts/__tests__/HostStatus.test.js +0 -6
  78. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHosts.test.js +0 -6
  79. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsPage.test.js +0 -9
  80. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +0 -31
  81. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap +0 -12
  82. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHosts.test.js.snap +0 -96
  83. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +0 -75
  84. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +0 -16
  85. data/webpack/react_app/components/TargetingHosts/__tests__/fixtures.js +0 -56
  86. data/webpack/react_app/components/TargetingHosts/components/HostItem.js +0 -39
  87. data/webpack/react_app/components/TargetingHosts/components/HostStatus.js +0 -54
  88. data/webpack/react_app/components/TargetingHosts/index.js +0 -150
  89. data/webpack/react_app/components/jobInvocations/AggregateStatus/index.js +0 -60
  90. data/webpack/react_app/components/jobInvocations/AggregateStatus/index.test.js +0 -76
  91. data/webpack/react_app/components/jobInvocations/index.js +0 -94
  92. data/webpack/react_app/extend/reducers.js +0 -5
  93. data/webpack/react_app/redux/actions/jobInvocations/index.js +0 -87
  94. data/webpack/react_app/redux/consts.js +0 -5
  95. data/webpack/react_app/redux/reducers/index.js +0 -6
  96. data/webpack/react_app/redux/reducers/jobInvocations/index.fixtures.js +0 -46
  97. data/webpack/react_app/redux/reducers/jobInvocations/index.js +0 -34
  98. data/webpack/react_app/redux/reducers/jobInvocations/index.test.js +0 -43
@@ -0,0 +1,185 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { useDispatch } from 'react-redux';
3
+ // eslint-disable-next-line import/no-extraneous-dependencies
4
+ import API from 'foremanReact/API';
5
+ import { translate as __ } from 'foremanReact/common/I18n';
6
+ import { addToast } from 'foremanReact/components/ToastsList';
7
+ import { APIActions } from 'foremanReact/redux/API';
8
+ import {
9
+ GET_TEMPLATE_INVOCATION,
10
+ OUTPUT_MAX_REFRESH_INTERVAL_MS,
11
+ OUTPUT_REFRESH_INTERVAL_MS,
12
+ showTemplateInvocationUrl,
13
+ templateInvocationOutputUrl,
14
+ } from './JobInvocationConstants';
15
+ import {
16
+ getLastOutputTimestamp,
17
+ mergeOutput,
18
+ } from './TemplateInvocationHelpers';
19
+
20
+ export const useTemplateInvocationOutputPolling = ({
21
+ hostID,
22
+ jobID,
23
+ isExpanded,
24
+ response,
25
+ }) => {
26
+ const dispatch = useDispatch();
27
+ const responseOutput = response?.output;
28
+ const responseFinished = response?.finished || false;
29
+ const hostFinishedRef = useRef(response?.finished || false);
30
+ const [liveOutput, setLiveOutput] = useState(responseOutput || []);
31
+ const lastTimestampRef = useRef(getLastOutputTimestamp(responseOutput || []));
32
+ const syncResponseRef = useRef(true);
33
+ const hasCachedOutput = Array.isArray(responseOutput);
34
+
35
+ const replaceOutput = useCallback(output => {
36
+ const nextOutput = Array.isArray(output) ? output : [];
37
+ lastTimestampRef.current = getLastOutputTimestamp(nextOutput);
38
+ setLiveOutput(nextOutput);
39
+ }, []);
40
+
41
+ const appendOutput = useCallback(output => {
42
+ const nextOutput = Array.isArray(output) ? output : [];
43
+ const lastTimestamp = getLastOutputTimestamp(nextOutput);
44
+ if (lastTimestamp !== null && lastTimestamp !== undefined) {
45
+ lastTimestampRef.current = lastTimestamp;
46
+ }
47
+ setLiveOutput(currentOutput => mergeOutput(currentOutput, nextOutput));
48
+ }, []);
49
+
50
+ useEffect(() => {
51
+ if (syncResponseRef.current && Array.isArray(responseOutput)) {
52
+ replaceOutput(responseOutput);
53
+ syncResponseRef.current = false;
54
+ }
55
+ if (responseFinished) hostFinishedRef.current = true;
56
+ }, [replaceOutput, responseFinished, responseOutput]);
57
+
58
+ useEffect(() => {
59
+ let cancelled = false;
60
+ let timeoutId = null;
61
+ let idlePolls = 0;
62
+ let outputErrorShown = false;
63
+ const detailsURL = showTemplateInvocationUrl(hostID, jobID);
64
+ const outputURL = templateInvocationOutputUrl(hostID, jobID);
65
+
66
+ const scheduleNextPoll = newOutput => {
67
+ idlePolls = newOutput.length ? 0 : idlePolls + 1;
68
+ const interval = Math.min(
69
+ OUTPUT_REFRESH_INTERVAL_MS * Math.max(idlePolls, 1),
70
+ OUTPUT_MAX_REFRESH_INTERVAL_MS
71
+ );
72
+ timeoutId = setTimeout(scheduleOutputPoll, interval);
73
+ };
74
+
75
+ async function scheduleOutputPoll() {
76
+ if (cancelled) return;
77
+ const isDocumentVisible =
78
+ document.visibilityState === 'visible' ||
79
+ document.visibilityState === 'prerender';
80
+ if (!isDocumentVisible) {
81
+ timeoutId = setTimeout(scheduleOutputPoll, OUTPUT_REFRESH_INTERVAL_MS);
82
+ return;
83
+ }
84
+ try {
85
+ const { data } = await API.get(
86
+ outputURL,
87
+ {},
88
+ lastTimestampRef.current === null
89
+ ? {}
90
+ : { since: lastTimestampRef.current }
91
+ );
92
+ if (cancelled) return;
93
+
94
+ outputErrorShown = false;
95
+ const newOutput = Array.isArray(data?.output) ? data.output : [];
96
+ appendOutput(newOutput);
97
+ if (data?.refresh) {
98
+ scheduleNextPoll(newOutput);
99
+ } else {
100
+ hostFinishedRef.current = true;
101
+ timeoutId = null;
102
+ fetchDetails(false);
103
+ }
104
+ } catch (error) {
105
+ if (cancelled) return;
106
+ if (!outputErrorShown) {
107
+ dispatch(
108
+ addToast({
109
+ key: `template-invocation-output-error-${hostID}`,
110
+ type: 'danger',
111
+ message:
112
+ // eslint-disable-next-line camelcase
113
+ error?.response?.data?.error?.full_messages?.[0] ||
114
+ error?.response?.data?.error?.message ||
115
+ __('Failed to load live job output'),
116
+ })
117
+ );
118
+ outputErrorShown = true;
119
+ }
120
+ scheduleNextPoll([]);
121
+ }
122
+ }
123
+
124
+ function fetchDetails(pollOutput = true) {
125
+ if (cancelled) return;
126
+ dispatch(
127
+ APIActions.get({
128
+ url: detailsURL,
129
+ key: `${GET_TEMPLATE_INVOCATION}_${hostID}`,
130
+ handleSuccess: ({ data }) => {
131
+ if (cancelled) return;
132
+ replaceOutput(data?.output || []);
133
+ const isFinished = data?.finished ?? true;
134
+ // eslint-disable-next-line camelcase
135
+ const autoRefresh = data?.auto_refresh || false;
136
+ if (pollOutput && !isFinished && autoRefresh) {
137
+ hostFinishedRef.current = false;
138
+ idlePolls = 0;
139
+ timeoutId = setTimeout(
140
+ scheduleOutputPoll,
141
+ OUTPUT_REFRESH_INTERVAL_MS
142
+ );
143
+ } else {
144
+ timeoutId = null;
145
+ }
146
+ },
147
+ handleError: () => {
148
+ if (cancelled) return;
149
+ timeoutId = null;
150
+ },
151
+ errorToast: ({ response: errorResponse }) =>
152
+ // eslint-disable-next-line camelcase
153
+ errorResponse?.data?.error?.full_messages?.[0] ||
154
+ errorResponse?.data?.error?.message ||
155
+ __('Failed to load template invocation details'),
156
+ })
157
+ );
158
+ }
159
+
160
+ if (isExpanded) {
161
+ if (hostFinishedRef.current || responseFinished) return undefined;
162
+ if (hasCachedOutput) {
163
+ scheduleOutputPoll();
164
+ } else {
165
+ fetchDetails();
166
+ }
167
+ }
168
+
169
+ return () => {
170
+ cancelled = true;
171
+ clearTimeout(timeoutId);
172
+ };
173
+ }, [
174
+ appendOutput,
175
+ dispatch,
176
+ hasCachedOutput,
177
+ hostID,
178
+ isExpanded,
179
+ jobID,
180
+ replaceOutput,
181
+ responseFinished,
182
+ ]);
183
+
184
+ return liveOutput;
185
+ };
@@ -114,16 +114,6 @@ const JobWizardPageRerun = ({
114
114
  header={title}
115
115
  breadcrumbOptions={breadcrumbOptions}
116
116
  searchable={false}
117
- toolbarButtons={
118
- <Button
119
- ouiaId="job-wizard-rerun-old-form-button"
120
- variant="link"
121
- component="a"
122
- href={`/old/job_invocations/${id}/rerun${search}`}
123
- >
124
- {__('Use legacy form')}
125
- </Button>
126
- }
127
117
  >
128
118
  <React.Fragment>
129
119
  <React.Fragment>
@@ -0,0 +1,23 @@
1
+ import { buildHostSearchFromIds } from '../autofill';
2
+
3
+ describe('buildHostSearchFromIds', () => {
4
+ it('builds id search for numeric host ids', () => {
5
+ expect(buildHostSearchFromIds('105')).toBe('id ^ (105)');
6
+ expect(buildHostSearchFromIds(['105', '37'])).toBe('id ^ (105,37)');
7
+ });
8
+
9
+ it('builds name search for hostname/FQDN host ids', () => {
10
+ expect(buildHostSearchFromIds('client.example.com')).toBe(
11
+ 'name ^ (client.example.com)'
12
+ );
13
+ expect(buildHostSearchFromIds(['host1', 'host3'])).toBe(
14
+ 'name ^ (host1, host3)'
15
+ );
16
+ });
17
+
18
+ it('handles mixed numeric and hostname ids', () => {
19
+ expect(buildHostSearchFromIds(['105', 'client.example.com'])).toBe(
20
+ 'id ^ (105) or name ^ (client.example.com)'
21
+ );
22
+ });
23
+ });
@@ -105,7 +105,7 @@ describe('Job wizard fill', () => {
105
105
  });
106
106
  expectDispatchedGet({
107
107
  key: 'HOST_IDS',
108
- params: { search: 'id = 105 or id = 37' },
108
+ params: { search: 'id ^ (105,37)' },
109
109
  url: '/api/hosts',
110
110
  });
111
111
  expectDispatchedGet({
@@ -11,6 +11,21 @@ import {
11
11
  import { selectHostBookmarks } from './JobWizardSelectors';
12
12
  import './JobWizard.scss';
13
13
 
14
+ export const buildHostSearchFromIds = hostIds => {
15
+ const ids = Array.isArray(hostIds) ? hostIds : [hostIds];
16
+ const isNumericId = id => /^\d+$/.test(String(id));
17
+ const numericIds = ids.filter(isNumericId);
18
+ const hostnames = ids.filter(id => !isNumericId(id));
19
+ const parts = [];
20
+ if (numericIds.length) {
21
+ parts.push(`id ^ (${numericIds.join(',')})`);
22
+ }
23
+ if (hostnames.length) {
24
+ parts.push(`name ^ (${hostnames.join(', ')})`);
25
+ }
26
+ return parts.join(' or ');
27
+ };
28
+
14
29
  export const useAutoFill = ({
15
30
  fills,
16
31
  setFills,
@@ -52,9 +67,7 @@ export const useAutoFill = ({
52
67
  setFills({});
53
68
  if (hostIds) {
54
69
  setSelectedBookmark(null);
55
- const hostSearch = Array.isArray(hostIds)
56
- ? `id = ${hostIds.join(' or id = ')}`
57
- : `id = ${hostIds}`;
70
+ const hostSearch = buildHostSearchFromIds(hostIds);
58
71
  dispatch(
59
72
  get({
60
73
  key: HOST_IDS,
@@ -69,8 +82,9 @@ export const useAutoFill = ({
69
82
  // eslint-disable-next-line camelcase
70
83
  ({ id, name, display_name }) => ({
71
84
  id,
85
+ name,
72
86
  // eslint-disable-next-line camelcase
73
- name: display_name || name,
87
+ displayName: display_name || name,
74
88
  })
75
89
  ),
76
90
  }));
@@ -1,7 +1,5 @@
1
1
  import React, { useState } from 'react';
2
2
  import { isEmpty } from 'lodash';
3
- import PropTypes from 'prop-types';
4
- import { Button } from '@patternfly/react-core';
5
3
  import { translate as __ } from 'foremanReact/common/I18n';
6
4
  import { STATUS } from 'foremanReact/constants';
7
5
  import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
@@ -13,7 +11,7 @@ import {
13
11
  currentPermissionsUrl,
14
12
  } from './JobWizardConstants';
15
13
 
16
- const JobWizardPage = ({ location: { search } }) => {
14
+ const JobWizardPage = () => {
17
15
  const title = __('Run job');
18
16
  const breadcrumbOptions = {
19
17
  breadcrumbItems: [
@@ -57,16 +55,6 @@ const JobWizardPage = ({ location: { search } }) => {
57
55
  header={title}
58
56
  breadcrumbOptions={breadcrumbOptions}
59
57
  searchable={false}
60
- toolbarButtons={
61
- <Button
62
- ouiaId="legacy-form"
63
- variant="link"
64
- component="a"
65
- href={`/old/job_invocations/new${search}`}
66
- >
67
- {__('Use legacy form')}
68
- </Button>
69
- }
70
58
  pageSectionType="wizard"
71
59
  >
72
60
  <React.Fragment>
@@ -76,9 +64,4 @@ const JobWizardPage = ({ location: { search } }) => {
76
64
  );
77
65
  };
78
66
 
79
- JobWizardPage.propTypes = {
80
- location: PropTypes.shape({
81
- search: PropTypes.string,
82
- }).isRequired,
83
- };
84
67
  export default JobWizardPage;
@@ -163,7 +163,7 @@ describe('Hosts', () => {
163
163
  });
164
164
  api.get.mock.calls.forEach(call => {
165
165
  if (call[0].key === 'HOST_IDS') {
166
- expect(call[0].params).toEqual({ search: 'id = host1 or id = host3' });
166
+ expect(call[0].params).toEqual({ search: 'name ^ (host1, host3)' });
167
167
  }
168
168
  });
169
169
 
@@ -171,6 +171,54 @@ describe('Hosts', () => {
171
171
  expect(screen.queryAllByText('host2')).toHaveLength(0);
172
172
  expect(screen.queryAllByText('host3')).toHaveLength(1);
173
173
  });
174
+
175
+ it('shows displayName chip when autofill receives short display_name', async () => {
176
+ routerSelectors.selectRouterLocation.mockImplementation(() => ({
177
+ search: '?host_ids%5B%5D=2',
178
+ }));
179
+
180
+ const defaultGet = api.get.getMockImplementation();
181
+ api.get.mockImplementation(({ handleSuccess, ...action }) => {
182
+ if (action.key === 'HOST_IDS') {
183
+ handleSuccess &&
184
+ handleSuccess({
185
+ data: {
186
+ results: [
187
+ {
188
+ id: 2,
189
+ name: 'centos9-katello-client.example.com',
190
+ display_name: 'centos9-katello-client',
191
+ },
192
+ ],
193
+ },
194
+ });
195
+ return { type: 'get', ...action };
196
+ }
197
+ return defaultGet({ handleSuccess, ...action });
198
+ });
199
+
200
+ try {
201
+ render(
202
+ <MockedProvider mocks={gqlMock} addTypename={false}>
203
+ <Provider store={store}>
204
+ <JobWizard />
205
+ </Provider>
206
+ </MockedProvider>
207
+ );
208
+
209
+ await act(async () => {
210
+ fireEvent.click(screen.getByText('Target hosts and inputs'));
211
+ });
212
+
213
+ expect(screen.queryAllByText('centos9-katello-client')).toHaveLength(1);
214
+ expect(
215
+ screen.queryAllByText('centos9-katello-client.example.com')
216
+ ).toHaveLength(0);
217
+ } finally {
218
+ mockApi(api);
219
+ }
220
+ });
221
+
174
222
  it('Host fill search from url', async () => {
175
223
  routerSelectors.selectRouterLocation.mockImplementation(() => ({
176
224
  search: 'search=os=gnome',
@@ -1,8 +1,6 @@
1
1
  import { registerRoutes } from 'foremanReact/routes/RoutingService';
2
2
  import routes from './Routes/routes';
3
- import registerReducers from './react_app/extend/reducers';
4
3
  import registerFills from './react_app/extend/Fills';
5
4
 
6
- registerReducers();
7
5
  registerRoutes('foreman_remote_execution', routes);
8
6
  registerFills();
data/webpack/index.js CHANGED
@@ -1,15 +0,0 @@
1
- import componentRegistry from 'foremanReact/components/componentRegistry';
2
- import JobInvocationContainer from './react_app/components/jobInvocations';
3
- import TargetingHosts from './react_app/components/TargetingHosts';
4
- import registerReducers from './react_app/extend/reducers';
5
-
6
- registerReducers();
7
-
8
- const components = [
9
- { name: 'JobInvocationContainer', type: JobInvocationContainer },
10
- { name: 'TargetingHosts', type: TargetingHosts },
11
- ];
12
-
13
- components.forEach(component => {
14
- componentRegistry.register(component);
15
- });
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { screen, fireEvent, render, act } from '@testing-library/react';
3
- import '@testing-library/jest-dom/extend-expect';
3
+ import '@testing-library/jest-dom';
4
4
  import RexInterface from '../RexInterface';
5
5
 
6
6
  const fixtures = {
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_remote_execution
3
3
  version: !ruby/object:Gem::Version
4
- version: 18.0.1
4
+ version: 18.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Foreman Remote Execution team
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-10-02 00:00:00.000000000 Z
10
+ date: 2026-10-26 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: deface
@@ -67,7 +67,6 @@ files:
67
67
  - app/assets/javascripts/foreman_remote_execution/template_invocation.js
68
68
  - app/assets/stylesheets/foreman_remote_execution/foreman_remote_execution.css
69
69
  - app/assets/stylesheets/foreman_remote_execution/job_invocations.scss
70
- - app/assets/stylesheets/foreman_remote_execution/modal_window.scss
71
70
  - app/assets/stylesheets/foreman_remote_execution/template_invocation.scss
72
71
  - app/controllers/api/v2/foreign_input_sets_controller.rb
73
72
  - app/controllers/api/v2/job_invocations_controller.rb
@@ -96,7 +95,6 @@ files:
96
95
  - app/helpers/concerns/foreman_remote_execution/job_templates_extensions.rb
97
96
  - app/helpers/hosts_extensions_helper.rb
98
97
  - app/helpers/job_invocation_output_helper.rb
99
- - app/helpers/job_invocations_chart_helper.rb
100
98
  - app/helpers/job_invocations_helper.rb
101
99
  - app/helpers/remote_execution_helper.rb
102
100
  - app/lib/actions/middleware/bind_job_invocation.rb
@@ -182,25 +180,8 @@ files:
182
180
  - app/views/api/v2/template_invocations/template_invocations.json.rabl
183
181
  - app/views/dashboard/_latest-jobs.html.erb
184
182
  - app/views/job_invocation_task_groups/_job_invocation_task_groups.html.erb
185
- - app/views/job_invocations/_card_results.html.erb
186
- - app/views/job_invocations/_card_schedule.html.erb
187
- - app/views/job_invocations/_card_target_hosts.html.erb
188
- - app/views/job_invocations/_card_user_input.html.erb
189
- - app/views/job_invocations/_description_fields.html.erb
190
- - app/views/job_invocations/_form.html.erb
191
183
  - app/views/job_invocations/_preview_hosts_list.html.erb
192
- - app/views/job_invocations/_preview_hosts_modal.html.erb
193
- - app/views/job_invocations/_rerun_taxonomies.html.erb
194
- - app/views/job_invocations/_tab_hosts.html.erb
195
- - app/views/job_invocations/_tab_overview.html.erb
196
- - app/views/job_invocations/_tab_preview_templates.html.erb
197
- - app/views/job_invocations/_user_input.html.erb
198
184
  - app/views/job_invocations/index.html.erb
199
- - app/views/job_invocations/new.html.erb
200
- - app/views/job_invocations/refresh.js.erb
201
- - app/views/job_invocations/show.html.erb
202
- - app/views/job_invocations/show.js.erb
203
- - app/views/job_invocations/show.json.erb
204
185
  - app/views/job_invocations/welcome.html.erb
205
186
  - app/views/job_templates/_alerts.html.erb
206
187
  - app/views/job_templates/_custom_tab_headers.html.erb
@@ -356,7 +337,6 @@ files:
356
337
  - test/graphql/queries/job_invocations_query_test.rb
357
338
  - test/helpers/remote_execution_helper_test.rb
358
339
  - test/integration/job_wizard_category_js_test.rb
359
- - test/support/remote_execution_helper.rb
360
340
  - test/test_plugin_helper.rb
361
341
  - test/unit/actions/run_host_job_test.rb
362
342
  - test/unit/actions/run_hosts_job_test.rb
@@ -396,6 +376,7 @@ files:
396
376
  - webpack/JobInvocationDetail/TemplateInvocationComponents/PreviewTemplate.js
397
377
  - webpack/JobInvocationDetail/TemplateInvocationComponents/TemplateActionButtons.js
398
378
  - webpack/JobInvocationDetail/TemplateInvocationComponents/index.scss
379
+ - webpack/JobInvocationDetail/TemplateInvocationHelpers.js
399
380
  - webpack/JobInvocationDetail/TemplateInvocationPage.js
400
381
  - webpack/JobInvocationDetail/__tests__/JobInvocationDetailEmptyState.test.js
401
382
  - webpack/JobInvocationDetail/__tests__/JobInvocationHostTablePolling.test.js
@@ -404,11 +385,13 @@ files:
404
385
  - webpack/JobInvocationDetail/__tests__/OutputCodeBlock.test.js
405
386
  - webpack/JobInvocationDetail/__tests__/TableToolbarActions.test.js
406
387
  - webpack/JobInvocationDetail/__tests__/TemplateInvocation.test.js
388
+ - webpack/JobInvocationDetail/__tests__/TemplateInvocationHelpers.test.js
407
389
  - webpack/JobInvocationDetail/__tests__/TemplateInvocationPolling.test.js
408
390
  - webpack/JobInvocationDetail/__tests__/areAllHostsTerminal.test.js
409
391
  - webpack/JobInvocationDetail/__tests__/fixtures.js
410
392
  - webpack/JobInvocationDetail/__tests__/foremanTestHelpers.js
411
393
  - webpack/JobInvocationDetail/index.js
394
+ - webpack/JobInvocationDetail/useTemplateInvocationOutputPolling.js
412
395
  - webpack/JobWizard/Footer.js
413
396
  - webpack/JobWizard/JobWizard.js
414
397
  - webpack/JobWizard/JobWizard.scss
@@ -420,6 +403,7 @@ files:
420
403
  - webpack/JobWizard/StartsErrorAlert.js
421
404
  - webpack/JobWizard/__tests__/Footer.test.js
422
405
  - webpack/JobWizard/__tests__/JobWizardPageRerun.test.js
406
+ - webpack/JobWizard/__tests__/autofill.test.js
423
407
  - webpack/JobWizard/__tests__/fixtures.js
424
408
  - webpack/JobWizard/__tests__/integration.test.js
425
409
  - webpack/JobWizard/__tests__/validation.test.js
@@ -513,38 +497,7 @@ files:
513
497
  - webpack/react_app/components/RegistrationExtension/RexInterface.js
514
498
  - webpack/react_app/components/RegistrationExtension/RexPull.js
515
499
  - webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js
516
- - webpack/react_app/components/TargetingHosts/TargetingHosts.js
517
- - webpack/react_app/components/TargetingHosts/TargetingHostsConsts.js
518
- - webpack/react_app/components/TargetingHosts/TargetingHostsHelpers.js
519
- - webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.js
520
- - webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.scss
521
- - webpack/react_app/components/TargetingHosts/TargetingHostsPage.js
522
- - webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss
523
- - webpack/react_app/components/TargetingHosts/TargetingHostsSelectors.js
524
- - webpack/react_app/components/TargetingHosts/__tests__/HostItem.test.js
525
- - webpack/react_app/components/TargetingHosts/__tests__/HostStatus.test.js
526
- - webpack/react_app/components/TargetingHosts/__tests__/TargetingHosts.test.js
527
- - webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsPage.test.js
528
- - webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js
529
- - webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap
530
- - webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHosts.test.js.snap
531
- - webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap
532
- - webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap
533
- - webpack/react_app/components/TargetingHosts/__tests__/fixtures.js
534
- - webpack/react_app/components/TargetingHosts/components/HostItem.js
535
- - webpack/react_app/components/TargetingHosts/components/HostStatus.js
536
- - webpack/react_app/components/TargetingHosts/index.js
537
- - webpack/react_app/components/jobInvocations/AggregateStatus/index.js
538
- - webpack/react_app/components/jobInvocations/AggregateStatus/index.test.js
539
- - webpack/react_app/components/jobInvocations/index.js
540
500
  - webpack/react_app/extend/Fills.js
541
- - webpack/react_app/extend/reducers.js
542
- - webpack/react_app/redux/actions/jobInvocations/index.js
543
- - webpack/react_app/redux/consts.js
544
- - webpack/react_app/redux/reducers/index.js
545
- - webpack/react_app/redux/reducers/jobInvocations/index.fixtures.js
546
- - webpack/react_app/redux/reducers/jobInvocations/index.js
547
- - webpack/react_app/redux/reducers/jobInvocations/index.test.js
548
501
  - webpack/test_setup.js
549
502
  homepage: https://github.com/theforeman/foreman_remote_execution
550
503
  licenses:
@@ -1,4 +0,0 @@
1
- #previewHostsModal div.modal-body {
2
- overflow-y: auto;
3
- max-height: 500px;
4
- }
@@ -1,80 +0,0 @@
1
- # frozen_string_literal:true
2
-
3
- module JobInvocationsChartHelper
4
- def job_invocation_chart(invocation)
5
- donut_chart('#status_chart',
6
- job_invocation_data(invocation)[:columns],
7
- job_invocation_data(invocation)[:groups])
8
- end
9
-
10
- def job_invocation_data(invocation)
11
- return @job_invocation_data if @job_invocation_data.present?
12
-
13
- progress_report = invocation.progress_report
14
- success = progress_report[:success]
15
- cancelled = progress_report[:cancelled]
16
- failed = progress_report[:error]
17
- pending = progress_report[:pending]
18
- columns = [[_('Success'), success, '#5CB85C'],
19
- [_('Failed'), failed, '#D9534F'],
20
- [_('Pending'), pending, '#DEDEDE'],
21
- [_('Cancelled'), cancelled, '#B7312D']]
22
- groups = [columns.map(&:first)]
23
-
24
- @job_invocation_data = { :columns => columns, :groups => groups}
25
- end
26
-
27
- def job_invocation_status(invocation, percent = nil, verbose = true)
28
- case invocation.status
29
- when HostStatus::ExecutionStatus::QUEUED
30
- if verbose && invocation.task
31
- _('queued to start executing in %{time}') % {:time => time_ago_in_words(invocation.task.start_at) }
32
- else
33
- _('queued')
34
- end
35
- when HostStatus::ExecutionStatus::RUNNING
36
- percent ||= invocation.progress_report[:progress]
37
- _('running %{percent}%%') % {:percent => percent}
38
- when HostStatus::ExecutionStatus::OK
39
- _('succeeded')
40
- when HostStatus::ExecutionStatus::CANCELLED
41
- _('cancelled')
42
- when HostStatus::ExecutionStatus::ERROR
43
- _('failed')
44
- else
45
- _('unknown status')
46
- end
47
- end
48
-
49
- def job_invocation_success_status
50
- icon_text('ok',
51
- @job_invocation_data[0][1],
52
- :kind => 'pficon')
53
- end
54
-
55
- def job_invocation_failed_status
56
- icon_text('error-circle-o',
57
- @job_invocation_data[1][1],
58
- :kind => 'pficon')
59
- end
60
-
61
- def job_invocation_pending_status
62
- icon_text('running',
63
- @job_invocation_data[2][1],
64
- :kind => 'pficon')
65
- end
66
-
67
- def job_invocation_cancelled_status
68
- icon_text('close',
69
- @job_invocation_data[3][1],
70
- :kind => 'pficon')
71
- end
72
-
73
- def task_failed?(task)
74
- %w(warning error).include? task.result
75
- end
76
-
77
- def task_cancelled?(task)
78
- task.execution_plan.errors.map(&:exception).any? { |exception| exception.class == ::ForemanTasks::Task::TaskCancelledException }
79
- end
80
- end
@@ -1,12 +0,0 @@
1
- <div id="job-invocation-chart">
2
- <div class="card-pf card-pf-accented card-pf-aggregate-status">
3
- <h2 style="height: 18px;" class="card-pf-title">
4
- <a href="#"><%= _('Results') %></a>
5
- </h2>
6
- <div class="card-pf-body">
7
- <div id='status_chart'>
8
- <%= react_component('JobInvocationContainer', data: { url: "/job_invocations/chart?id=#{job_invocation.id}" }) %>
9
- </div>
10
- </div>
11
- </div>
12
- </div>