foreman_webhooks 5.1.0 → 6.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c90b385f92db59657a6f026239c2ccfda1b574c852563755fd4dab6522cbf84c
4
- data.tar.gz: 05324c9d1589fed4e35b0bf477d0edc7380b5e1abe391872d27a18457355f6f9
3
+ metadata.gz: 0b5922c7ae2630c642e151ea99d59e8fce67ca342a93bd595a3c80d8837db0c7
4
+ data.tar.gz: c7d0bc6894477642639612f3d7b68974ea90dff25fabb91c3d684316fcd12230
5
5
  SHA512:
6
- metadata.gz: d8956d6c344db760a44ee50c9699d701078a5cae604dc561a40d55ecb25717d03129dceed5bb770c337cd9b1bc28455b0fff5aa80431e2b6834a368476f358ad
7
- data.tar.gz: 5d47beee070a29ac44960ea8c8bbe81e19179be5d806f062809a804457fc2929ea63dab6c6569b89d8ebf0d22a0af7c8a14dee2e1c53b15f1a3e6889eb485755
6
+ metadata.gz: 3f8c365cbf5d75887f69a3ffe3c9949e4fdfff3ca3129f12432747dda722ddf017892f700eb712353a2d805e07a53ff1f82f278afe8db3ac92ef6bec802bb76a
7
+ data.tar.gz: 34717dcb661c339a41377fbaa2c40603d0821bc5a0949f80abfd828497c49c9778543cd87d4505207d2e1b92cc9f684e7adca9c90eca4d854d6267dcce28a964
@@ -14,7 +14,7 @@ module ForemanWebhooks
14
14
  initializer 'foreman_webhooks.register_plugin', before: :finisher_hook do |app|
15
15
  app.reloader.to_prepare do
16
16
  Foreman::Plugin.register :foreman_webhooks do
17
- requires_foreman '>= 3.18'
17
+ requires_foreman '>= 5.0'
18
18
  register_gettext
19
19
 
20
20
  apipie_documented_controllers ["#{ForemanWebhooks::Engine.root}/app/controllers/api/v2/*.rb"]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ForemanWebhooks
4
- VERSION = "5.1.0"
4
+ VERSION = "6.0.0"
5
5
  end
@@ -2,10 +2,10 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
4
4
 
5
+ import FieldConstructor from 'foremanReact/components/common/FieldConstructor/FieldConstructor';
5
6
  import { sprintf, translate as __ } from 'foremanReact/common/I18n';
6
7
 
7
8
  import './WebhookFormTabs.css';
8
- import FieldConstructor from './FieldConstructor';
9
9
 
