foreman_templates 7.0.7 → 8.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/template_controller.rb +6 -1
  3. data/app/controllers/concerns/foreman/controller/parameters/template_params.rb +22 -2
  4. data/app/models/setting/template_sync.rb +12 -2
  5. data/app/services/foreman_templates/template_exporter.rb +2 -2
  6. data/app/services/foreman_templates/template_importer.rb +20 -4
  7. data/app/views/template_syncs/index.html.erb +2 -6
  8. data/db/migrate/20180627134929_change_lock_setting.rb +5 -0
  9. data/lib/foreman_templates/engine.rb +6 -0
  10. data/lib/foreman_templates/version.rb +1 -1
  11. data/package.json +15 -21
  12. data/webpack/__mocks__/foremanReact/components/common/forms/ForemanForm.js +2 -0
  13. data/webpack/components/NewTemplateSync/__fixtures__/templateSyncSettings.fixtures.js +4 -4
  14. data/webpack/components/NewTemplateSync/__tests__/__snapshots__/NewTemplateSync.test.js.snap +2 -2
  15. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncForm.js +68 -71
  16. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncFormSelectors.js +10 -13
  17. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/__tests__/NewTemplateSyncFormSelectors.test.js +1 -19
  18. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/__tests__/__snapshots__/NewTemplateSyncFormSelectors.test.js.snap +7 -36
  19. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/index.js +3 -20
  20. data/webpack/components/NewTemplateSync/components/SyncSettingField.js +5 -11
  21. data/webpack/components/NewTemplateSync/components/SyncSettingFields.js +8 -25
  22. data/webpack/components/NewTemplateSync/components/TextButtonField/index.js +27 -20
  23. data/webpack/components/NewTemplateSync/components/__tests__/SyncSettingField.test.js +2 -1
  24. data/webpack/components/NewTemplateSync/components/__tests__/SyncSettingFields.test.js +1 -0
  25. data/webpack/components/NewTemplateSync/components/__tests__/TextButtonField.test.js +4 -4
  26. data/webpack/components/NewTemplateSync/components/__tests__/__snapshots__/SyncSettingField.test.js.snap +18 -27
  27. data/webpack/components/NewTemplateSync/components/__tests__/__snapshots__/SyncSettingFields.test.js.snap +5 -3
  28. data/webpack/components/NewTemplateSync/components/__tests__/__snapshots__/TextButtonField.test.js.snap +8 -91
  29. data/webpack/components/TemplateSyncResult/__fixtures__/templateSyncResult.fixtures.js +2 -2
  30. data/webpack/components/TemplateSyncResult/__tests__/__snapshots__/TemplateSyncResult.test.js.snap +3 -1
  31. data/webpack/components/TemplateSyncResult/__tests__/__snapshots__/TemplateSyncResultReducer.test.js.snap +3 -1
  32. data/webpack/components/TemplateSyncResult/components/SyncedTemplate/helpers.js +19 -9
  33. data/webpack/components/TemplateSyncResult/components/__tests__/__snapshots__/SyncResultList.test.js.snap +3 -1
  34. data/webpack/components/TemplateSyncResult/components/__tests__/__snapshots__/SyncedTemplate.test.js.snap +39 -15
  35. data/webpack/testSetup.js +2 -1
  36. metadata +4 -7
  37. data/webpack/__mocks__/foremanReact/components/Layout/LayoutSelectors.js +0 -4
  38. data/webpack/__mocks__/foremanReact/components/common/forms/Form.js +0 -2
  39. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncFormConstants.js +0 -1
  40. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/__tests__/NewTemplateSyncForm.test.js +0 -42
  41. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/__tests__/__snapshots__/NewTemplateSyncForm.test.js.snap +0 -186
@@ -5,20 +5,17 @@ import {
5
5
  selectExportSettings,
6
6
  } from '../../NewTemplateSyncSelectors';
