foreman_templates 7.0.7 → 9.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) 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/controllers/ui_template_syncs_controller.rb +1 -1
  5. data/app/models/setting/template_sync.rb +12 -2
  6. data/app/services/foreman_templates/template_exporter.rb +3 -3
  7. data/app/services/foreman_templates/template_importer.rb +21 -5
  8. data/app/views/template_syncs/index.html.erb +14 -17
  9. data/app/views/ui_template_syncs/template_attrs.json.rabl +1 -1
  10. data/db/migrate/20180627134929_change_lock_setting.rb +5 -0
  11. data/lib/foreman_templates/engine.rb +11 -5
  12. data/lib/foreman_templates/version.rb +1 -1
  13. data/lib/tasks/foreman_templates_tasks.rake +10 -9
  14. data/package.json +15 -21
  15. data/webpack/ForemanTemplates.js +17 -13
  16. data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext.js +2 -0
  17. data/webpack/__mocks__/foremanReact/components/common/forms/ForemanForm.js +2 -0
  18. data/webpack/components/NewTemplateSync/__fixtures__/templateSyncSettings.fixtures.js +4 -4
  19. data/webpack/components/NewTemplateSync/__tests__/__snapshots__/NewTemplateSync.test.js.snap +2 -2
  20. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncForm.js +102 -132
  21. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncFormHelpers.js +43 -0
  22. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncFormSelectors.js +10 -13
  23. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/__tests__/NewTemplateSyncFormSelectors.test.js +1 -19
  24. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/__tests__/__snapshots__/NewTemplateSyncFormSelectors.test.js.snap +7 -36
  25. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/index.js +3 -20
  26. data/webpack/components/NewTemplateSync/components/SyncSettingField.js +5 -11
  27. data/webpack/components/NewTemplateSync/components/SyncSettingFields.js +8 -25
  28. data/webpack/components/NewTemplateSync/components/TextButtonField/index.js +27 -20
  29. data/webpack/components/NewTemplateSync/components/__tests__/SyncSettingField.test.js +2 -1
  30. data/webpack/components/NewTemplateSync/components/__tests__/SyncSettingFields.test.js +1 -0
  31. data/webpack/components/NewTemplateSync/components/__tests__/TextButtonField.test.js +4 -4
  32. data/webpack/components/NewTemplateSync/components/__tests__/__snapshots__/SyncSettingField.test.js.snap +18 -27
  33. data/webpack/components/NewTemplateSync/components/__tests__/__snapshots__/SyncSettingFields.test.js.snap +5 -3
  34. data/webpack/components/NewTemplateSync/components/__tests__/__snapshots__/TextButtonField.test.js.snap +8 -91
  35. data/webpack/components/TemplateSyncResult/__fixtures__/templateSyncResult.fixtures.js +2 -2
  36. data/webpack/components/TemplateSyncResult/__tests__/__snapshots__/TemplateSyncResult.test.js.snap +3 -1
  37. data/webpack/components/TemplateSyncResult/__tests__/__snapshots__/TemplateSyncResultReducer.test.js.snap +3 -1
  38. data/webpack/components/TemplateSyncResult/components/SyncResultList.js +2 -2
  39. data/webpack/components/TemplateSyncResult/components/SyncedTemplate/__snapshots__/helpers.test.js.snap +37 -0
  40. data/webpack/components/TemplateSyncResult/components/SyncedTemplate/helpers.js +21 -11
  41. data/webpack/components/TemplateSyncResult/components/SyncedTemplate/helpers.test.js +21 -0
  42. data/webpack/components/TemplateSyncResult/components/__tests__/__snapshots__/SyncResultList.test.js.snap +8 -6
  43. data/webpack/components/TemplateSyncResult/components/__tests__/__snapshots__/SyncedTemplate.test.js.snap +39 -15
  44. data/webpack/testSetup.js +2 -1
  45. metadata +9 -8
  46. data/webpack/__mocks__/foremanReact/components/common/forms/Form.js +0 -2
  47. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/NewTemplateSyncFormConstants.js +0 -1
  48. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/__tests__/NewTemplateSyncForm.test.js +0 -42
  49. data/webpack/components/NewTemplateSync/components/NewTemplateSyncForm/__tests__/__snapshots__/NewTemplateSyncForm.test.js.snap +0 -186
