foreman_acd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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';