10
10
  const WebhookFormTabs = ({
11
11
  inputValues,
@@ -48,7 +48,7 @@ const WebhookFormTabs = ({
48
48
  type="select"
49
49
  label={__('Subscribe to')}
50
50
  required
51
- allowClear={false}
51
+ allowClear
52
52
  options={availableEvents}
53
53
  isLoading={isEventsLoading}
54
54
  placeholder={__('Start typing to search...')}
@@ -93,7 +93,7 @@ const WebhookFormTabs = ({
93
93
  type="select"
94
94
  label={__('Template')}
95
95
  required
96
- allowClear={false}
96
+ allowClear
97
97
  options={webhookTemplates}
98
98
  isLoading={isTemplatesLoading}
99
99
  placeholder={__('Start typing to search...')}
@@ -105,7 +105,7 @@ const WebhookFormTabs = ({
105
105
  type="select"
106
106
  label={__('HTTP Method')}
107
107
  required
108
- allowClear={false}
108
+ allowClear
109
109
  options={httpMethods}
110
110
  placeholder={__('Start typing to search...')}
111
111
  />
@@ -173,7 +173,6 @@ const WebhookFormTabs = ({
173
173
  placeholder={__(
174
174
  "Optional CAs in PEM format concatenated to verify the receiver's SSL certificate"
175
175
  )}
176
- inputSizeClass="col-md-8"
177
176
  rows={8}
178
177
  setValue={updateFieldValue}
179
178
  />
@@ -200,7 +199,6 @@ const WebhookFormTabs = ({
200
199
  label={__('HTTP Headers')}
201
200
  labelHelp={__('Optional. Must be a JSON object (ERB allowed)')}
202
201
  placeholder='{&#13;&#10;"X-Shellhook-Arg-1": "value",&#13;&#10;"X-Shellhook-Arg-2": "<%= @object.id %>"&#13;&#10;}'
203
- inputSizeClass="col-md-8"
204
202
  rows={8}
205
203
  setValue={updateFieldValue}
206
204
  />
@@ -12,6 +12,7 @@ import {
12
12
  ModalVariant,
13
13
  } from '@patternfly/react-core';
14
14
 
15
+ import FieldConstructor from 'foremanReact/components/common/FieldConstructor/FieldConstructor';
15
16
  import { APIActions } from 'foremanReact/redux/API';
16
17
  import {
17
18
  WEBHOOK_TEST_MODAL_ID,
@@ -19,7 +20,6 @@ import {
19
20
  } from '../../constants';
20
21
 
21
22
  import './WebhookModal.scss';
22
- import FieldConstructor from '../../Components/WebhookForm/Components/FieldConstructor';
23
23
 
24
24
  const WebhookTestModal = ({ toTest, modalState }) => {
25
25
  const dispatch = useDispatch();
@@ -80,7 +80,6 @@ const WebhookTestModal = ({ toTest, modalState }) => {
80
80
  label={__('Payload')}
81
81
  labelHelp={__('Will be sent as is')}
82
82
  placeholder="{&#13;&#10;id: 1,&#13;&#10;name: test&#13;&#10;}"
83
- inputSizeClass="col-md-8"
84
83
  rows={8}
85
84
  value={value.payload}
86
85
  setValue={(key, val) => {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_webhooks
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Timo Goebel
@@ -138,10 +138,8 @@ files:
138
138
  - test/test_plugin_helper.rb
139
139
  - test/unit/foreman_webhooks/webhook_service_test.rb
140
140
  - webpack/ForemanWebhooks/Routes/ForemanWebhooksRoutes.js
141
- - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/FieldConstructor.js
142
141
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.css
143
142
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.js
144
- - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/__tests__/FieldConstructor.test.js
145
143
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/WebhookForm.js
146
144
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/WebhookFormSelectors.js
147
145
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/WebhookForm.test.js
@@ -214,7 +212,6 @@ test_files:
214
212
  - test/models/webhook_test.rb
215
213
  - test/test_plugin_helper.rb
216
214
  - test/unit/foreman_webhooks/webhook_service_test.rb
217
- - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/__tests__/FieldConstructor.test.js
218
215
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/WebhookForm.test.js
219
216
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookTable/Components/Formatters/__tests__/actionCellFormatter.test.js
220
217
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookTable/Components/Formatters/__tests__/enabledCellFormatter.test.js
@@ -1,312 +0,0 @@
1
- /* eslint-disable max-lines */
2
- import React, { useEffect, useState } from 'react';
3
- import PropTypes from 'prop-types';
4
- import AutocompleteInput from 'foremanReact/components/common/AutocompleteInput/AutocompleteInput';
5
- import { translate as __ } from 'foremanReact/common/I18n';
6
- import {
7
- ExclamationCircleIcon,
8
- HelpIcon,
9
- PencilAltIcon,
10
- } from '@patternfly/react-icons';
11
- import {
12
- TextInput,
13
- Checkbox,
14
- Button,
15
- FormHelperText,
16
- HelperText,
17
- HelperTextItem,
18
- Icon,
19
- TextArea,
20
- FormGroup,
21
- Popover,
22
- Grid,
23
- GridItem,
24
- } from '@patternfly/react-core';
25
-
26
- const FormField = ({
27
- name,
28
- type,
29
- required,
30
- options,
31
- validated,
32
- value,
33
- disabled,
34
- setValue,
35
- placeholder,
36
- errMsg,
37
- fieldId,
38
- setIsPasswordDisabled,
39
- ...props
40
- }) => {
41
- const [fieldValidated, setFieldValidated] = useState('default');
42
- const [firstLoad, setFirstLoad] = useState(true);
43
-
44
- const requiredValidate = () => {
45
- if (firstLoad || !required) return;
46
- if (!value || value === '' || validated === 'error')
47
- setFieldValidated('error');
48
- else setFieldValidated('success');
49
- };
50
-
51
- const localHandler = (_event, newValue) => {
52
- setValue(name, newValue);
53
- };
54
-
55
- useEffect(() => {
56
- setFirstLoad(false);
57
- }, []);
58
-
59
- useEffect(() => {
60
- requiredValidate();
61
- // eslint-disable-next-line react-hooks/exhaustive-deps
62
- }, [value]);
63
-
64
- if (type === 'checkbox') {
65
- return (
66
- <>
67
- <Checkbox
68
- ouiaId={name}
69
- id={fieldId ?? `id-${name}`}
70
- isChecked={value || false}
71
- onChange={(_, newValue) => {
72
- setValue(name, newValue);
73
- }}
74
- isDisabled={disabled}
75
- isRequired={required}
76
- type={type}
77
- validated={fieldValidated}
78
- />
79
- <ValidationComponent localValidated={fieldValidated} errMsg={errMsg} />
80
- </>
81
- );
82
- } else if (type === 'textarea') {
83
- return (
84
- <>
85
- <TextArea
86
- rows={6}
87
- name={name}
88
- id={fieldId ?? `id-${name}`}
89
- value={value ?? ''}
90
- onChange={(_, newValue) => setValue(name, newValue)}
91
- isRequired={required}
92
- isDisabled={disabled}
93
- type={type}
94
- validated={fieldValidated}
95
- placeholder={placeholder}
96
- />
97
- <ValidationComponent localValidated={fieldValidated} errMsg={errMsg} />
98
- </>
99
- );
100
- } else if (options.length !== 0) {
101
- return (
102
- <>
103
- <AutocompleteInput
104
- options={options}
105
- selected={value ?? ''}
106
- onSelect={newValue => setValue(name, newValue)}
107
- onChange={() => requiredValidate()}
108
- name={name}
109
- placeholder={placeholder}
110
- validationStatus={fieldValidated}
111
- validationMsg={errMsg}
112
- fieldId={fieldId}
113
- />
114
- </>
115
- );
116
- }
117
- return (
118
- <>
119
- {name === 'password' && disabled && setIsPasswordDisabled ? (
120
- <Grid hasGutter={false}>
121
- <GridItem span={11}>
122
- <TextInput
123
- name={name}
124
- value={value ?? ''}
125
- id={fieldId ?? `id-${name}`}
126
- onChange={localHandler}
127
- isDisabled={disabled}
128
- isRequired={required}
129
- type={type}
130
- validated={fieldValidated}
131
- placeholder={placeholder}
132
- onBlur={requiredValidate}
133
- autoComplete="off"
134
- />
135
- </GridItem>
136
- <GridItem span={1}>
137
- <Button
138
- ouiaId={`reset-${name}`}
139
- onClick={() => setIsPasswordDisabled(!disabled)}
140
- variant="control"
141
- icon={<PencilAltIcon />}
142
- />
143
- </GridItem>
144
- </Grid>
145
- ) : (
146
- <TextInput
147
- name={name}
148
- value={value ?? ''}
149
- id={fieldId ?? `id-${name}`}
150
- onChange={localHandler}
151
- isRequired={required}
152
- type={type}
153
- validated={fieldValidated}
154
- onBlur={requiredValidate}
155
- autoComplete={type === 'password' ? 'new-password' : null}
156
- />
157
- )}
158
-
159
- <ValidationComponent localValidated={fieldValidated} errMsg={errMsg} />
160
- </>
161
- );
162
- };
163
-
164
- const ValidationComponent = ({ localValidated, errMsg }) => (
165
- <>
166
- {localValidated === 'error' && (
167
- <FormHelperText>
168
- <HelperText>
169
- <HelperTextItem
170
- icon={
171
- <Icon>
172
- <ExclamationCircleIcon />
173
- </Icon>
174
- }
175
- variant={localValidated}
176
- >
177
- {errMsg ?? __('Field is required')}
178
- </HelperTextItem>
179
- </HelperText>
180
- </FormHelperText>
181
- )}
182
- </>
183
- );
184
-
185
- const FieldConstructor = ({
186
- label,
187
- value,
188
- required,
189
- labelHelp,
190
- fieldId,
191
- name,
192
- ...props
193
- }) => (
194
- <FormGroup
195
- isInline
196
- fieldId={fieldId ?? `id-${name}`}
197
- label={label}
198
- isRequired={required}
199
- labelIcon={
200
- labelHelp ? (
201
- <Popover
202
- bodyContent={
203
- typeof labelHelp === 'string' ? (
204
- <div dangerouslySetInnerHTML={{ __html: labelHelp }} />
205
- ) : (
206
- labelHelp
207
- )
208
- }
209
- >
210
- <Button
211
- ouiaId={`label-${label}`}
212
- type="button"
213
- variant="plain"
214
- onClick={e => e.preventDefault()}
215
- >
216
- <Icon>
217
- <HelpIcon />
218
- </Icon>
219
- </Button>
220
- </Popover>
221
- ) : (
222
- ''
223
- )
224
- }
225
- >
226
- <FormField
227
- value={value}
228
- required={required}
229
- name={name}
230
- {...props}
231
- fieldId={fieldId}
232
- />
233
- </FormGroup>
234
- );
235
-
236
- FieldConstructor.propTypes = {
237
- label: PropTypes.string.isRequired,
238
- required: PropTypes.bool,
239
- labelHelp: PropTypes.oneOfType([
240
- PropTypes.string,
241
- PropTypes.object,
242
- PropTypes.node,
243
- ]),
244
- setValue: PropTypes.func.isRequired,
245
- name: PropTypes.string.isRequired,
246
- disabled: PropTypes.bool,
247
- value: PropTypes.oneOfType([
248
- PropTypes.string,
249
- PropTypes.object,
250
- PropTypes.bool,
251
- PropTypes.number,
252
- ]),
253
- fieldId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
254
- };
255
-
256
- FieldConstructor.defaultProps = {
257
- labelHelp: undefined,
258
- required: false,
259
- disabled: false,
260
- value: '',
261
- fieldId: undefined,
262
- };
263
-
264
- FormField.propTypes = {
265
- disabled: PropTypes.bool,
266
- setValue: PropTypes.func.isRequired,
267
- name: PropTypes.string.isRequired,
268
- type: PropTypes.string.isRequired,
269
- label: PropTypes.string,
270
- required: PropTypes.bool,
271
- allowClear: PropTypes.bool,
272
- options: PropTypes.arrayOf(
273
- PropTypes.shape({
274
- value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
275
- .isRequired,
276
- label: PropTypes.string.isRequired,
277
- })
278
- ),
279
- validated: PropTypes.string,
280
- placeholder: PropTypes.string,
281
- errMsg: PropTypes.string,
282
- value: PropTypes.oneOfType([
283
- PropTypes.bool,
284
- PropTypes.string,
285
- PropTypes.number,
286
- ]),
287
- fieldId: PropTypes.string,
288
- };
289
-
290
- FormField.defaultProps = {
291
- label: '',
292
- errMsg: null,
293
- placeholder: '',
294
- validated: 'default',
295
- required: false,
296
- allowClear: false,
297
- options: [],
298
- disabled: false,
299
- value: '',
300
- fieldId: undefined,
301
- };
302
-
303
- ValidationComponent.propTypes = {
304
- localValidated: PropTypes.string.isRequired,
305
- errMsg: PropTypes.string,
306
- };
307
-
308
- ValidationComponent.defaultProps = {
309
- errMsg: null,
310
- };
311
-
312
- export default FieldConstructor;
@@ -1,216 +0,0 @@
1
- import React from 'react';
2
- import { render, screen, fireEvent } from '@testing-library/react';
3
- import userEvent from '@testing-library/user-event';
4
- import '@testing-library/jest-dom';
5
- import FieldConstructor from '../FieldConstructor';
6
-
7
- const defaultProps = {
8
- name: 'test-field',
9
- setValue: jest.fn(),
10
- label: 'Test Field',
11
- value: '',
12
- type: 'text',
13
- };
14
-
15
- describe('FieldConstructor RTL Tests', () => {
16
- beforeEach(() => {
17
- jest.clearAllMocks();
18
- });
19
-
20
- describe('Text Input Fields', () => {
21
- test('renders text input field', () => {
22
- render(<FieldConstructor {...defaultProps} type="text" />);
23
-
24
- expect(screen.getByText('Test Field')).toBeInTheDocument();
25
- expect(document.getElementById('id-test-field')).toHaveAttribute(
26
- 'type',
27
- 'text'
28
- );
29
- });
30
-
31
- test('renders password input field', () => {
32
- render(<FieldConstructor {...defaultProps} type="password" />);
33
-
34
- expect(screen.getByText('Test Field')).toBeInTheDocument();
35
- expect(document.getElementById('id-test-field')).toHaveAttribute(
36
- 'type',
37
- 'password'
38
- );
39
- });
40
-
41
- test('calls setValue when text input changes', () => {
42
- render(<FieldConstructor {...defaultProps} type="text" />);
43
-
44
- const input = document.getElementById('id-test-field');
45
- fireEvent.change(input, { target: { value: 'test value' } });
46
-
47
- expect(defaultProps.setValue).toHaveBeenCalledWith(
48
- 'test-field',
49
- 'test value'
50
- );
51
- });
52
-
53
- test('renders with initial value', () => {
54
- render(
55
- <FieldConstructor {...defaultProps} type="text" value="initial value" />
56
- );
57
-
58
- expect(document.getElementById('id-test-field')).toHaveValue(
59
- 'initial value'
60
- );
61
- });
62
-
63
- test('renders as enabled when loading', () => {
64
- render(<FieldConstructor {...defaultProps} type="text" isLoading />);
65
-
66
- expect(document.getElementById('id-test-field')).toBeEnabled();
67
- });
68
- });
69
-
70
- describe('Textarea Fields', () => {
71
- test('renders textarea field', () => {
72
- render(<FieldConstructor {...defaultProps} type="textarea" />);
73
-
74
- expect(screen.getByText('Test Field')).toBeInTheDocument();
75
- expect(document.getElementById('id-test-field').tagName).toBe('TEXTAREA');
76
- });
77
-
78
- test('calls setValue when textarea changes', () => {
79
- render(<FieldConstructor {...defaultProps} type="textarea" />);
80
-
81
- const textarea = document.getElementById('id-test-field');
82
- fireEvent.change(textarea, { target: { value: 'textarea content' } });
83
-
84
- expect(defaultProps.setValue).toHaveBeenCalledWith(
85
- 'test-field',
86
- 'textarea content'
87
- );
88
- });
89
-
90
- test('renders with specified rows', () => {
91
- render(<FieldConstructor {...defaultProps} type="textarea" rows={5} />);
92
-
93
- expect(document.getElementsByTagName('textarea')).toHaveLength(1);
94
- });
95
- });
96
-
97
- describe('Checkbox Fields', () => {
98
- test('renders checkbox field', () => {
99
- render(<FieldConstructor {...defaultProps} type="checkbox" />);
100
-
101
- expect(screen.getByText('Test Field')).toBeInTheDocument();
102
- expect(document.getElementById('id-test-field')).toHaveAttribute(
103
- 'type',
104
- 'checkbox'
105
- );
106
- });
107
-
108
- test('renders unchecked checkbox', () => {
109
- render(
110
- <FieldConstructor {...defaultProps} type="checkbox" value={false} />
111
- );
112
-
113
- expect(document.getElementById('id-test-field')).not.toBeChecked();
114
- });
115
- });
116
-
117
- describe('Autocomplete render test', () => {
118
- test('renders autocomplete', () => {
119
- const props = {
120
- ...defaultProps,
121
- onChange: defaultProps.setValue,
122
- options: [
123
- { value: 'host_created.event.foreman', label: 'Host Created' },
124
- { value: 'host_updated.event.foreman', label: 'Host Updated' },
125
- ],
126
- };
127
- render(<FieldConstructor {...props} type="select" />);
128
-
129
- expect(screen.getByRole('combobox')).toBeInTheDocument();
130
- });
131
- });
132
-
133
- describe('Label Help', () => {
134
- test('renders help icon when labelHelp is provided', () => {
135
- render(
136
- <FieldConstructor {...defaultProps} labelHelp="This is help text" />
137
- );
138
-
139
- expect(screen.getByRole('button')).toBeInTheDocument();
140
- });
141
-
142
- test('does not render help icon when labelHelp is not provided', () => {
143
- render(<FieldConstructor {...defaultProps} />);
144
-
145
- expect(screen.queryByRole('button')).not.toBeInTheDocument();
146
- });
147
-
148
- test('renders help icon with JSX content', async () => {
149
- const helpContent = <div>JSX help content</div>;
150
- render(<FieldConstructor {...defaultProps} labelHelp={helpContent} />);
151
-
152
- const button = screen.getByRole('button');
153
- expect(button).toBeInTheDocument();
154
-
155
- userEvent.click(button);
156
-
157
- expect(await screen.findByText('JSX help content')).toBeInTheDocument();
158
- });
159
- });
160
-
161
- describe('Validation', () => {
162
- test('renders error message when validated is error', async () => {
163
- render(
164
- <>
165
- <FieldConstructor {...defaultProps} required type="text" />
166
- <button>Dummy</button>
167
- </>
168
- );
169
-
170
- const input = document.getElementById('id-test-field');
171
- await userEvent.click(input);
172
- expect(input).toHaveFocus();
173
-
174
- await userEvent.tab();
175
- expect(input).not.toHaveFocus();
176
-
177
- expect(screen.getByText('Field is required')).toBeInTheDocument();
178
- });
179
-
180
- test('does not render error message when validated is not error', () => {
181
- render(
182
- <FieldConstructor {...defaultProps} type="text" validated="success" />
183
- );
184
-
185
- expect(screen.queryByText('Field is required')).not.toBeInTheDocument();
186
- });
187
- });
188
-
189
- describe('Edge Cases', () => {
190
- test('handles undefined value', () => {
191
- render(
192
- <FieldConstructor {...defaultProps} type="text" value={undefined} />
193
- );
194
-
195
- expect(document.getElementById('id-test-field')).toHaveValue('');
196
- });
197
-
198
- test('handles null value', () => {
199
- render(<FieldConstructor {...defaultProps} type="text" value={null} />);
200
-
201
- expect(document.getElementById('id-test-field')).toHaveValue('');
202
- });
203
-
204
- test('handles numeric value', () => {
205
- render(<FieldConstructor {...defaultProps} type="text" value={123} />);
206
-
207
- expect(document.getElementById('id-test-field')).toHaveValue('123');
208
- });
209
-
210
- test('handles boolean value for checkbox', () => {
211
- render(<FieldConstructor {...defaultProps} type="checkbox" value />);
212
-
213
- expect(document.getElementById('id-test-field')).toBeChecked();
214
- });
215
- });
216
- });