@@ -1,112 +1,29 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`TextButtonField should render checkbox item 1`] = `
4
- <Field
5
- blank={Object {}}
6
- buttonAttrs={
7
- Object {
8
- "buttonAction": [Function],
9
- "buttonText": "Button",
10
- }
11
- }
12
- component={[Function]}
13
- disabled={false}
14
- fieldRequired={false}
15
- fieldSelector={[Function]}
16
- item={
17
- Object {
18
- "type": "checkbox",
19
- }
20
- }
21
- label="Checkbox"
4
+ <FormikConnect(FieldInner)
22
5
  name="Checkbox field"
23
- tooltipHelp={null}
6
+ render={[Function]}
24
7
  />
25
8
  `;
26
9
 
27
10
  exports[`TextButtonField should render select item with custom blank opt 1`] = `
28
- <Field
29
- blank={
30
- Object {
31
- "label": "Bare Metal",
32
- "value": "bareMetal",
33
- }
34
- }
35
- buttonAttrs={
36
- Object {
37
- "buttonAction": [Function],
38
- "buttonText": "Button",
39
- }
40
- }
41
- component={[Function]}
42
- disabled={false}
43
- fieldRequired={false}
44
- fieldSelector={[Function]}
45
- item={
46
- Object {
47
- "selection": Array [
48
- Object {
49
- "label": "A",
50
- "value": "a",
51
- },
52
- Object {
53
- "label": "B",
54
- "value": "b",
55
- },
56
- Object {
57
- "label": "C",
58
- "value": "c",
59
- },
60
- ],
61
- "type": "select",
62
- }
63
- }
64
- label="Select"
11
+ <FormikConnect(FieldInner)
65
12
  name="select field"
66
- tooltipHelp={null}
13
+ render={[Function]}
67
14
  />
68
15
  `;
69
16
 
70
17
  exports[`TextButtonField should render text field without field selector 1`] = `
71
- <Field
72
- blank={Object {}}
73
- buttonAttrs={
74
- Object {
75
- "buttonAction": [Function],
76
- "buttonText": "Button",
77
- }
78
- }
79
- component={[Function]}
80
- disabled={false}
81
- fieldRequired={false}
82
- fieldSelector={[Function]}
83
- item={Object {}}
84
- label="Text field"
18
+ <FormikConnect(FieldInner)
85
19
  name="no selector"
86
- tooltipHelp={null}
20
+ render={[Function]}
87
21
  />
88
22
  `;
89
23
 
90
24
  exports[`TextButtonField should render text item 1`] = `
91
- <Field
92
- blank={Object {}}
93
- buttonAttrs={
94
- Object {
95
- "buttonAction": [Function],
96
- "buttonText": "Button",
97
- }
98
- }
99
- component={[Function]}
100
- disabled={false}
101
- fieldRequired={false}
102
- fieldSelector={[Function]}
103
- item={
104
- Object {
105
- "type": "text",
106
- }
107
- }
108
- label="Text"
25
+ <FormikConnect(FieldInner)
109
26
  name="Text field"
110
- tooltipHelp={null}
27
+ render={[Function]}
111
28
  />
112
29
  `;