7
7
 
8
+ export const transformInitialValues = settings =>
9
+ settings.reduce(
10
+ (memo, item) => Object.assign(memo, { [item.name]: item.value }),
11
+ {}
12
+ );
13
+
8
14
  export const selectInitialFormValues = createSelector(
9
15
  selectImportSettings,
10
16
  selectExportSettings,
11
- (importSettings, exportSettings) =>
12
- importSettings
13
- .concat(exportSettings)
14
- .reduce(
15
- (memo, item) => Object.assign(memo, { [item.name]: item.value }),
16
- {}
17
- )
17
+ (importSettings, exportSettings) => ({
18
+ import: transformInitialValues(importSettings),
19
+ export: transformInitialValues(exportSettings),
20
+ })
18
21
  );
19
-
20
- const selectFormState = (formName, state) =>
21
- state.form && state.form[formName] ? state.form[formName] : {};
22
-
23
- export const selectRegisteredFields = (formName, state) =>
24
- selectFormState(formName, state).registeredFields || {};
@@ -1,35 +1,17 @@
1
1
  import { testSelectorsSnapshotWithFixtures } from 'react-redux-test-utils';
2
2
 
3
- import { NEW_TEMPLATE_SYNC_FORM_NAME } from '../NewTemplateSyncFormConstants';
4
3
  import {
5
- registeredImportSettings,
6
- initialValues,
7
4
  stateFactory,
8
5
  importSettings,
9
6
  exportSettings,
10
7
  } from '../../../__fixtures__/templateSyncSettings.fixtures';
11
8
 
12
- import {
13
- selectInitialFormValues,
14
- selectRegisteredFields,
15
- } from '../NewTemplateSyncFormSelectors';
16
-
17
- const formStateFactory = obj => ({
18
- form: {
19
- [NEW_TEMPLATE_SYNC_FORM_NAME]: obj,
20
- },
21
- });
9
+ import { selectInitialFormValues } from '../NewTemplateSyncFormSelectors';
22
10
 
23
11
  const fixtures = {
24
- 'should return registered fields': () =>
25
- selectRegisteredFields(
26
- NEW_TEMPLATE_SYNC_FORM_NAME,
27
- formStateFactory(registeredImportSettings)
28
- ),
29
12
  'should return initial form values': () =>
30
13
  selectInitialFormValues({
31
14
  ...stateFactory({ importSettings, exportSettings }),
32
- ...formStateFactory(initialValues),
33
15
  }),
34
16
  };
35
17
 
@@ -2,43 +2,14 @@
2
2
 
3
3
  exports[`NewTemplateSyncFormSelectors should return initial form values 1`] = `
4
4
  Object {
5
- "associate": "new",
6
- "filter": "",
7
- "force": false,
8
- "negate": false,
9
- "repo": "https://github.com/theforeman/community-templates.git",
10
- }
11
- `;
12
-
13
- exports[`NewTemplateSyncFormSelectors should return registered fields 1`] = `
14
- Object {
15
- "associate": Object {
16
- "description": "Associate templates to OS, organization and location",
17
- "id": 45,
18
- "name": "associate",
19
- "selection": Array [
20
- Object {
21
- "label": "New",
22
- "value": "new",
23
- },
24
- Object {
25
- "label": "Never",
26
- "value": "never",
27
- },
28
- Object {
29
- "label": "Always",
30
- "value": "always",
31
- },
32
- ],
33
- "settingsType": "string",
34
- "value": "new",
5
+ "export": Object {
6
+ "filter": "",
7
+ "negate": false,
8
+ "repo": "https://github.com/theforeman/community-templates.git",
35
9
  },
36
- "force": Object {
37
- "description": "Should importing overwrite locked templates?",
38
- "id": 46,
39
- "name": "force",
40
- "settingsType": "bool",
41
- "value": false,
10
+ "import": Object {
11
+ "associate": "new",
12
+ "force": false,
42
13
  },
43
14
  }
44
15
  `;
