foreman_remote_execution 4.5.1 → 4.7.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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby_ci.yml +7 -0
- data/app/controllers/ui_job_wizard_controller.rb +7 -0
- data/app/graphql/types/job_invocation.rb +16 -0
- data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +5 -1
- data/app/helpers/remote_execution_helper.rb +9 -3
- data/app/lib/actions/remote_execution/run_hosts_job.rb +1 -1
- data/app/models/job_invocation_composer.rb +3 -3
- data/app/models/job_template.rb +1 -1
- data/app/models/remote_execution_feature.rb +5 -1
- data/app/models/remote_execution_provider.rb +1 -1
- data/app/views/templates/ssh/module_action.erb +1 -0
- data/app/views/templates/ssh/power_action.erb +2 -0
- data/app/views/templates/ssh/puppet_run_once.erb +1 -0
- data/foreman_remote_execution.gemspec +2 -4
- data/lib/foreman_remote_execution/engine.rb +3 -0
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/graphql/queries/job_invocation_query_test.rb +31 -0
- data/test/graphql/queries/job_invocations_query_test.rb +35 -0
- data/test/unit/concerns/host_extensions_test.rb +4 -4
- data/test/unit/input_template_renderer_test.rb +1 -89
- data/test/unit/job_invocation_composer_test.rb +1 -12
- data/webpack/JobWizard/JobWizard.js +28 -8
- data/webpack/JobWizard/JobWizard.scss +39 -0
- data/webpack/JobWizard/JobWizardConstants.js +10 -0
- data/webpack/JobWizard/JobWizardSelectors.js +9 -0
- data/webpack/JobWizard/__tests__/fixtures.js +104 -2
- data/webpack/JobWizard/__tests__/integration.test.js +13 -85
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +21 -4
- data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +67 -0
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +73 -59
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +135 -16
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +23 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +122 -51
- data/webpack/JobWizard/steps/Schedule/QueryType.js +48 -0
- data/webpack/JobWizard/steps/Schedule/RepeatOn.js +61 -0
- data/webpack/JobWizard/steps/Schedule/ScheduleType.js +25 -0
- data/webpack/JobWizard/steps/Schedule/StartEndDates.js +51 -0
- data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +22 -0
- data/webpack/JobWizard/steps/Schedule/index.js +41 -0
- data/webpack/JobWizard/steps/form/FormHelpers.js +1 -0
- data/webpack/JobWizard/steps/form/Formatter.js +149 -0
- data/webpack/JobWizard/steps/form/NumberInput.js +33 -0
- data/webpack/JobWizard/steps/form/SelectField.js +14 -2
- data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +76 -0
- data/webpack/__mocks__/foremanReact/components/SearchBar.js +18 -1
- data/webpack/react_app/components/RecentJobsCard/JobStatusIcon.js +43 -0
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +72 -66
- data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +98 -0
- data/webpack/react_app/components/RecentJobsCard/constants.js +11 -0
- data/webpack/react_app/components/RecentJobsCard/styles.scss +11 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -0
- data/webpack/react_app/extend/fillRecentJobsCard.js +1 -1
- metadata +23 -27
- data/webpack/JobWizard/__tests__/JobWizard.test.js +0 -13
- data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +0 -32
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +0 -249
- data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +0 -113
- data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +0 -38
- data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +0 -23
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +0 -37
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +0 -23
- data/webpack/react_app/components/RecentJobsCard/styles.css +0 -15
@@ -350,12 +350,6 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
|
|
350
350
|
end
|
351
351
|
|
352
352
|
describe '#available_bookmarks' do
|
353
|
-
it 'obeys authorization' do
|
354
|
-
composer
|
355
|
-
Bookmark.expects(:authorized).with(:view_bookmarks).returns(Bookmark.where({}))
|
356
|
-
composer.available_bookmarks
|
357
|
-
end
|
358
|
-
|
359
353
|
context 'there are hostgroups and hosts bookmark' do
|
360
354
|
let(:hostgroups) { Bookmark.create(:name => 'hostgroups', :query => 'name = x', :controller => 'hostgroups') }
|
361
355
|
let(:hosts) { Bookmark.create(:name => 'hosts', :query => 'name = x', :controller => 'hosts') }
|
@@ -931,12 +925,7 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
|
|
931
925
|
|
932
926
|
context 'with template in setting present' do
|
933
927
|
before do
|
934
|
-
|
935
|
-
:setting,
|
936
|
-
:name => 'remote_execution_form_job_template',
|
937
|
-
:category => 'Setting::RemoteExecution',
|
938
|
-
:value => setting_template.name
|
939
|
-
)
|
928
|
+
Setting[:remote_execution_form_job_template] = setting_template.name
|
940
929
|
end
|
941
930
|
|
942
931
|
it 'should resolve category to the setting value' do
|
@@ -1,3 +1,4 @@
|
|
1
|
+
/* eslint-disable camelcase */
|
1
2
|
import React, { useState, useEffect, useCallback } from 'react';
|
2
3
|
import { useDispatch, useSelector } from 'react-redux';
|
3
4
|
import { Wizard } from '@patternfly/react-core';
|
@@ -8,6 +9,7 @@ import CategoryAndTemplate from './steps/CategoryAndTemplate/';
|
|
8
9
|
import { AdvancedFields } from './steps/AdvancedFields/AdvancedFields';
|
9
10
|
import { JOB_TEMPLATE } from './JobWizardConstants';
|
10
11
|
import { selectTemplateError } from './JobWizardSelectors';
|
12
|
+
import Schedule from './steps/Schedule/';
|
11
13
|
import './JobWizard.scss';
|
12
14
|
|
13
15
|
export const JobWizard = () => {
|
@@ -16,13 +18,31 @@ export const JobWizard = () => {
|
|
16
18
|
const [advancedValues, setAdvancedValues] = useState({});
|
17
19
|
const dispatch = useDispatch();
|
18
20
|
|
19
|
-
const setDefaults = useCallback(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
const setDefaults = useCallback(
|
22
|
+
({
|
23
|
+
data: {
|
24
|
+
advanced_template_inputs,
|
25
|
+
effective_user,
|
26
|
+
job_template: { executionTimeoutInterval, description_format },
|
27
|
+
},
|
28
|
+
}) => {
|
29
|
+
const advancedTemplateValues = {};
|
30
|
+
const advancedInputs = advanced_template_inputs;
|
31
|
+
if (advancedInputs) {
|
32
|
+
advancedInputs.forEach(input => {
|
33
|
+
advancedTemplateValues[input.name] = input?.default || '';
|
34
|
+
});
|
35
|
+
}
|
36
|
+
setAdvancedValues(currentAdvancedValues => ({
|
37
|
+
...currentAdvancedValues,
|
38
|
+
effectiveUserValue: effective_user?.value || '',
|
39
|
+
timeoutToKill: executionTimeoutInterval || '',
|
40
|
+
templateValues: advancedTemplateValues,
|
41
|
+
description: description_format || '',
|
42
|
+
}));
|
43
|
+
},
|
44
|
+
[]
|
45
|
+
);
|
26
46
|
useEffect(() => {
|
27
47
|
if (jobTemplateID) {
|
28
48
|
dispatch(
|
@@ -72,7 +92,7 @@ export const JobWizard = () => {
|
|
72
92
|
},
|
73
93
|
{
|
74
94
|
name: __('Schedule'),
|
75
|
-
component: <
|
95
|
+
component: <Schedule />,
|
76
96
|
canJumpTo: isTemplate,
|
77
97
|
},
|
78
98
|
{
|
@@ -10,5 +10,44 @@
|
|
10
10
|
.advanced-fields-title {
|
11
11
|
margin-bottom: 10px;
|
12
12
|
}
|
13
|
+
#advanced-fields-job-template {
|
14
|
+
.foreman-search-field {
|
15
|
+
// Giving pf3 search bar a pf4 look
|
16
|
+
.search-bar {
|
17
|
+
display: block;
|
18
|
+
}
|
19
|
+
.input-group-btn {
|
20
|
+
display: none;
|
21
|
+
}
|
22
|
+
li {
|
23
|
+
font-size: 16px;
|
24
|
+
}
|
25
|
+
input {
|
26
|
+
font-size: 16px;
|
27
|
+
height: 36px;
|
28
|
+
}
|
29
|
+
.foreman-autocomplete .autocomplete-focus-shortcut {
|
30
|
+
top: 8px;
|
31
|
+
font-size: 16px;
|
32
|
+
}
|
33
|
+
.foreman-autocomplete .autocomplete-aux {
|
34
|
+
top: 8px;
|
35
|
+
font-size: 16px;
|
36
|
+
.autocomplete-clear-button {
|
37
|
+
font-size: 16px;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
.schedule-tab {
|
45
|
+
input[type='radio'],
|
46
|
+
input[type='checkbox'] {
|
47
|
+
margin: 0;
|
48
|
+
}
|
49
|
+
.advanced-scheduling-button {
|
50
|
+
text-align: start;
|
51
|
+
}
|
13
52
|
}
|
14
53
|
}
|
@@ -1,6 +1,16 @@
|
|
1
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
1
2
|
import { foremanUrl } from 'foremanReact/common/helpers';
|
2
3
|
|
3
4
|
export const JOB_TEMPLATES = 'JOB_TEMPLATES';
|
4
5
|
export const JOB_CATEGORIES = 'JOB_CATEGORIES';
|
5
6
|
export const JOB_TEMPLATE = 'JOB_TEMPLATE';
|
6
7
|
export const templatesUrl = foremanUrl('/api/v2/job_templates');
|
8
|
+
|
9
|
+
export const repeatTypes = {
|
10
|
+
noRepeat: __('Does not repeat'),
|
11
|
+
cronline: __('Cronline'),
|
12
|
+
monthly: __('Monthly'),
|
13
|
+
weekly: __('Weekly'),
|
14
|
+
daily: __('Daily'),
|
15
|
+
hourly: __('Hourly'),
|
16
|
+
};
|
@@ -36,3 +36,12 @@ export const selectTemplateError = state =>
|
|
36
36
|
|
37
37
|
export const selectJobTemplate = state =>
|
38
38
|
selectAPIResponse(state, JOB_TEMPLATE);
|
39
|
+
|
40
|
+
export const selectEffectiveUser = state =>
|
41
|
+
selectAPIResponse(state, JOB_TEMPLATE).effective_user;
|
42
|
+
|
43
|
+
export const selectAdvancedTemplateInputs = state =>
|
44
|
+
selectAPIResponse(state, JOB_TEMPLATE).advanced_template_inputs || [];
|
45
|
+
|
46
|
+
export const selectTemplateInputs = state =>
|
47
|
+
selectAPIResponse(state, JOB_TEMPLATE).template_inputs || [];
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
import configureMockStore from 'redux-mock-store';
|
2
|
+
|
3
|
+
export const jobTemplate = {
|
2
4
|
id: 178,
|
3
|
-
name: '
|
5
|
+
name: 'template1',
|
4
6
|
template:
|
5
7
|
"---\n- hosts: all\n tasks:\n - shell:\n cmd: |\n<%= indent(10) { input('command') } %>\n register: out\n - debug: var=out",
|
6
8
|
snippet: false,
|
@@ -23,4 +25,104 @@ export const jobTemplateResponse = {
|
|
23
25
|
overridable: true,
|
24
26
|
current_user: false,
|
25
27
|
},
|
28
|
+
advanced_template_inputs: [
|
29
|
+
{
|
30
|
+
name: 'adv plain hidden',
|
31
|
+
required: true,
|
32
|
+
input_type: 'user',
|
33
|
+
description: 'some Description',
|
34
|
+
advanced: true,
|
35
|
+
value_type: 'plain',
|
36
|
+
resource_type: 'ansible_roles',
|
37
|
+
default: 'Default val',
|
38
|
+
hidden_value: true,
|
39
|
+
},
|
40
|
+
{
|
41
|
+
name: 'adv plain select',
|
42
|
+
required: false,
|
43
|
+
input_type: 'user',
|
44
|
+
options: 'option 1\r\noption 2\r\noption 3\r\noption 4',
|
45
|
+
advanced: true,
|
46
|
+
value_type: 'plain',
|
47
|
+
resource_type: 'ansible_roles',
|
48
|
+
default: '',
|
49
|
+
hidden_value: false,
|
50
|
+
},
|
51
|
+
{
|
52
|
+
name: 'adv search',
|
53
|
+
required: false,
|
54
|
+
options: '',
|
55
|
+
advanced: true,
|
56
|
+
value_type: 'search',
|
57
|
+
resource_type: 'foreman_tasks/tasks',
|
58
|
+
default: '',
|
59
|
+
hidden_value: false,
|
60
|
+
},
|
61
|
+
{
|
62
|
+
name: 'adv date',
|
63
|
+
required: false,
|
64
|
+
options: '',
|
65
|
+
advanced: true,
|
66
|
+
value_type: 'date',
|
67
|
+
resource_type: 'ansible_roles',
|
68
|
+
default: '',
|
69
|
+
hidden_value: false,
|
70
|
+
},
|
71
|
+
],
|
72
|
+
template_inputs: [
|
73
|
+
{
|
74
|
+
name: 'plain hidden',
|
75
|
+
required: true,
|
76
|
+
input_type: 'user',
|
77
|
+
description: 'some Description',
|
78
|
+
advanced: false,
|
79
|
+
value_type: 'plain',
|
80
|
+
resource_type: 'ansible_roles',
|
81
|
+
default: 'Default val',
|
82
|
+
hidden_value: true,
|
83
|
+
},
|
84
|
+
],
|
85
|
+
};
|
86
|
+
|
87
|
+
export const jobCategories = ['Ansible Commands', 'Puppet', 'Services'];
|
88
|
+
|
89
|
+
export const testSetup = (selectors, api) => {
|
90
|
+
jest.spyOn(api, 'get');
|
91
|
+
jest.spyOn(selectors, 'selectJobTemplate');
|
92
|
+
jest.spyOn(selectors, 'selectJobTemplates');
|
93
|
+
jest.spyOn(selectors, 'selectJobCategories');
|
94
|
+
jest.spyOn(selectors, 'selectJobCategoriesStatus');
|
95
|
+
|
96
|
+
selectors.selectJobCategories.mockImplementation(() => jobCategories);
|
97
|
+
selectors.selectJobTemplates.mockImplementation(() => [
|
98
|
+
jobTemplate,
|
99
|
+
{ ...jobTemplate, id: 2, name: 'template2' },
|
100
|
+
]);
|
101
|
+
const mockStore = configureMockStore([]);
|
102
|
+
const store = mockStore({});
|
103
|
+
return store;
|
104
|
+
};
|
105
|
+
|
106
|
+
export const mockTemplate = selectors => {
|
107
|
+
selectors.selectJobTemplate.mockImplementation(() => jobTemplate);
|
108
|
+
selectors.selectJobCategoriesStatus.mockImplementation(() => 'RESOLVED');
|
109
|
+
};
|
110
|
+
export const mockApi = api => {
|
111
|
+
api.get.mockImplementation(({ handleSuccess, ...action }) => {
|
112
|
+
if (action.key === 'JOB_CATEGORIES') {
|
113
|
+
handleSuccess &&
|
114
|
+
handleSuccess({ data: { job_categories: jobCategories } });
|
115
|
+
} else if (action.key === 'JOB_TEMPLATE') {
|
116
|
+
handleSuccess &&
|
117
|
+
handleSuccess({
|
118
|
+
data: jobTemplateResponse,
|
119
|
+
});
|
120
|
+
} else if (action.key === 'JOB_TEMPLATES') {
|
121
|
+
handleSuccess &&
|
122
|
+
handleSuccess({
|
123
|
+
data: { results: [jobTemplate] },
|
124
|
+
});
|
125
|
+
}
|
126
|
+
return { type: 'get', ...action };
|
127
|
+
});
|
26
128
|
};
|
@@ -1,40 +1,27 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { Provider } from 'react-redux';
|
3
|
-
import configureMockStore from 'redux-mock-store';
|
4
3
|
import { mount } from '@theforeman/test';
|
5
4
|
import { render, fireEvent, screen, act } from '@testing-library/react';
|
6
5
|
import * as api from 'foremanReact/redux/API';
|
7
6
|
import { JobWizard } from '../JobWizard';
|
8
7
|
import * as selectors from '../JobWizardSelectors';
|
9
|
-
import {
|
8
|
+
import {
|
9
|
+
testSetup,
|
10
|
+
mockApi,
|
11
|
+
jobCategories,
|
12
|
+
jobTemplateResponse as jobTemplate,
|
13
|
+
} from './fixtures';
|
10
14
|
|
11
|
-
|
12
|
-
jest.spyOn(selectors, 'selectJobTemplate');
|
13
|
-
jest.spyOn(selectors, 'selectJobTemplates');
|
14
|
-
jest.spyOn(selectors, 'selectJobCategories');
|
15
|
-
jest.spyOn(selectors, 'selectJobCategoriesStatus');
|
15
|
+
const store = testSetup(selectors, api);
|
16
16
|
|
17
|
-
|
17
|
+
selectors.selectJobTemplate.mockImplementation(() => {});
|
18
18
|
|
19
19
|
api.get.mockImplementation(({ handleSuccess, ...action }) => {
|
20
20
|
if (action.key === 'JOB_CATEGORIES') {
|
21
21
|
handleSuccess && handleSuccess({ data: { job_categories: jobCategories } });
|
22
|
-
} else if (action.key === 'JOB_TEMPLATE') {
|
23
|
-
handleSuccess &&
|
24
|
-
handleSuccess({
|
25
|
-
data: jobTemplate,
|
26
|
-
});
|
27
22
|
}
|
28
23
|
return { type: 'get', ...action };
|
29
24
|
});
|
30
|
-
|
31
|
-
selectors.selectJobTemplate.mockImplementation(() => null);
|
32
|
-
selectors.selectJobCategories.mockImplementation(() => jobCategories);
|
33
|
-
selectors.selectJobCategoriesStatus.mockImplementation(() => null);
|
34
|
-
selectors.selectJobTemplates.mockImplementation(() => jobTemplates);
|
35
|
-
|
36
|
-
const mockStore = configureMockStore([]);
|
37
|
-
const store = mockStore({});
|
38
25
|
describe('Job wizard fill', () => {
|
39
26
|
it('should select template', async () => {
|
40
27
|
const wrapper = mount(
|
@@ -51,7 +38,10 @@ describe('Job wizard fill', () => {
|
|
51
38
|
selectors.selectJobTemplate.mockImplementation(() => jobTemplate);
|
52
39
|
wrapper.find('.pf-c-button.pf-c-select__toggle-button').simulate('click');
|
53
40
|
await act(async () => {
|
54
|
-
await wrapper
|
41
|
+
await wrapper
|
42
|
+
.find('.pf-c-select__menu-item')
|
43
|
+
.first()
|
44
|
+
.simulate('click');
|
55
45
|
await wrapper.update();
|
56
46
|
});
|
57
47
|
expect(store.getActions().slice(-1)).toMatchSnapshot('select template');
|
@@ -60,74 +50,12 @@ describe('Job wizard fill', () => {
|
|
60
50
|
);
|
61
51
|
});
|
62
52
|
|
63
|
-
it('should save data between steps for advanced fields', async () => {
|
64
|
-
const wrapper = mount(
|
65
|
-
<Provider store={store}>
|
66
|
-
<JobWizard advancedValues={{}} setAdvancedValues={jest.fn()} />
|
67
|
-
</Provider>
|
68
|
-
);
|
69
|
-
// setup
|
70
|
-
selectors.selectJobCategoriesStatus.mockImplementation(() => 'RESOLVED');
|
71
|
-
selectors.selectJobTemplate.mockImplementation(() => jobTemplate);
|
72
|
-
wrapper.find('.pf-c-button.pf-c-select__toggle-button').simulate('click');
|
73
|
-
wrapper.find('.pf-c-select__menu-item').simulate('click');
|
74
|
-
|
75
|
-
// test
|
76
|
-
expect(wrapper.find('.pf-c-wizard__nav-link.pf-m-disabled')).toHaveLength(
|
77
|
-
0
|
78
|
-
);
|
79
|
-
wrapper
|
80
|
-
.find('.pf-c-wizard__nav-link')
|
81
|
-
.at(2)
|
82
|
-
.simulate('click'); // Advanced step
|
83
|
-
const effectiveUserInput = () => wrapper.find('input#effective-user');
|
84
|
-
const effectiveUesrValue = 'effective user new value';
|
85
|
-
effectiveUserInput().getDOMNode().value = effectiveUesrValue;
|
86
|
-
await act(async () => {
|
87
|
-
await effectiveUserInput().simulate('change');
|
88
|
-
wrapper.update();
|
89
|
-
});
|
90
|
-
|
91
|
-
expect(effectiveUserInput().prop('value')).toEqual(effectiveUesrValue);
|
92
|
-
|
93
|
-
wrapper
|
94
|
-
.find('.pf-c-wizard__nav-link')
|
95
|
-
.at(1)
|
96
|
-
.simulate('click');
|
97
|
-
|
98
|
-
expect(wrapper.find('.pf-c-wizard__nav-link.pf-m-current').text()).toEqual(
|
99
|
-
'Target Hosts'
|
100
|
-
);
|
101
|
-
wrapper
|
102
|
-
.find('.pf-c-wizard__nav-link')
|
103
|
-
.at(2)
|
104
|
-
.simulate('click'); // Advanced step
|
105
|
-
|
106
|
-
expect(effectiveUserInput().prop('value')).toEqual(effectiveUesrValue);
|
107
|
-
});
|
108
|
-
|
109
53
|
it('have all steps', async () => {
|
110
54
|
selectors.selectJobCategoriesStatus.mockImplementation(() => null);
|
111
55
|
selectors.selectJobTemplate.mockRestore();
|
112
56
|
selectors.selectJobTemplates.mockRestore();
|
113
57
|
selectors.selectJobCategories.mockRestore();
|
114
|
-
api
|
115
|
-
if (action.key === 'JOB_CATEGORIES') {
|
116
|
-
handleSuccess &&
|
117
|
-
handleSuccess({ data: { job_categories: jobCategories } });
|
118
|
-
} else if (action.key === 'JOB_TEMPLATE') {
|
119
|
-
handleSuccess &&
|
120
|
-
handleSuccess({
|
121
|
-
data: jobTemplate,
|
122
|
-
});
|
123
|
-
} else if (action.key === 'JOB_TEMPLATES') {
|
124
|
-
handleSuccess &&
|
125
|
-
handleSuccess({
|
126
|
-
data: { results: [jobTemplate.job_template] },
|
127
|
-
});
|
128
|
-
}
|
129
|
-
return { type: 'get', ...action };
|
130
|
-
});
|
58
|
+
mockApi(api);
|
131
59
|
|
132
60
|
render(
|
133
61
|
<Provider store={store}>
|
@@ -3,7 +3,11 @@ import PropTypes from 'prop-types';
|
|
3
3
|
import { useSelector } from 'react-redux';
|
4
4
|
import { Title, Form } from '@patternfly/react-core';
|
5
5
|
import { translate as __ } from 'foremanReact/common/I18n';
|
6
|
-
import {
|
6
|
+
import {
|
7
|
+
selectEffectiveUser,
|
8
|
+
selectAdvancedTemplateInputs,
|
9
|
+
selectTemplateInputs,
|
10
|
+
} from '../../JobWizardSelectors';
|
7
11
|
import {
|
8
12
|
EffectiveUserField,
|
9
13
|
TimeoutToKillField,
|
@@ -12,17 +16,25 @@ import {
|
|
12
16
|
EffectiveUserPasswordField,
|
13
17
|
ConcurrencyLevelField,
|
14
18
|
TimeSpanLevelField,
|
19
|
+
TemplateInputsFields,
|
15
20
|
} from './Fields';
|
21
|
+
import { DescriptionField } from './DescriptionField';
|
16
22
|
|
17
23
|
export const AdvancedFields = ({ advancedValues, setAdvancedValues }) => {
|
18
|
-
const
|
19
|
-
const
|
24
|
+
const effectiveUser = useSelector(selectEffectiveUser);
|
25
|
+
const advancedTemplateInputs = useSelector(selectAdvancedTemplateInputs);
|
26
|
+
const templateInputs = useSelector(selectTemplateInputs);
|
20
27
|
return (
|
21
28
|
<>
|
22
29
|
<Title headingLevel="h2" className="advanced-fields-title">
|
23
30
|
{__('Advanced Fields')}
|
24
31
|
</Title>
|
25
|
-
<Form>
|
32
|
+
<Form id="advanced-fields-job-template" autoComplete="off">
|
33
|
+
<TemplateInputsFields
|
34
|
+
inputs={advancedTemplateInputs}
|
35
|
+
value={advancedValues.templateValues}
|
36
|
+
setValue={newValue => setAdvancedValues({ templateValues: newValue })}
|
37
|
+
/>
|
26
38
|
{effectiveUser?.overridable && (
|
27
39
|
<EffectiveUserField
|
28
40
|
value={advancedValues.effectiveUserValue}
|
@@ -33,6 +45,11 @@ export const AdvancedFields = ({ advancedValues, setAdvancedValues }) => {
|
|
33
45
|
}
|
34
46
|
/>
|
35
47
|
)}
|
48
|
+
<DescriptionField
|
49
|
+
inputs={templateInputs}
|
50
|
+
value={advancedValues.description}
|
51
|
+
setValue={newValue => setAdvancedValues({ description: newValue })}
|
52
|
+
/>
|
36
53
|
<TimeoutToKillField
|
37
54
|
value={advancedValues.timeoutToKill}
|
38
55
|
setValue={newValue =>
|