@@ -24,7 +24,7 @@ export const exportTemplates = [
24
24
  export const noName = {
25
25
  name: null,
26
26
  templateFile: 'random_template.erb',
27
- additionalErrors: 'No "name" found in metadata',
27
+ additionalErrors: ['No "name" found in metadata'],
28
28
  errors: null,
29
29
  };
30
30
 
@@ -55,7 +55,7 @@ export const filteredOut = {
55
55
  name: 'CoreOS default',
56
56
  templateFile: 'coreos_default.erb',
57
57
  additionalErrors: null,
58
- additionalInfo: 'Skipping, this template was filtered out.',
58
+ additionalInfo: ['Skipping, this template was filtered out.'],
59
59
  errors: {},
60
60
  snippet: false,
61
61
  locked: false,
@@ -55,7 +55,9 @@ exports[`TemplateSyncResult should render import result 1`] = `
55
55
  templates={
56
56
  Array [
57
57
  Object {
58
- "additionalErrors": "No \\"name\\" found in metadata",
58
+ "additionalErrors": Array [
59
+ "No \\"name\\" found in metadata",
60
+ ],
59
61
  "errors": null,
60
62
  "name": null,
61
63
  "templateFile": "random_template.erb",
@@ -33,7 +33,9 @@ Object {
33
33
  "resultAction": "import",
34
34
  "templates": Array [
35
35
  Object {
36
- "additionalErrors": "No \\"name\\" found in metadata",
36
+ "additionalErrors": Array [
37
+ "No \\"name\\" found in metadata",
38
+ ],
37
39
  "errors": null,
38
40
  "name": null,
39
41
  "templateFile": "random_template.erb",
@@ -11,10 +11,10 @@ import ListViewHeader from './ListViewHeader';
11
11
  const SyncResultList = ({ pagination, pageChange, templates, editPaths }) => (
12
12
  <ListView>
13
13
  <ListViewHeader />
14
- {templatesPage(templates, pagination).map(template => (
14
+ {templatesPage(templates, pagination).map((template, idx) => (
15
15
  <SyncedTemplate
16
16
  template={template}
17
- key={template.name}
17
+ key={idx}
18
18
  editPath={editPaths[template.className]}
19
19
  />
20
20
  ))}
@@ -0,0 +1,37 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`expandableContent should return additional error 1`] = `
4
+ <ul>
5
+ <li>
6
+ These are additional errors
7
+ </li>
8
+ </ul>
9
+ `;
10
+
11
+ exports[`expandableContent should return additional info 1`] = `
12
+ <ul>
13
+ <li>
14
+ This is additional info
15
+ </li>
16
+ </ul>
17
+ `;
18
+
19
+ exports[`expandableContent should return attribute errors 1`] = `
20
+ <ul>
21
+ <li>
22
+ could not be processed
23
+ </li>
24
+ <li>
25
+ name: can't be blank
26
+ </li>
27
+ <li>
28
+ name: has invalid format
29
+ </li>
30
+ </ul>
31
+ `;
32
+
33
+ exports[`expandableContent should return no errors 1`] = `
34
+ <span>
35
+ There were no errors.
36
+ </span>
37
+ `;
@@ -7,8 +7,8 @@ import EmptyInfoItem from './EmptyInfoItem';
7
7
  import StringInfoItem from './StringInfoItem';
8
8
  import LinkInfoItem from './LinkInfoItem';
9
9
 
10
- export const itemIteratorId = (template, attr) =>
11
- `${template.templateFile}-${attr}`;
10
+ export const itemIteratorId = (template, ...rest) =>
11
+ `${template.templateFile}-${rest.join('-')}`;
12
12
 
13
13
  export const additionalInfo = (template, editPath) => {
14
14
  const infoAttrs = [
@@ -72,7 +72,12 @@ export const additionalInfo = (template, editPath) => {
72
72
  );
73
73
  case 'name':
74
74
  return (
75
- <LinkInfoItem template={template} editPath={editPath} attr={attr} />
75
+ <LinkInfoItem
76
+ template={template}
77
+ editPath={editPath}
78
+ attr={attr}
79
+ key={key}
80
+ />
76
81
  );
77
82
  default:
78
83
  return '';
@@ -91,11 +96,16 @@ export const itemLeftContentIcon = template => {
91
96
 
92
97
  export const expandableContent = template => {
93
98
  if (Object.keys(aggregatedMessages(template)).length !== 0) {
94
- const res = Object.keys(aggregatedMessages(template)).map(key => (
95
- <li key={itemIteratorId(template, key)}>
96
- {formatError(key, aggregatedMessages(template)[key])}
97
- </li>
98
- ));
99
+ const msgs = aggregatedMessages(template);
100
+
101
+ const res = Object.keys(msgs).map(key => {
102
+ const errorMsgs = aggregatedMessages(template)[key];
103
+ return errorMsgs.map((errValue, idx) => (
104
+ <li key={itemIteratorId(template, key, idx)}>
105
+ {formatError(key, errValue)}
106
+ </li>
107
+ ));
108
+ });
99
109
  return <ul>{res}</ul>;
100
110
  }
101
111
  return <span>There were no errors.</span>;
@@ -104,7 +114,7 @@ export const expandableContent = template => {
104
114
  const aggregatedErrors = template => {
105
115
  const err = { ...template.errors } || {};
106
116
  if (template.additionalErrors) {
107
- err.additional = template.additionalErrors;
117
+ err.additional = [template.additionalErrors];
108
118
  }
109
119
 
110
120
  return err;
@@ -113,14 +123,14 @@ const aggregatedErrors = template => {
113
123
  const aggregatedMessages = template => {
114
124
  const errors = aggregatedErrors(template);
115
125
  if (template.additionalInfo) {
116
- errors.info = template.additionalInfo;
126
+ errors.info = [template.additionalInfo];
117
127
  }
118
128
  return errors;
119
129
  };
120
130
 
121
131
  const formatError = (key, value) => {
122
132
  const omitKeys = ['base', 'additional', 'info'];
123
- if (omitKeys.reduce((memo, item) => memo || key === item, false)) {
133
+ if (omitKeys.includes(key)) {
124
134
  return value;
125
135
  }
126
136
 
@@ -0,0 +1,21 @@
1
+ import { testSelectorsSnapshotWithFixtures } from 'react-redux-test-utils';
2
+
3
+ import { expandableContent } from './helpers';
4
+
5
+ const fixtures = {
6
+ 'should return no errors': () => expandableContent({}),
7
+ 'should return additional info': () =>
8
+ expandableContent({ additionalInfo: 'This is additional info' }),
9
+ 'should return additional error': () =>
10
+ expandableContent({ additionalErrors: 'These are additional errors' }),
11
+ 'should return attribute errors': () =>
12
+ expandableContent({
13
+ errors: {
14
+ base: ['could not be processed'],
15
+ name: ["can't be blank", 'has invalid format'],
16
+ },
17
+ }),
18
+ };
19
+
20
+ describe('expandableContent', () =>
21
+ testSelectorsSnapshotWithFixtures(fixtures));
@@ -7,10 +7,12 @@ exports[`SyncResultList should render 1`] = `
7
7
  <ListViewHeader />
8
8
  <SyncedTemplate
9
9
  editPath=""
10
- key="null"
10
+ key="0"
11
11
  template={
12
12
  Object {
13
- "additionalErrors": "No \\"name\\" found in metadata",
13
+ "additionalErrors": Array [
14
+ "No \\"name\\" found in metadata",
15
+ ],
14
16
  "errors": null,
15
17
  "name": null,
16
18
  "templateFile": "random_template.erb",
@@ -19,7 +21,7 @@ exports[`SyncResultList should render 1`] = `
19
21
  />
20
22
  <SyncedTemplate
21
23
  editPath=""
22
- key="EPEL"
24
+ key="1"
23
25
  template={
24
26
  Object {
25
27
  "additionalErrors": null,
@@ -40,7 +42,7 @@ exports[`SyncResultList should render 1`] = `
40
42
  />
41
43
  <SyncedTemplate
42
44
  editPath=""
43
- key="CoreOS default"
45
+ key="2"
44
46
  template={
45
47
  Object {
46
48
  "additionalErrors": null,
@@ -56,7 +58,7 @@ exports[`SyncResultList should render 1`] = `
56
58
  />
57
59
  <SyncedTemplate
58
60
  editPath=""
59
- key="null"
61
+ key="3"
60
62
  template={
61
63
  Object {
62
64
  "additionalErrors": "No \\"model\\" found in metadata",
@@ -68,7 +70,7 @@ exports[`SyncResultList should render 1`] = `
68
70
  />
69
71
  <SyncedTemplate
70
72
  editPath=""
71
- key="Kickstart fake"
73
+ key="4"
72
74
  template={
73
75
  Object {
74
76
  "additionalErrors": null,
@@ -11,7 +11,9 @@ exports[`SyncedTemplate should render skipped template 1`] = `
11
11
  template={
12
12
  Object {
13
13
  "additionalErrors": null,
14
- "additionalInfo": "Skipping, this template was filtered out.",
14
+ "additionalInfo": Array [
15
+ "Skipping, this template was filtered out.",
16
+ ],
15
17
  "className": "Ptable",
16
18
  "errors": Object {},
17
19
  "humanizedClassName": "Ptable",
@@ -27,7 +29,9 @@ exports[`SyncedTemplate should render skipped template 1`] = `
27
29
  template={
28
30
  Object {
29
31
  "additionalErrors": null,
30
- "additionalInfo": "Skipping, this template was filtered out.",
32
+ "additionalInfo": Array [
33
+ "Skipping, this template was filtered out.",
34
+ ],
31
35
  "className": "Ptable",
32
36
  "errors": Object {},
33
37
  "humanizedClassName": "Ptable",
@@ -43,7 +47,9 @@ exports[`SyncedTemplate should render skipped template 1`] = `
43
47
  template={
44
48
  Object {
45
49
  "additionalErrors": null,
46
- "additionalInfo": "Skipping, this template was filtered out.",
50
+ "additionalInfo": Array [
51
+ "Skipping, this template was filtered out.",
52
+ ],
47
53
  "className": "Ptable",
48
54
  "errors": Object {},
49
55
  "humanizedClassName": "Ptable",
@@ -61,7 +67,9 @@ exports[`SyncedTemplate should render skipped template 1`] = `
61
67
  template={
62
68
  Object {
63
69
  "additionalErrors": null,
64
- "additionalInfo": "Skipping, this template was filtered out.",
70
+ "additionalInfo": Array [
71
+ "Skipping, this template was filtered out.",
72
+ ],
65
73
  "className": "Ptable",
66
74
  "errors": Object {},
67
75
  "humanizedClassName": "Ptable",
@@ -78,7 +86,9 @@ exports[`SyncedTemplate should render skipped template 1`] = `
78
86
  template={
79
87
  Object {
80
88
  "additionalErrors": null,
81
- "additionalInfo": "Skipping, this template was filtered out.",
89
+ "additionalInfo": Array [
90
+ "Skipping, this template was filtered out.",
91
+ ],
82
92
  "className": "Ptable",
83
93
  "errors": Object {},
84
94
  "humanizedClassName": "Ptable",
@@ -96,7 +106,9 @@ exports[`SyncedTemplate should render skipped template 1`] = `
96
106
  template={
97
107
  Object {
98
108
  "additionalErrors": null,
99
- "additionalInfo": "Skipping, this template was filtered out.",
109
+ "additionalInfo": Array [
110
+ "Skipping, this template was filtered out.",
111
+ ],
100
112
  "className": "Ptable",
101
113
  "errors": Object {},
102
114
  "humanizedClassName": "Ptable",
@@ -132,7 +144,7 @@ exports[`SyncedTemplate should render skipped template 1`] = `
132
144
  >
133
145
  <ul>
134
146
  <li
135
- key="coreos_default.erb-info"
147
+ key="coreos_default.erb-info-0"
136
148
  >
137
149
  Skipping, this template was filtered out.
138
150
  </li>
@@ -149,7 +161,9 @@ exports[`SyncedTemplate should render template with invalid metadata 1`] = `
149
161
  attr="name"
150
162
  template={
151
163
  Object {
152
- "additionalErrors": "No \\"name\\" found in metadata",
164
+ "additionalErrors": Array [
165
+ "No \\"name\\" found in metadata",
166
+ ],
153
167
  "errors": null,
154
168
  "name": null,
155
169
  "templateFile": "random_template.erb",
@@ -160,7 +174,9 @@ exports[`SyncedTemplate should render template with invalid metadata 1`] = `
160
174
  attr="locked"
161
175
  template={
162
176
  Object {
163
- "additionalErrors": "No \\"name\\" found in metadata",
177
+ "additionalErrors": Array [
178
+ "No \\"name\\" found in metadata",
179
+ ],
164
180
  "errors": null,
165
181
  "name": null,
166
182
  "templateFile": "random_template.erb",
@@ -171,7 +187,9 @@ exports[`SyncedTemplate should render template with invalid metadata 1`] = `
171
187
  attr="snippet"
172
188
  template={
173
189
  Object {
174
- "additionalErrors": "No \\"name\\" found in metadata",
190
+ "additionalErrors": Array [
191
+ "No \\"name\\" found in metadata",
192
+ ],
175
193
  "errors": null,
176
194
  "name": null,
177
195
  "templateFile": "random_template.erb",
@@ -182,7 +200,9 @@ exports[`SyncedTemplate should render template with invalid metadata 1`] = `
182
200
  attr="humanizedClassName"
183
201
  template={
184
202
  Object {
185
- "additionalErrors": "No \\"name\\" found in metadata",
203
+ "additionalErrors": Array [
204
+ "No \\"name\\" found in metadata",
205
+ ],
186
206
  "errors": null,
187
207
  "name": null,
188
208
  "templateFile": "random_template.erb",
@@ -193,7 +213,9 @@ exports[`SyncedTemplate should render template with invalid metadata 1`] = `
193
213
  attr="kind"
194
214
  template={
195
215
  Object {
196
- "additionalErrors": "No \\"name\\" found in metadata",
216
+ "additionalErrors": Array [
217
+ "No \\"name\\" found in metadata",
218
+ ],
197
219
  "errors": null,
198
220
  "name": null,
199
221
  "templateFile": "random_template.erb",
@@ -206,7 +228,9 @@ exports[`SyncedTemplate should render template with invalid metadata 1`] = `
206
228
  mapAttr={[Function]}
207
229
  template={
208
230
  Object {
209
- "additionalErrors": "No \\"name\\" found in metadata",
231
+ "additionalErrors": Array [
232
+ "No \\"name\\" found in metadata",
233
+ ],
210
234
  "errors": null,
211
235
  "name": null,
212
236
  "templateFile": "random_template.erb",
@@ -238,7 +262,7 @@ exports[`SyncedTemplate should render template with invalid metadata 1`] = `
238
262
  >
239
263
  <ul>
240
264
  <li
241
- key="random_template.erb-additional"
265
+ key="random_template.erb-additional-0"
242
266
  >
243
267
  No "name" found in metadata
244
268
  </li>
@@ -409,7 +433,7 @@ exports[`SyncedTemplate should render template with validation errors 1`] = `
409
433
  >
410
434
  <ul>
411
435
  <li
412
- key="epel.erb-base"
436
+ key="epel.erb-base-0"
413
437
  >
414
438
  This template is locked
415
439
  </li>