@@ -1,21 +1,15 @@
1
1
  import { connect } from 'react-redux';
2
- import { reduxForm } from 'redux-form';
3
2
 
4
3
  import * as FormActions from 'foremanReact/redux/actions/common/forms';
5
4
 
6
- import { selectLayout } from 'foremanReact/components/Layout/LayoutSelectors';
7
-
8
- import { NEW_TEMPLATE_SYNC_FORM_NAME } from './NewTemplateSyncFormConstants';
9
5
  import NewTemplateSyncForm from './NewTemplateSyncForm';
10
6
 
11
7
  import {
12
8
  selectImportSettings,
13
9
  selectExportSettings,
14
10
  } from '../../NewTemplateSyncSelectors';
15
- import {
16
- selectInitialFormValues,
17
- selectRegisteredFields,
18
- } from './NewTemplateSyncFormSelectors';
11
+
12
+ import { selectInitialFormValues } from './NewTemplateSyncFormSelectors';
19
13
 
20
14
  const mapStateToProps = (state, ownProps) => {
21
15
  const importSettings = selectImportSettings(state);
@@ -24,22 +18,11 @@ const mapStateToProps = (state, ownProps) => {
24
18
 
25
19
  const initialFormValues = selectInitialFormValues(state);
26
20
 
27
- const currentFields = selectRegisteredFields(
28
- NEW_TEMPLATE_SYNC_FORM_NAME,
29
- state
30
- );
31
-
32
21
  return {
33
22
  initialValues: { ...initialFormValues },
34
23
  importSettings,
35
24
  exportSettings,
36
- currentFields,
37
- currentOrganization: selectLayout(state).currentOrganization,
38
- currentLocation: selectLayout(state).currentLocation,
39
25
  };
40
26
  };
41
27
 
42
- const form = reduxForm({ form: NEW_TEMPLATE_SYNC_FORM_NAME })(
43
- NewTemplateSyncForm
44
- );
45
- export default connect(mapStateToProps, FormActions)(form);
28
+ export default connect(mapStateToProps, FormActions)(NewTemplateSyncForm);
@@ -5,7 +5,7 @@ import { FieldLevelHelp } from 'patternfly-react';
5
5
  import TextButtonField from './TextButtonField';
6
6
  import ButtonTooltip from './ButtonTooltip';
7
7
 
8
- const SyncSettingField = ({ setting, resetField, disabled }) => {
8
+ const SyncSettingField = ({ setting, resetField, disabled, syncType }) => {
9
9
  const label = settingObj => `${settingObj.fullName} `;
10
10
 
11
11
  const fieldSelector = settingObj => {
@@ -20,10 +20,6 @@ const SyncSettingField = ({ setting, resetField, disabled }) => {
20
20
  return 'text';
21
21
  };
22
22
 
23
- const handleReset = (settingName, settingValue) => {
24
- resetField(settingName, settingValue);
25
- };
26
-
27
23
  const tooltipContent = (
28
24
  <div
29
25
  dangerouslySetInnerHTML={{
@@ -34,18 +30,15 @@ const SyncSettingField = ({ setting, resetField, disabled }) => {
34
30
 
35
31
  return (
36
32
  <TextButtonField
37
- name={setting.name}
33
+ name={`${syncType}.${setting.name}`}
38
34
  label={label(setting)}
39
35
  blank={{}}
40
36
  item={setting}
41
- buttonAttrs={{
42
- buttonText: <ButtonTooltip tooltipId={setting.name} />,
43
- buttonAction: () => handleReset(setting.name, setting.value),
44
- }}
37
+ buttonText={<ButtonTooltip tooltipId={setting.name} />}
38
+ buttonAction={resetField(`${syncType}.${setting.name}`, setting.value)}
45
39
  fieldSelector={fieldSelector}
46
40
  disabled={disabled}
47
41
  fieldRequired={setting.required}
48
- validate={setting.validate}
49
42
  tooltipHelp={<FieldLevelHelp content={tooltipContent} />}
50
43
  >
51
44
  {setting.value}
@@ -57,6 +50,7 @@ SyncSettingField.propTypes = {
57
50
  setting: PropTypes.object.isRequired,
58
51
  resetField: PropTypes.func.isRequired,
59
52
  disabled: PropTypes.bool.isRequired,
53
+ syncType: PropTypes.string.isRequired,
60
54
  };
61
55
 
62
56
  export default SyncSettingField;
@@ -1,36 +1,20 @@
1
1
  import React from 'react';
2
- import { memoize, upperFirst } from 'lodash';
2
+ import { upperFirst } from 'lodash';
3
3
  import PropTypes from 'prop-types';
4
4
 
5
5
  import SyncSettingField from './SyncSettingField';
6
6
 
7
- const repoFormat = memoize(formatAry => value => {
8
- if (value) {
9
- const valid = formatAry
10
- .map(item => value.startsWith(item))
11
- .reduce((memo, item) => item || memo, false);
12
-
13
- if (valid) {
14
- return undefined;
15
- }
16
- }
17
-
18
- return `Invalid repo format, must start with one of: ${formatAry.join(', ')}`;
19
- });
20
-
21
7
  const SyncSettingsFields = ({
22
8
  importSettings,
23
9
  exportSettings,
24
10
  syncType,
25
11
  resetField,
26
- disabled,
27
- validationData,
12
+ formProps: { isSubmitting },
28
13
  }) => {
29
- const addValidationToSetting = (setting, validations) =>
14
+ const addRequiredToSetting = setting =>
30
15
  setting.name === 'repo'
31
16
  ? setting.merge({
32
17
  required: true,
33
- validate: [repoFormat(validations.repo)],
34
18
  })
35
19
  : setting;
36
20
 
@@ -58,14 +42,15 @@ const SyncSettingsFields = ({
58
42
  return (
59
43
  <React.Fragment>
60
44
  {settingsAry
61
- .map(setting => addValidationToSetting(setting, validationData))
45
+ .map(addRequiredToSetting)
62
46
  .map(setting => modifyDescription(setting, syncType))
63
47
  .map(setting => specializeDescription(setting, syncType))
64
48
  .map(setting => (
65
49
  <SyncSettingField
66
50
  setting={setting}
51
+ syncType={syncType}
67
52
  key={setting.name}
68
- disabled={disabled}
53
+ disabled={isSubmitting}
69
54
  resetField={resetField}
70
55
  />
71
56
  ))}
@@ -78,13 +63,11 @@ SyncSettingsFields.propTypes = {
78
63
  exportSettings: PropTypes.array.isRequired,
79
64
  syncType: PropTypes.string.isRequired,
80
65
  resetField: PropTypes.func.isRequired,
81
- disabled: PropTypes.bool,
82
- validationData: PropTypes.object,
66
+ formProps: PropTypes.object,
83
67
  };
84
68
 
85
69
  SyncSettingsFields.defaultProps = {
86
- disabled: false,
87
- validationData: {},
70
+ formProps: {},
88
71
  };
89
72
 
90
73
  export default SyncSettingsFields;
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- import { Field } from 'redux-form';
2
+ import { get } from 'lodash';
3
+ import { Field as FormikField } from 'formik';
3
4
  import PropTypes from 'prop-types';
4
5
 
5
6
  import RenderField from './RenderField';
@@ -11,25 +12,35 @@ const TextButtonField = ({
11
12
  className,
12
13
  inputClassName,
13
14
  blank,
14
- buttonAttrs,
15
+ buttonText,
16
+ buttonAction,
15
17
  fieldSelector,
16
- validate,
17
18
  disabled,
18
19
  fieldRequired,
19
20
  tooltipHelp,
20
21
  }) => (
21
- <Field
22
+ <FormikField
22
23
  name={name}
23
- label={label}
24
- fieldSelector={fieldSelector}
25
- tooltipHelp={tooltipHelp}
26
- component={RenderField}
27
- buttonAttrs={buttonAttrs}
28
- blank={blank}
29
- item={item}
30
- disabled={disabled}
31
- validate={item.validate}
32
- fieldRequired={fieldRequired}
24
+ render={({ field, form }) => (
25
+ <RenderField
26
+ label={label}
27
+ fieldSelector={fieldSelector}
28
+ tooltipHelp={tooltipHelp}
29
+ buttonAttrs={{
30
+ buttonText,
31
+ buttonAction: () => buttonAction(form.setFieldValue),
32
+ }}
33
+ blank={blank}
34
+ item={item}
35
+ disabled={disabled}
36
+ fieldRequired={fieldRequired}
37
+ meta={{
38
+ touched: get(form.touched, name),
39
+ error: get(form.errors, name),
40
+ }}
41
+ input={field}
42
+ />
43
+ )}
33
44
  />
34
45
  );
35
46
 
@@ -43,12 +54,9 @@ TextButtonField.propTypes = {
43
54
  label: PropTypes.string,
44
55
  value: PropTypes.string,
45
56
  }),
46
- buttonAttrs: PropTypes.shape({
47
- buttonText: PropTypes.node,
48
- buttonAction: PropTypes.func,
49
- }).isRequired,
57
+ buttonText: PropTypes.node.isRequired,
58
+ buttonAction: PropTypes.func.isRequired,
50
59
  fieldSelector: PropTypes.func,
51
- validate: PropTypes.array,
52
60
  disabled: PropTypes.bool,
53
61
  fieldRequired: PropTypes.bool,
54
62
  tooltipHelp: PropTypes.node,
@@ -58,7 +66,6 @@ TextButtonField.defaultProps = {
58
66
  blank: { label: 'Choose one...', value: '' },
59
67
  className: '',
60
68
  inputClassName: 'col-md-6',
61
- validate: [],
62
69
  disabled: false,
63
70
  fieldRequired: false,
64
71
  tooltipHelp: null,
@@ -8,11 +8,12 @@ import {
8
8
  filterSetting,
9
9
  } from '../../__fixtures__/templateSyncSettings.fixtures';
10
10
 
11
- const noop = () => {};
11
+ const noop = () => () => {};
12
12
 
13
13
  const commonFixtures = {
14
14
  resetField: noop,
15
15
  disabled: false,
16
+ syncType: 'import',
16
17
  };
17
18
 
18
19
  const fixtures = {
@@ -13,6 +13,7 @@ const commonFixtures = {
13
13
  importSettings,
14
14
  exportSettings,
15
15
  resetField: noop,
16
+ formProps: { isSubmitting: false },
16
17
  disabled: false,
17
18
  };
18
19
 
@@ -29,7 +29,7 @@ const fixtures = {
29
29
  name: 'Text field',
30
30
  item: textItem,
31
31
  fieldSelector,
32
- buttonAttrs,
32
+ ...buttonAttrs,
33
33
  disabled: false,
34
34
  label: 'Text',
35
35
  blank: {},
@@ -38,7 +38,7 @@ const fixtures = {
38
38
  name: 'Checkbox field',
39
39
  item: checkboxItem,
40
40
  fieldSelector,
41
- buttonAttrs,
41
+ ...buttonAttrs,
42
42
  disabled: false,
43
43
  label: 'Checkbox',
44
44
  blank: {},
@@ -47,7 +47,7 @@ const fixtures = {
47
47
  name: 'select field',
48
48
  item: selectItem,
49
49
  fieldSelector,
50
- buttonAttrs,
50
+ ...buttonAttrs,
51
51
  label: 'Select',
52
52
  disabled: false,
53
53
  blank,
@@ -55,7 +55,7 @@ const fixtures = {
55
55
  'should render text field without field selector': {
56
56
  name: 'no selector',
57
57
  label: 'Text field',
58
- buttonAttrs,
58
+ ...buttonAttrs,
59
59
  disabled: false,
60
60
  blank: {},
61
61
  },
@@ -3,13 +3,11 @@
3
3
  exports[`SyncSettingField should render boolean setting as checkbox 1`] = `
4
4
  <TextButtonField
5
5
  blank={Object {}}
6
- buttonAttrs={
7
- Object {
8
- "buttonAction": [Function],
9
- "buttonText": <ButtonTooltip
10
- tooltipId="force"
11
- />,
12
- }
6
+ buttonAction={[Function]}
7
+ buttonText={
8
+ <ButtonTooltip
9
+ tooltipId="force"
10
+ />
13
11
  }
14
12
  className=""
15
13
  disabled={false}
@@ -26,7 +24,7 @@ exports[`SyncSettingField should render boolean setting as checkbox 1`] = `
26
24
  }
27
25
  }
28
26
  label="undefined "
29
- name="force"
27
+ name="import.force"
30
28
  tooltipHelp={
31
29
  <FieldLevelHelp
32
30
  buttonClass=""
@@ -43,20 +41,17 @@ exports[`SyncSettingField should render boolean setting as checkbox 1`] = `
43
41
  rootClose={true}
44
42
  />
45
43
  }
46
- validate={Array []}
47
44
  />
48
45
  `;
49
46
 
50
47
  exports[`SyncSettingField should render setting with input field 1`] = `
51
48
  <TextButtonField
52
49
  blank={Object {}}
53
- buttonAttrs={
54
- Object {
55
- "buttonAction": [Function],
56
- "buttonText": <ButtonTooltip
57
- tooltipId="filter"
58
- />,
59
- }
50
+ buttonAction={[Function]}
51
+ buttonText={
52
+ <ButtonTooltip
53
+ tooltipId="filter"
54
+ />
60
55
  }
61
56
  className=""
62
57
  disabled={false}
@@ -73,7 +68,7 @@ exports[`SyncSettingField should render setting with input field 1`] = `
73
68
  }
74
69
  }
75
70
  label="undefined "
76
- name="filter"
71
+ name="import.filter"
77
72
  tooltipHelp={
78
73
  <FieldLevelHelp
79
74
  buttonClass=""
@@ -90,20 +85,17 @@ exports[`SyncSettingField should render setting with input field 1`] = `
90
85
  rootClose={true}
91
86
  />
92
87
  }
93
- validate={Array []}
94
88
  />
95
89
  `;
96
90
 
97
91
  exports[`SyncSettingField should render setting with select choices 1`] = `
98
92
  <TextButtonField
99
93
  blank={Object {}}
100
- buttonAttrs={
101
- Object {
102
- "buttonAction": [Function],
103
- "buttonText": <ButtonTooltip
104
- tooltipId="associate"
105
- />,
106
- }
94
+ buttonAction={[Function]}
95
+ buttonText={
96
+ <ButtonTooltip
97
+ tooltipId="associate"
98
+ />
107
99
  }
108
100
  className=""
109
101
  disabled={false}
@@ -134,7 +126,7 @@ exports[`SyncSettingField should render setting with select choices 1`] = `
134
126
  }
135
127
  }
136
128
  label="undefined "
137
- name="associate"
129
+ name="import.associate"
138
130
  tooltipHelp={
139
131
  <FieldLevelHelp
140
132
  buttonClass=""
@@ -151,7 +143,6 @@ exports[`SyncSettingField should render setting with select choices 1`] = `
151
143
  rootClose={true}
152
144
  />
153
145
  }
154
- validate={Array []}
155
146
  >
156
147
  new
157
148
  </TextButtonField>