foreman_acd 0.0.1

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 (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +87 -0
  4. data/Rakefile +49 -0
  5. data/app/assets/javascripts/foreman_acd/acd_dummy.js +3 -0
  6. data/app/controllers/foreman_acd/api/v2/app_definitions_controller.rb +55 -0
  7. data/app/controllers/foreman_acd/api/v2/base_controller.rb +16 -0
  8. data/app/controllers/foreman_acd/app_definitions_controller.rb +53 -0
  9. data/app/controllers/foreman_acd/app_instances_controller.rb +158 -0
  10. data/app/controllers/foreman_acd/application_controller.rb +10 -0
  11. data/app/controllers/foreman_acd/concerns/app_definition_parameters.rb +23 -0
  12. data/app/controllers/foreman_acd/concerns/app_instance_parameters.rb +23 -0
  13. data/app/controllers/ui_acd_controller.rb +30 -0
  14. data/app/models/foreman_acd/app_definition.rb +23 -0
  15. data/app/models/foreman_acd/app_instance.rb +21 -0
  16. data/app/views/foreman_acd/app_definitions/_form.html.erb +40 -0
  17. data/app/views/foreman_acd/app_definitions/edit.html.erb +11 -0
  18. data/app/views/foreman_acd/app_definitions/index.html.erb +27 -0
  19. data/app/views/foreman_acd/app_definitions/new.html.erb +10 -0
  20. data/app/views/foreman_acd/app_instances/_form.html.erb +46 -0
  21. data/app/views/foreman_acd/app_instances/edit.html.erb +11 -0
  22. data/app/views/foreman_acd/app_instances/index.html.erb +30 -0
  23. data/app/views/foreman_acd/app_instances/new.html.erb +11 -0
  24. data/app/views/ui_acd/app.json.rabl +9 -0
  25. data/app/views/ui_acd/app_definition.json.rabl +5 -0
  26. data/app/views/ui_acd/computeprofile.json.rabl +4 -0
  27. data/app/views/ui_acd/domain.json.rabl +4 -0
  28. data/app/views/ui_acd/environment.json.rabl +4 -0
  29. data/app/views/ui_acd/fdata.json.rabl +24 -0
  30. data/app/views/ui_acd/lifecycle_environment.json.rabl +4 -0
  31. data/app/views/ui_acd/ptable.json.rabl +4 -0
  32. data/config/routes.rb +24 -0
  33. data/db/migrate/20190610202252_create_app_definitions.rb +19 -0
  34. data/db/migrate/20190625140305_create_app_instances.rb +19 -0
  35. data/lib/foreman_acd/engine.rb +43 -0
  36. data/lib/foreman_acd/plugin.rb +80 -0
  37. data/lib/foreman_acd/version.rb +5 -0
  38. data/lib/foreman_acd.rb +7 -0
  39. data/lib/tasks/foreman_acd_tasks.rake +47 -0
  40. data/locale/Makefile +60 -0
  41. data/locale/en/foreman_acd.po +19 -0
  42. data/locale/foreman_acd.pot +19 -0
  43. data/locale/gemspec.rb +2 -0
  44. data/package.json +121 -0
  45. data/test/controllers/app_definitions_controller_test.rb +24 -0
  46. data/test/controllers/app_instances_controller_test.rb +24 -0
  47. data/test/controllers/ui_acd_controller_test.rb +26 -0
  48. data/test/factories/foreman_acd_factories.rb +17 -0
  49. data/test/models/app_definition_test.rb +11 -0
  50. data/test/models/app_instance_test.rb +8 -0
  51. data/test/test_plugin_helper.rb +8 -0
  52. data/webpack/__mocks__/foremanReact/components/common/forms/Select.js +2 -0
  53. data/webpack/components/ParameterSelection/ParameterSelection.js +468 -0
  54. data/webpack/components/ParameterSelection/ParameterSelection.scss +3 -0
  55. data/webpack/components/ParameterSelection/ParameterSelectionActions.js +294 -0
  56. data/webpack/components/ParameterSelection/ParameterSelectionConstants.js +31 -0
  57. data/webpack/components/ParameterSelection/ParameterSelectionHelper.js +52 -0
  58. data/webpack/components/ParameterSelection/ParameterSelectionReducer.js +175 -0
  59. data/webpack/components/ParameterSelection/ParameterSelectionSelectors.js +15 -0
  60. data/webpack/components/ParameterSelection/__fixtures__/parameterSelection.fixtures.js +162 -0
  61. data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionData_1.fixtures.js +194 -0
  62. data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionReducer.fixtures.js +127 -0
  63. data/webpack/components/ParameterSelection/__tests__/ParameterSelection.test.js +48 -0
  64. data/webpack/components/ParameterSelection/__tests__/ParameterSelectionReducer.test.js +150 -0
  65. data/webpack/components/ParameterSelection/__tests__/ParameterSelectionSelectors.test.js +47 -0
  66. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelection.test.js.snap +454 -0
  67. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionReducer.test.js.snap +2265 -0
  68. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionSelectors.test.js.snap +209 -0
  69. data/webpack/components/ParameterSelection/index.js +39 -0
  70. data/webpack/index.js +8 -0
  71. data/webpack/reducer.js +7 -0
  72. data/webpack/test_setup.js +11 -0
  73. metadata +206 -0
@@ -0,0 +1,24 @@
1
+ require 'test_plugin_helper'
2
+ require 'nokogiri'
3
+
4
+ module ForemanAcd
5
+ class AppDefinitionsControllerTest < ActionController::TestCase
6
+ setup do
7
+ as_admin { FactoryBot.create(:app_definition) }
8
+ @model = ForemanAcd::AppDefinition.first
9
+ end
10
+
11
+ basic_index_test('app_definitions')
12
+ basic_new_test
13
+ basic_edit_test('app_definition')
14
+
15
+ test 'should destroy app definition' do
16
+ assert_difference('AppDefinition.count', -1) do
17
+ delete :destroy,
18
+ :params => { :id => @model.id },
19
+ :session => set_session_user
20
+ end
21
+ assert_redirected_to app_definitions_url
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'test_plugin_helper'
2
+ require 'nokogiri'
3
+
4
+ module ForemanAcd
5
+ class AppInstancesControllerTest < ActionController::TestCase
6
+ setup do
7
+ as_admin { FactoryBot.create(:app_instance) }
8
+ @model = ForemanAcd::AppInstance.first
9
+ end
10
+
11
+ basic_index_test('app_instances')
12
+ basic_new_test
13
+ basic_edit_test('app_instance')
14
+
15
+ test 'should destroy app instance' do
16
+ assert_difference('AppInstance.count', -1) do
17
+ delete :destroy,
18
+ :params => { :id => @model.id },
19
+ :session => set_session_user
20
+ end
21
+ assert_redirected_to app_instances_url
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ require 'test_plugin_helper'
2
+ require 'nokogiri'
3
+
4
+ class UiAcdControllerTest < ActionController::TestCase
5
+ test 'get app json' do
6
+ app_def = FactoryBot.create(:app_definition)
7
+ get :app, params: { :id => app_def.id }, session: set_session_user
8
+ assert_response :success
9
+
10
+ assert_equal app_def.name, json_response['app_definition']['name']
11
+ assert_equal app_def.hostgroup_id, json_response['fdata']['hostgroup_id']
12
+ assert_equal app_def.hostgroup.domain.name, json_response['fdata']['domains'][0]['name']
13
+ assert_equal app_def.hostgroup.environment.name, json_response['fdata']['environments'][0]['name']
14
+ assert_equal app_def.hostgroup.ptable.name, json_response['fdata']['ptables'][0]['name']
15
+ end
16
+
17
+ test 'get fdata json' do
18
+ hostgroup = FactoryBot.create(:hostgroup, :with_domain, :with_os, :with_environment)
19
+ get :fdata, params: { :id => hostgroup.id }, session: set_session_user
20
+ assert_response :success
21
+
22
+ assert_equal hostgroup.environment.name, json_response['environments'][0]['name']
23
+ assert_equal hostgroup.domain.name, json_response['domains'][0]['name']
24
+ assert_equal hostgroup.ptable.name, json_response['ptables'][0]['name']
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :app_definition, :class => 'ForemanAcd::AppDefinition' do
5
+ sequence(:name) { |n| "app_definition#{n}" }
6
+ sequence(:description) {|n| "description#{n}" }
7
+ hostgroup { FactoryBot.create(:hostgroup, :with_domain, :with_os, :with_environment ) }
8
+ parameters { "[{\"id\":0,\"name\":\"Hostname\",\"description\":\"\",\"type\":\"hostname\",\"value\":\"mysqllinux\"},{\"id\":1,\"name\":\"RootPW\",\"description\":\"\",\"type\":\"password\",\"value\":\"sesam12345\"},{\"id\":2,\"name\":\"ansible_root\",\"description\":\"\",\"type\":\"hostparam\",\"value\":\"/root/ansible\"}]" }
9
+ end
10
+
11
+ factory :app_instance, :class => 'ForemanAcd::AppInstance' do
12
+ sequence(:name) { |n| "app_instance#{n}" }
13
+ app_definition { FactoryBot.create(:app_definition) }
14
+ sequence(:description) {|n| "description#{n}" }
15
+ parameters { "[{\"id\":0,\"name\":\"Hostname\",\"description\":\"\",\"type\":\"hostname\",\"value\":\"mysqllinux\"},{\"id\":1,\"name\":\"RootPW\",\"description\":\"\",\"type\":\"password\",\"value\":\"sesam12345\"},{\"id\":2,\"name\":\"ansible_root\",\"description\":\"\",\"type\":\"hostparam\",\"value\":\"/root/ansible\"}]" }
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module ForemanAcd
4
+ class AppDefinitionTest < ActiveSupport::TestCase
5
+ should validate_presence_of(:name)
6
+ should validate_uniqueness_of(:name)
7
+ should belong_to(:hostgroup)
8
+ should have_many(:app_instances).dependent(:destroy)
9
+
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module ForemanAcd
4
+ class AppInstanceTest < ActiveSupport::TestCase
5
+ should validate_presence_of(:name)
6
+ should belong_to(:app_definition)
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This calls the main test_helper in Foreman-core
4
+ require 'test_helper'
5
+
6
+ # Add plugin to FactoryBot's paths
7
+ FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
8
+ FactoryBot.reload
@@ -0,0 +1,2 @@
1
+ const Select = () => jest.fn();
2
+ export default Select;
@@ -0,0 +1,468 @@
1
+ import $ from 'jquery';
2
+ import React from 'react';
3
+ import PropTypes from 'prop-types';
4
+ import * as sort from 'sortabular';
5
+ import { orderBy } from 'lodash';
6
+ import * as resolve from 'table-resolver';
7
+ import Select from 'foremanReact/components/common/forms/Select';
8
+
9
+ import {
10
+ isNewDefinition,
11
+ isEditDefinition,
12
+ isDefinition,
13
+ isNewInstance,
14
+ isEditInstance,
15
+ isInstance,
16
+ transformForemanData,
17
+ } from './ParameterSelectionHelper';
18
+
19
+ import {
20
+ dropRight,
21
+ findIndex,
22
+ cloneDeep,
23
+ } from 'lodash';
24
+
25
+
26
+ import {
27
+ PARAMETER_TYPES,
28
+ } from './ParameterSelectionConstants';
29
+
30
+ import {
31
+ Icon,
32
+ Button,
33
+ Table,
34
+ FormControl,
35
+ defaultSortingOrder,
36
+ customHeaderFormattersDefinition,
37
+ inlineEditFormatterFactory,
38
+ } from 'patternfly-react';
39
+
40
+ const theme = {
41
+ scheme: 'foreman',
42
+ backgroundColor: 'rgba(0, 0, 0, 255)',
43
+ base00: 'rgba(0, 0, 0, 0)',
44
+ };
45
+
46
+
47
+ class ParameterSelection extends React.Component {
48
+
49
+ constructor(props) {
50
+ super(props);
51
+ }
52
+
53
+ renderAddButton(mode, addParameter) {
54
+ if (isInstance(mode))
55
+ return null;
56
+
57
+ return (
58
+ <Button bsStyle="default" disabled={ this.props.editMode || this.props.hostgroupId <= 0 } onClick={() => addParameter()}>
59
+ <Icon type="fa" name="plus" />
60
+ </Button>
61
+ );
62
+ }
63
+
64
+ renderDeleteButton(mode, deleteParameter, additionalData, disabled=false) {
65
+ if (isInstance(mode))
66
+ return null;
67
+
68
+ return (
69
+ <span>
70
+ &nbsp;
71
+ <Button
72
+ bsStyle="default"
73
+ disabled={disabled}
74
+ onClick={() => window.confirm("Are you sure you wish to delete this item?") && deleteParameter(additionalData) }
75
+ >
76
+ <Icon type="pf" name="delete" />
77
+ </Button>
78
+ </span>
79
+ );
80
+ }
81
+
82
+ renderSelectApplication(applications, url, loadParameterSelection, appDefinition) {
83
+ return (
84
+ <Select
85
+ value={appDefinition.id.toString()}
86
+ onChange={e => loadParameterSelection(url, e.target.value) }
87
+ options={applications}
88
+ allowClear
89
+ key="key"
90
+ />
91
+ );
92
+ }
93
+
94
+ renderSelectHostgroup(hostgroups, url, loadForemanData, hostgroupId) {
95
+ return (
96
+ <Select
97
+ value={hostgroupId.toString()}
98
+ onChange={e => loadForemanData(url, e.target.value, true) }
99
+ options={hostgroups}
100
+ allowClear
101
+ key="key"
102
+ />
103
+ );
104
+ }
105
+
106
+ renderRailsInputHidden(view, parameter, value) {
107
+ var id = "foreman_acd_"+ view +"_"+ parameter;
108
+ var name = "foreman_acd_"+ view +"["+ parameter +"]";
109
+
110
+ return (
111
+ <input
112
+ id={id}
113
+ name={name}
114
+ value={value}
115
+ type="hidden"
116
+ />
117
+ );
118
+ }
119
+
120
+ renderShowDivText(text) {
121
+ return (
122
+ <div>{text}</div>
123
+ );
124
+ }
125
+
126
+ isEditing({rowData}) {
127
+ return (rowData.backup !== undefined);
128
+ }
129
+
130
+ // enables our custom header formatters extensions to reactabular
131
+ customHeaderFormatters = customHeaderFormattersDefinition;
132
+
133
+ validateRows() {
134
+ var result = (this.props.rows.map(e => e.value).filter(i => i == "").length == 0);
135
+ if (result === false) {
136
+ window.alert("All parameters need to have a value!");
137
+ }
138
+ return result;
139
+ }
140
+
141
+ componentDidMount() {
142
+ const {
143
+ data: { mode, appDefinition, location, organization, loadForemanDataUrl, parameters },
144
+ initParameterSelection,
145
+ sortParameter,
146
+ deleteParameter,
147
+ activateEditParameter,
148
+ changeEditParameter,
149
+ loadForemanData,
150
+ } = this.props;
151
+
152
+ if (isEditDefinition(mode) || isEditInstance(mode)) {
153
+ loadForemanData(loadForemanDataUrl, appDefinition.hostgroup_id);
154
+ }
155
+
156
+ if (isInstance(mode)) {
157
+ $('input[type="submit"][name="commit"]').on('click', () => this.validateRows());
158
+ }
159
+
160
+ const inlineEditButtonsFormatter = inlineEditFormatterFactory({
161
+ isEditing: additionalData => this.props.editMode,
162
+ renderValue: (value, additionalData) => (
163
+ <td style={{ padding: '2px' }}>
164
+ <Button
165
+ bsStyle="default"
166
+ onClick={() => activateEditParameter(additionalData)}
167
+ >
168
+ <Icon type="pf" name="edit" />
169
+ </Button>
170
+ {this.renderDeleteButton(mode, deleteParameter, additionalData)}
171
+ </td>
172
+ ),
173
+ renderEdit: (value, additionalData) => (
174
+ <td style={{ padding: '2px' }}>
175
+ <Button bsStyle="default" disabled>
176
+ <Icon type="pf" name="edit" />
177
+ </Button>
178
+ {this.renderDeleteButton(mode, deleteParameter, additionalData, true)}
179
+ </td>
180
+ )
181
+ });
182
+ this.inlineEditButtonsFormatter = inlineEditButtonsFormatter;
183
+
184
+ const getSortingColumns = () => this.props.sortingColumns || {};
185
+
186
+ const sortableTransform = sort.sort({
187
+ getSortingColumns,
188
+ onSort: (selectedColumn, defaultSortingOrder) => sortParameter(selectedColumn, defaultSortingOrder),
189
+ strategy: sort.strategies.byProperty,
190
+ });
191
+ this.sortableTransform = sortableTransform;
192
+
193
+ const sortingFormatter = sort.header({
194
+ sortableTransform,
195
+ getSortingColumns,
196
+ strategy: sort.strategies.byProperty,
197
+ });
198
+ this.sortingFormatter = sortingFormatter;
199
+
200
+ const inlineEditFormatterImpl = {
201
+ renderValue: (value, additionalData) => (
202
+ <td>
203
+ <span className="static">{value}</span>
204
+ </td>
205
+ ),
206
+ renderEditText: (value, additionalData, subtype='text') => (
207
+ <td className="editing">
208
+ <FormControl
209
+ type={subtype}
210
+ defaultValue={value}
211
+ onBlur={e => changeEditParameter(e.target.value, additionalData) }
212
+ />
213
+ </td>
214
+ ),
215
+ renderEditSelect: (value, additionalData, options) => (
216
+ <td className="editing">
217
+ <Select
218
+ value={value.toString()}
219
+ onChange={e => changeEditParameter(e.target.value, additionalData) }
220
+ options={options}
221
+ allowClear
222
+ key="key"
223
+ />
224
+ </td>
225
+ ),
226
+ };
227
+
228
+ const inlineEditFormatter = inlineEditFormatterFactory({
229
+ isEditing: additionalData => this.isEditing(additionalData),
230
+ renderValue: (value, additionalData) => {
231
+ var prettyValue = value;
232
+ if (additionalData.property == 'type') {
233
+ prettyValue = PARAMETER_TYPES[value];
234
+ } else if (additionalData.property == 'value') {
235
+ switch (additionalData.rowData.type) {
236
+ case 'computeprofile':
237
+ prettyValue = transformForemanData(this.props.foremanData['computeprofiles'])[value]
238
+ break;
239
+ case 'domain':
240
+ prettyValue = transformForemanData(this.props.foremanData['domains'])[value]
241
+ break;
242
+ case 'lifecycleenv':
243
+ prettyValue = transformForemanData(this.props.foremanData['lifecycle_environments'])[value]
244
+ break;
245
+ case 'ptable':
246
+ prettyValue = transformForemanData(this.props.foremanData['ptables'])[value]
247
+ break;
248
+ case 'puppetenv':
249
+ prettyValue = transformForemanData(this.props.foremanData['environments'])[value]
250
+ break;
251
+ }
252
+ }
253
+ return inlineEditFormatterImpl.renderValue(prettyValue, additionalData)
254
+ },
255
+ renderEdit: (value, additionalData) => {
256
+ switch (additionalData.property) {
257
+ case 'type':
258
+ if (additionalData.rowData.newEntry === true) {
259
+ return inlineEditFormatterImpl.renderEditSelect(value, additionalData, this.props.parameterTypes);
260
+ }
261
+ return inlineEditFormatterImpl.renderValue(PARAMETER_TYPES[value], additionalData)
262
+ case 'value':
263
+ switch (additionalData.rowData.type) {
264
+ case 'computeprofile':
265
+ return inlineEditFormatterImpl.renderEditSelect(value, additionalData, transformForemanData(this.props.foremanData['computeprofiles']));
266
+ case 'domain':
267
+ return inlineEditFormatterImpl.renderEditSelect(value, additionalData, transformForemanData(this.props.foremanData['domains']));
268
+ case 'lifecycleenv':
269
+ return inlineEditFormatterImpl.renderEditSelect(value, additionalData, transformForemanData(this.props.foremanData['lifecycle_environments']));
270
+ case 'puppetenv':
271
+ return inlineEditFormatterImpl.renderEditSelect(value, additionalData, transformForemanData(this.props.foremanData['environments']));
272
+ case 'ptable':
273
+ return inlineEditFormatterImpl.renderEditSelect(value, additionalData, transformForemanData(this.props.foremanData['ptables']));
274
+ case 'text':
275
+ default:
276
+ return inlineEditFormatterImpl.renderEditText(value, additionalData);
277
+ }
278
+ default:
279
+ return inlineEditFormatterImpl.renderEditText(value, additionalData)
280
+ }
281
+ }
282
+ });
283
+ this.inlineEditFormatter = inlineEditFormatter;
284
+
285
+ initParameterSelection(
286
+ mode,
287
+ appDefinition,
288
+ parameters,
289
+ this.sortingFormatter,
290
+ this.sortableTransform,
291
+ this.inlineEditFormatter,
292
+ this.inlineEditButtonsFormatter,
293
+ );
294
+ }
295
+
296
+ // workaround because Recompose JS doesn't work -> webpack issues
297
+ compose = (...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg)
298
+
299
+ render() {
300
+ const {
301
+ data: { mode, applications, hostgroups, loadParameterSelectionUrl, loadForemanDataUrl },
302
+ rows,
303
+ columns,
304
+ sortingColumns,
305
+ loading,
306
+ addParameter,
307
+ confirmEditParameter,
308
+ cancelEditParameter,
309
+ loadParameterSelection,
310
+ loadForemanData,
311
+ appDefinition,
312
+ hostgroupId,
313
+ } = this.props;
314
+
315
+ var sortedRows;
316
+ var newEntryIndex = findIndex(rows, 'newEntry');
317
+
318
+ if (newEntryIndex >= 0) {
319
+ let newEntry = rows[newEntryIndex];
320
+ // sort all elements, besides the newEntry which will be
321
+ // added to the end of the Array
322
+ var tmpRows = cloneDeep(rows);
323
+ tmpRows.splice(newEntryIndex, 1);
324
+ sortedRows = this.compose(
325
+ sort.sorter({
326
+ columns,
327
+ sortingColumns,
328
+ sort: orderBy,
329
+ strategy: sort.strategies.byProperty
330
+ })
331
+ )(tmpRows);
332
+ sortedRows.push(newEntry);
333
+ } else {
334
+ sortedRows = this.compose(
335
+ sort.sorter({
336
+ columns,
337
+ sortingColumns,
338
+ sort: orderBy,
339
+ strategy: sort.strategies.byProperty
340
+ })
341
+ )(rows);
342
+ }
343
+
344
+ return(
345
+ <div>
346
+ {isDefinition(mode) ? (
347
+ <div className="clearfix">
348
+ <div className="form-group">
349
+ <label className="col-md-2 control-label">Host Group</label>
350
+ <div className="col-md-4">
351
+ {isNewDefinition(mode) && this.renderSelectHostgroup(hostgroups, loadForemanDataUrl, loadForemanData, hostgroupId) }
352
+ {isEditDefinition(mode) && this.renderShowDivText(hostgroups[hostgroupId]) }
353
+ {this.renderRailsInputHidden('app_definition', 'hostgroup_id', hostgroupId) }
354
+ </div>
355
+ </div>
356
+ </div>
357
+ ) : (
358
+ <div className="clearfix">
359
+ <div className="form-group">
360
+ <label className="col-md-2 control-label">Application Definition</label>
361
+ <div className="col-md-4">
362
+ {isNewInstance(mode) && this.renderSelectApplication(applications, loadParameterSelectionUrl, loadParameterSelection, appDefinition)}
363
+ {isEditInstance(mode) && this.renderShowDivText(appDefinition.name)}
364
+ {isInstance(mode) && this.renderRailsInputHidden('app_instance', 'app_definition_id', appDefinition.id)}
365
+ </div>
366
+ </div>
367
+ </div>
368
+ )}
369
+ <div className="clearfix">
370
+ <div className="form-group">
371
+ <label className="col-md-1 control-label">Application parameters</label>
372
+ <div className="col-md-5">&nbsp;</div>
373
+ </div>
374
+ </div>
375
+
376
+ <div className="clearfix">
377
+ <div className="form-group">
378
+ {this.renderAddButton(mode, addParameter)}
379
+ <Table.PfProvider
380
+ striped
381
+ bordered
382
+ hover
383
+ dataTable
384
+ inlineEdit
385
+ columns={columns}
386
+ components={{
387
+ header: {
388
+ cell: cellProps =>
389
+ this.customHeaderFormatters({
390
+ cellProps,
391
+ columns,
392
+ sortingColumns
393
+ })
394
+ },
395
+ body: {
396
+ row: Table.InlineEditRow,
397
+ cell: cellProps => cellProps.children
398
+ }
399
+ }}
400
+ >
401
+ <Table.Header headerRows={resolve.headerRows({ columns })} />
402
+ <Table.Body
403
+ rows={sortedRows}
404
+ rowKey="id"
405
+ onRow={(rowData, { rowIndex }) => ({
406
+ role: 'row',
407
+ isEditing: () => this.isEditing({ rowData }),
408
+ onCancel: () => cancelEditParameter({ rowData, rowIndex }),
409
+ onConfirm: () => confirmEditParameter({ rowData, rowIndex }),
410
+ last: rowIndex === sortedRows.length - 1
411
+ })}
412
+ />
413
+ </Table.PfProvider>
414
+ {this.renderAddButton(mode, addParameter)}
415
+ </div>
416
+ {this.renderRailsInputHidden(isDefinition(mode) ? 'app_definition' : 'app_instance', 'parameters', JSON.stringify(this.props.rows))}
417
+ </div>
418
+ </div>
419
+ );
420
+ }
421
+ }
422
+
423
+ ParameterSelection.defaultProps = {
424
+ error: {},
425
+ editMode: false,
426
+ loading: false,
427
+ foremanData: {},
428
+ rows: [],
429
+ columns: [],
430
+ sortingColumns: {},
431
+ appDefinition: { "id": '', "name": '', "hostgroup_id": '', "parameters": [] },
432
+ hostgroupId: -1,
433
+ };
434
+
435
+ ParameterSelection.propTypes = {
436
+ data: PropTypes.shape({
437
+ mode: PropTypes.string.isRequired,
438
+ location: PropTypes.string.isRequired,
439
+ organization: PropTypes.string.isRequired,
440
+ parameters: PropTypes.array,
441
+ appDefinition: PropTypes.object,
442
+ applications: PropTypes.object,
443
+ hostgroups: PropTypes.object,
444
+ loadParameterSelectionUrl: PropTypes.string,
445
+ loadForemanDataUrl: PropTypes.string,
446
+ }).isRequired,
447
+ initParameterSelection: PropTypes.func,
448
+ editMode: PropTypes.bool.isRequired,
449
+ loading: PropTypes.bool.isRequired,
450
+ foremanData: PropTypes.object.isRequired,
451
+ parameterTypes: PropTypes.object,
452
+ rows: PropTypes.array,
453
+ sortingColumns: PropTypes.object,
454
+ columns: PropTypes.array,
455
+ sortParameter: PropTypes.func,
456
+ addParameter: PropTypes.func,
457
+ deleteParameter: PropTypes.func,
458
+ activateEditParameter: PropTypes.func,
459
+ confirmEditParameter: PropTypes.func,
460
+ cancelEditParameter: PropTypes.func,
461
+ changeEditParameter: PropTypes.func,
462
+ loadParameterSelection: PropTypes.func,
463
+ loadForemanData: PropTypes.func,
464
+ appDefinition: PropTypes.object,
465
+ hostgroupId: PropTypes.number,
466
+ };
467
+
468
+ export default ParameterSelection;
@@ -0,0 +1,3 @@
1
+ @import '~patternfly/dist/sass/patternfly/_variables';
2
+ @import '~patternfly/dist/sass/patternfly/_loading-state';
3
+ @import '~patternfly-react/dist/sass/_inline-edit';