foreman_webhooks 4.0.2 → 5.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 +4 -4
- data/lib/foreman_webhooks/engine.rb +1 -1
- data/lib/foreman_webhooks/version.rb +1 -1
- data/test/integration/webhooks_test.rb +234 -0
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/FieldConstructor.js +321 -0
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.css +23 -4
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.js +191 -157
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/__tests__/FieldConstructor.test.js +216 -0
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/WebhookForm.js +55 -26
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/WebhookForm.test.js +253 -37
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/index.js +3 -4
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookCreateModal.js +30 -19
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookDeleteModal.js +52 -18
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookEditModal.js +37 -26
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookTestModal.js +57 -32
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/WebhooksTable.js +24 -11
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/__tests__/__snapshots__/WebhooksTable.test.js.snap +64 -0
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/index.js +21 -22
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/WebhooksIndexPage.js +9 -15
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/integration.test.js +0 -3
- metadata +7 -5
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/ForemanFormikField.js +0 -152
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/__snapshots__/WebhookForm.test.js.snap +0 -512
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/__snapshots__/integration.test.js.snap +0 -31
|
@@ -1,24 +1,14 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import * as Yup from 'yup';
|
|
4
|
-
|
|
5
3
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
6
|
-
import
|
|
7
|
-
|
|
4
|
+
import { Form, ActionGroup, Button } from '@patternfly/react-core';
|
|
8
5
|
import WebhookFormTabs from './Components/WebhookFormTabs';
|
|
9
6
|
|
|
10
|
-
import { HTTP_METHODS
|
|
11
|
-
|
|
12
|
-
const webhookFormSchema = Yup.object().shape({
|
|
13
|
-
name: Yup.string().required(__('is required')),
|
|
14
|
-
target_url: Yup.string().required(__('is required')),
|
|
15
|
-
http_method: Yup.string().required(__('is required')),
|
|
16
|
-
event: Yup.string().required(__('is required')),
|
|
17
|
-
webhook_template_id: Yup.string().required(__('is required')),
|
|
18
|
-
});
|
|
7
|
+
import { HTTP_METHODS } from './constants';
|
|
19
8
|
|
|
20
9
|
const WebhookForm = ({
|
|
21
10
|
onCancel,
|
|
11
|
+
isLoading,
|
|
22
12
|
handleSubmit,
|
|
23
13
|
initialValues,
|
|
24
14
|
templates,
|
|
@@ -26,7 +16,6 @@ const WebhookForm = ({
|
|
|
26
16
|
isTemplatesLoading,
|
|
27
17
|
isEventsLoading,
|
|
28
18
|
isPasswordDisabled,
|
|
29
|
-
setIsPasswordDisabled,
|
|
30
19
|
}) => {
|
|
31
20
|
const webhookTemplates = templates.map(t => ({ value: t.id, label: t.name }));
|
|
32
21
|
|
|
@@ -36,16 +25,44 @@ const WebhookForm = ({
|
|
|
36
25
|
setActiveTab(tabIndex);
|
|
37
26
|
};
|
|
38
27
|
|
|
28
|
+
const [inputValues, setInputValues] = useState(initialValues);
|
|
29
|
+
|
|
30
|
+
const requiredFields = [
|
|
31
|
+
'event',
|
|
32
|
+
'name',
|
|
33
|
+
'target_url',
|
|
34
|
+
'webhook_template_id',
|
|
35
|
+
'http_method',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const verifyFields = () =>
|
|
39
|
+
!requiredFields.every(field => {
|
|
40
|
+
if (field === 'target_url') return urlValidated() === 'success';
|
|
41
|
+
return (
|
|
42
|
+
inputValues[field] !== undefined &&
|
|
43
|
+
inputValues[field] !== null &&
|
|
44
|
+
String(inputValues[field]).trim() !== ''
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const urlValidated = () => {
|
|
49
|
+
const value = inputValues.target_url;
|
|
50
|
+
if (!value || !value.trim()) return 'error';
|
|
51
|
+
try {
|
|
52
|
+
const u = new URL(value.trim());
|
|
53
|
+
return u.protocol === 'http:' || u.protocol === 'https:'
|
|
54
|
+
? 'success'
|
|
55
|
+
: 'error';
|
|
56
|
+
} catch {
|
|
57
|
+
return 'error';
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
39
61
|
return (
|
|
40
|
-
<
|
|
41
|
-
onSubmit={handleSubmit}
|
|
42
|
-
initialValues={initialValues}
|
|
43
|
-
validationSchema={webhookFormSchema}
|
|
44
|
-
onCancel={onCancel}
|
|
45
|
-
enableReinitialize
|
|
46
|
-
item={WEBHOOK_ITEM}
|
|
47
|
-
>
|
|
62
|
+
<Form isHorizontal>
|
|
48
63
|
<WebhookFormTabs
|
|
64
|
+
inputValues={inputValues}
|
|
65
|
+
setInputValues={setInputValues}
|
|
49
66
|
activeTab={activeTab}
|
|
50
67
|
handleTabClick={handleTabClick}
|
|
51
68
|
webhookTemplates={webhookTemplates}
|
|
@@ -54,13 +71,27 @@ const WebhookForm = ({
|
|
|
54
71
|
isEventsLoading={isEventsLoading}
|
|
55
72
|
isTemplatesLoading={isTemplatesLoading}
|
|
56
73
|
isPasswordDisabled={isPasswordDisabled}
|
|
57
|
-
|
|
74
|
+
urlValidated={urlValidated}
|
|
58
75
|
/>
|
|
59
|
-
|
|
76
|
+
<ActionGroup>
|
|
77
|
+
<Button
|
|
78
|
+
ouiaId="submit-webhook-form"
|
|
79
|
+
isDisabled={verifyFields() || isLoading}
|
|
80
|
+
variant="primary"
|
|
81
|
+
onClick={() => handleSubmit(inputValues)}
|
|
82
|
+
>
|
|
83
|
+
{__('Submit')}
|
|
84
|
+
</Button>
|
|
85
|
+
<Button ouiaId="cancel-webhook-form" variant="link" onClick={onCancel}>
|
|
86
|
+
{__('Cancel')}
|
|
87
|
+
</Button>
|
|
88
|
+
</ActionGroup>
|
|
89
|
+
</Form>
|
|
60
90
|
);
|
|
61
91
|
};
|
|
62
92
|
|
|
63
93
|
WebhookForm.propTypes = {
|
|
94
|
+
isLoading: PropTypes.bool.isRequired,
|
|
64
95
|
onCancel: PropTypes.func.isRequired,
|
|
65
96
|
handleSubmit: PropTypes.func.isRequired,
|
|
66
97
|
initialValues: PropTypes.object.isRequired,
|
|
@@ -69,12 +100,10 @@ WebhookForm.propTypes = {
|
|
|
69
100
|
isEventsLoading: PropTypes.bool.isRequired,
|
|
70
101
|
isTemplatesLoading: PropTypes.bool.isRequired,
|
|
71
102
|
isPasswordDisabled: PropTypes.bool,
|
|
72
|
-
setIsPasswordDisabled: PropTypes.func,
|
|
73
103
|
};
|
|
74
104
|
|
|
75
105
|
WebhookForm.defaultProps = {
|
|
76
106
|
isPasswordDisabled: false,
|
|
77
|
-
setIsPasswordDisabled: undefined,
|
|
78
107
|
};
|
|
79
108
|
|
|
80
109
|
export default WebhookForm;
|
data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/WebhookForm.test.js
CHANGED
|
@@ -1,51 +1,267 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import '@testing-library/jest-dom';
|
|
3
5
|
import WebhookForm from '../WebhookForm';
|
|
4
6
|
|
|
5
|
-
const
|
|
7
|
+
const defaultProps = {
|
|
6
8
|
handleSubmit: jest.fn(),
|
|
7
9
|
onCancel: jest.fn(),
|
|
8
|
-
|
|
10
|
+
initialValues: {
|
|
11
|
+
http_method: 'POST',
|
|
12
|
+
enabled: true,
|
|
13
|
+
verify_ssl: true,
|
|
14
|
+
http_content_type: 'application/json',
|
|
15
|
+
event: 'host_created.event.foreman',
|
|
16
|
+
name: '',
|
|
17
|
+
target_url: '',
|
|
18
|
+
user: '',
|
|
19
|
+
password: '',
|
|
20
|
+
webhook_template_id: '',
|
|
21
|
+
ssl_ca_certs: '',
|
|
22
|
+
proxy_authorization: false,
|
|
23
|
+
http_headers: '',
|
|
24
|
+
},
|
|
9
25
|
templates: [
|
|
10
26
|
{ id: 204, name: 'default template' },
|
|
11
27
|
{ id: 205, name: 'default template clone' },
|
|
12
28
|
],
|
|
13
|
-
availableEvents: [
|
|
29
|
+
availableEvents: [
|
|
30
|
+
{ value: 'host_created.event.foreman', label: 'Host Created' },
|
|
31
|
+
{ value: 'host_updated.event.foreman', label: 'Host Updated' },
|
|
32
|
+
],
|
|
14
33
|
isEventsLoading: false,
|
|
15
34
|
isTemplatesLoading: false,
|
|
35
|
+
isPasswordDisabled: false,
|
|
36
|
+
isLoading: false,
|
|
37
|
+
setIsPasswordDisabled: jest.fn(),
|
|
16
38
|
};
|
|
17
39
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
describe('WebhookForm RTL Tests', () => {
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
jest.clearAllMocks();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('renders General tab fields by default', () => {
|
|
46
|
+
render(<WebhookForm {...defaultProps} />);
|
|
47
|
+
|
|
48
|
+
expect(screen.getByText('General')).toBeInTheDocument();
|
|
49
|
+
expect(screen.getByText('Credentials')).toBeInTheDocument();
|
|
50
|
+
expect(screen.getByText('Additional')).toBeInTheDocument();
|
|
51
|
+
|
|
52
|
+
expect(screen.getByText('Subscribe to')).toBeInTheDocument();
|
|
53
|
+
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
54
|
+
expect(screen.getByText('Target URL')).toBeInTheDocument();
|
|
55
|
+
expect(screen.getByText('Template')).toBeInTheDocument();
|
|
56
|
+
expect(screen.getByText('HTTP Method')).toBeInTheDocument();
|
|
57
|
+
expect(screen.getByText('Enabled')).toBeInTheDocument();
|
|
58
|
+
|
|
59
|
+
expect(screen.getByRole('button', { name: 'Submit' })).toBeInTheDocument();
|
|
60
|
+
expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('Tab Navigation', () => {
|
|
64
|
+
test('switches to Credentials tab when clicked', () => {
|
|
65
|
+
render(<WebhookForm {...defaultProps} />);
|
|
66
|
+
|
|
67
|
+
fireEvent.click(screen.getByText('Credentials'));
|
|
68
|
+
|
|
69
|
+
expect(screen.getByText('User')).toBeInTheDocument();
|
|
70
|
+
expect(screen.getByText('Password')).toBeInTheDocument();
|
|
71
|
+
expect(screen.getByText('Verify SSL')).toBeInTheDocument();
|
|
72
|
+
expect(screen.getByText('Proxy Authorization')).toBeInTheDocument();
|
|
73
|
+
expect(
|
|
74
|
+
screen.getByText('X509 Certificate Authorities')
|
|
75
|
+
).toBeInTheDocument();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('switches to Additional tab when clicked', () => {
|
|
79
|
+
render(<WebhookForm {...defaultProps} />);
|
|
80
|
+
|
|
81
|
+
fireEvent.click(screen.getByText('Additional'));
|
|
82
|
+
|
|
83
|
+
expect(screen.getByText('HTTP Content Type')).toBeInTheDocument();
|
|
84
|
+
expect(screen.getByText('HTTP Headers')).toBeInTheDocument();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('Form Field Interactions', () => {
|
|
89
|
+
test('toggles enabled checkbox', () => {
|
|
90
|
+
render(<WebhookForm {...defaultProps} />);
|
|
91
|
+
|
|
92
|
+
const enabledCheckbox = document.querySelector('input#id-enabled');
|
|
93
|
+
expect(enabledCheckbox).toBeChecked();
|
|
94
|
+
|
|
95
|
+
fireEvent.click(enabledCheckbox);
|
|
96
|
+
expect(enabledCheckbox).not.toBeChecked();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('updates user field in Credentials tab', () => {
|
|
100
|
+
render(<WebhookForm {...defaultProps} />);
|
|
101
|
+
|
|
102
|
+
fireEvent.click(screen.getByText('Credentials'));
|
|
103
|
+
|
|
104
|
+
const userInput = document.querySelector('input#id-user');
|
|
105
|
+
fireEvent.change(userInput, { target: { value: 'testuser' } });
|
|
106
|
+
|
|
107
|
+
expect(userInput.value).toBe('testuser');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('Form Submission', () => {
|
|
112
|
+
test('Submit button is disabled should not call handleSubmit', async () => {
|
|
113
|
+
const handleSubmit = jest.fn();
|
|
114
|
+
render(<WebhookForm {...defaultProps} handleSubmit={handleSubmit} />);
|
|
115
|
+
|
|
116
|
+
const submitBtn = screen.getByRole('button', { name: 'Submit' });
|
|
117
|
+
|
|
118
|
+
expect(submitBtn).toBeDisabled();
|
|
119
|
+
fireEvent.click(submitBtn);
|
|
120
|
+
|
|
121
|
+
expect(handleSubmit).toHaveBeenCalledTimes(0);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('calls handleSubmit with form data when Submit button is clicked', async () => {
|
|
125
|
+
const handleSubmit = jest.fn();
|
|
126
|
+
const initialValues = {
|
|
127
|
+
...defaultProps.initialValues,
|
|
128
|
+
webhook_template_id: 204,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
render(
|
|
132
|
+
<WebhookForm
|
|
133
|
+
{...defaultProps}
|
|
134
|
+
initialValues={initialValues}
|
|
135
|
+
handleSubmit={handleSubmit}
|
|
136
|
+
/>
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const input = document.querySelector('input#id-name');
|
|
140
|
+
expect(input).toBeInTheDocument();
|
|
141
|
+
await userEvent.type(input, 'Test Webhook');
|
|
142
|
+
fireEvent.change(document.querySelector('input#id-target_url'), {
|
|
143
|
+
target: { value: 'https://example.com/webhook' },
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const submitBtn = screen.getByRole('button', { name: 'Submit' });
|
|
147
|
+
|
|
148
|
+
expect(submitBtn).not.toBeDisabled();
|
|
149
|
+
fireEvent.click(submitBtn);
|
|
150
|
+
|
|
151
|
+
expect(handleSubmit).toHaveBeenCalledTimes(1);
|
|
152
|
+
expect(handleSubmit).toHaveBeenCalledWith(
|
|
153
|
+
expect.objectContaining({
|
|
154
|
+
name: 'Test Webhook',
|
|
155
|
+
target_url: 'https://example.com/webhook',
|
|
156
|
+
})
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test('calls onCancel when Cancel button is clicked', () => {
|
|
161
|
+
const onCancel = jest.fn();
|
|
162
|
+
render(<WebhookForm {...defaultProps} onCancel={onCancel} />);
|
|
163
|
+
|
|
164
|
+
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
|
165
|
+
|
|
166
|
+
expect(onCancel).toHaveBeenCalledTimes(1);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('Loading States', () => {
|
|
171
|
+
test('disables password field when isPasswordDisabled is true', async () => {
|
|
172
|
+
render(<WebhookForm {...defaultProps} isPasswordDisabled />);
|
|
173
|
+
|
|
174
|
+
await fireEvent.click(screen.getByText('Credentials'));
|
|
175
|
+
|
|
176
|
+
const passwordInput = document.querySelector('input#id-password');
|
|
177
|
+
expect(passwordInput).toBeDisabled();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('password field rendered correctly when isPasswordDisabled is false', async () => {
|
|
181
|
+
render(<WebhookForm {...defaultProps} />);
|
|
182
|
+
|
|
183
|
+
await fireEvent.click(screen.getByText('Credentials'));
|
|
184
|
+
|
|
185
|
+
const passwordInput = document.querySelector('input#id-password');
|
|
186
|
+
expect(passwordInput).toBeInTheDocument();
|
|
187
|
+
expect(passwordInput).not.toBeDisabled();
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe('Initial Values', () => {
|
|
192
|
+
test('renders with provided initial values', () => {
|
|
193
|
+
const initialValues = {
|
|
194
|
+
...defaultProps.initialValues,
|
|
195
|
+
name: 'Initial Webhook',
|
|
196
|
+
target_url: 'https://initial.example.com',
|
|
197
|
+
enabled: false,
|
|
198
|
+
verify_ssl: false,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
render(<WebhookForm {...defaultProps} initialValues={initialValues} />);
|
|
202
|
+
|
|
203
|
+
expect(document.querySelector('input#id-name')).toHaveValue(
|
|
204
|
+
'Initial Webhook'
|
|
205
|
+
);
|
|
206
|
+
expect(document.querySelector('input#id-target_url')).toHaveValue(
|
|
207
|
+
'https://initial.example.com'
|
|
208
|
+
);
|
|
209
|
+
expect(document.querySelector('input#id-enabled')).not.toBeChecked();
|
|
210
|
+
|
|
211
|
+
fireEvent.click(screen.getByText('Credentials'));
|
|
212
|
+
expect(document.querySelector('input#id-verify_ssl')).not.toBeChecked();
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe('Test submitted data', () => {
|
|
217
|
+
test('submits correct data structure', async () => {
|
|
218
|
+
const handleSubmit = jest.fn();
|
|
219
|
+
render(<WebhookForm {...defaultProps} handleSubmit={handleSubmit} />);
|
|
220
|
+
|
|
221
|
+
const nameInput = document.querySelector('input#id-name');
|
|
222
|
+
await userEvent.type(nameInput, 'Webhook Test');
|
|
223
|
+
await fireEvent.change(document.querySelector('input#id-target_url'), {
|
|
224
|
+
target: { value: 'https://example.com/webhook' },
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const webhookTemplateInput = document.querySelector(
|
|
228
|
+
'input#id-webhook_template_id'
|
|
229
|
+
);
|
|
230
|
+
fireEvent.click(webhookTemplateInput);
|
|
231
|
+
|
|
232
|
+
await waitFor(() => {
|
|
233
|
+
const option = screen.getByRole('option', { name: 'default template' });
|
|
234
|
+
fireEvent.click(option);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
await fireEvent.click(screen.getByText('Credentials'));
|
|
238
|
+
|
|
239
|
+
const userInput = document.querySelector('input#id-user');
|
|
240
|
+
await fireEvent.change(userInput, { target: { value: 'testuser' } });
|
|
241
|
+
|
|
242
|
+
const passwordInput = document.querySelector('input#id-password');
|
|
243
|
+
await fireEvent.change(passwordInput, { target: { value: 'testpass' } });
|
|
244
|
+
|
|
245
|
+
const submitBtn = screen.getByRole('button', { name: 'Submit' });
|
|
246
|
+
|
|
247
|
+
expect(submitBtn).not.toBeDisabled();
|
|
248
|
+
fireEvent.click(submitBtn);
|
|
47
249
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
250
|
+
expect(handleSubmit).toHaveBeenCalledWith({
|
|
251
|
+
name: 'Webhook Test',
|
|
252
|
+
target_url: 'https://example.com/webhook',
|
|
253
|
+
webhook_template_id: 204,
|
|
254
|
+
http_method: 'POST',
|
|
255
|
+
enabled: true,
|
|
256
|
+
verify_ssl: true,
|
|
257
|
+
http_content_type: 'application/json',
|
|
258
|
+
event: 'host_created.event.foreman',
|
|
259
|
+
user: 'testuser',
|
|
260
|
+
password: 'testpass',
|
|
261
|
+
proxy_authorization: false,
|
|
262
|
+
ssl_ca_certs: '',
|
|
263
|
+
http_headers: '',
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
});
|
|
51
267
|
});
|
|
@@ -25,11 +25,11 @@ import {
|
|
|
25
25
|
const params = { page: 1, search: 'snippet = false', per_page: 'all' };
|
|
26
26
|
|
|
27
27
|
const ConnectedWebhookForm = ({
|
|
28
|
+
isLoading,
|
|
28
29
|
onCancel,
|
|
29
30
|
handleSubmit,
|
|
30
31
|
initialValues,
|
|
31
32
|
isPasswordDisabled,
|
|
32
|
-
setIsPasswordDisabled,
|
|
33
33
|
}) => {
|
|
34
34
|
const dispatch = useDispatch();
|
|
35
35
|
|
|
@@ -59,6 +59,7 @@ const ConnectedWebhookForm = ({
|
|
|
59
59
|
|
|
60
60
|
return (
|
|
61
61
|
<WebhookForm
|
|
62
|
+
isLoading={isLoading}
|
|
62
63
|
templates={templates}
|
|
63
64
|
availableEvents={availableEvents}
|
|
64
65
|
onCancel={onCancel}
|
|
@@ -67,22 +68,20 @@ const ConnectedWebhookForm = ({
|
|
|
67
68
|
isTemplatesLoading={isTemplatesLoading}
|
|
68
69
|
isEventsLoading={isEventsLoading}
|
|
69
70
|
isPasswordDisabled={isPasswordDisabled}
|
|
70
|
-
setIsPasswordDisabled={setIsPasswordDisabled}
|
|
71
71
|
/>
|
|
72
72
|
);
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
ConnectedWebhookForm.propTypes = {
|
|
76
|
+
isLoading: PropTypes.bool.isRequired,
|
|
76
77
|
onCancel: PropTypes.func.isRequired,
|
|
77
78
|
handleSubmit: PropTypes.func.isRequired,
|
|
78
79
|
initialValues: PropTypes.object.isRequired,
|
|
79
80
|
isPasswordDisabled: PropTypes.bool,
|
|
80
|
-
setIsPasswordDisabled: PropTypes.func,
|
|
81
81
|
};
|
|
82
82
|
|
|
83
83
|
ConnectedWebhookForm.defaultProps = {
|
|
84
84
|
isPasswordDisabled: false,
|
|
85
|
-
setIsPasswordDisabled: undefined,
|
|
86
85
|
};
|
|
87
86
|
|
|
88
87
|
export default ConnectedWebhookForm;
|
data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookCreateModal.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import { Modal } from 'patternfly-
|
|
3
|
+
import { Modal, ModalVariant } from '@patternfly/react-core';
|
|
4
4
|
import { useDispatch } from 'react-redux';
|
|
5
5
|
|
|
6
6
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
7
|
-
import { submitForm } from 'foremanReact/redux/actions/common/forms';
|
|
8
7
|
import { foremanUrl } from 'foremanReact/common/helpers';
|
|
9
|
-
import
|
|
8
|
+
import { APIActions } from 'foremanReact/redux/API';
|
|
10
9
|
|
|
11
10
|
import ConnectedWebhookForm from '../../Components/WebhookForm';
|
|
12
11
|
|
|
@@ -14,9 +13,10 @@ import { WEBHOOK_CREATE_MODAL_ID, WEBHOOKS_PATH } from '../../constants';
|
|
|
14
13
|
|
|
15
14
|
import './WebhookModal.scss';
|
|
16
15
|
|
|
17
|
-
const WebhookCreateModal = ({ onSuccess, onCancel }) => {
|
|
16
|
+
const WebhookCreateModal = ({ onSuccess, onCancel, isOpen }) => {
|
|
18
17
|
const dispatch = useDispatch();
|
|
19
18
|
|
|
19
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
20
20
|
const initialWebhookValues = {
|
|
21
21
|
name: '',
|
|
22
22
|
target_url: '',
|
|
@@ -33,39 +33,50 @@ const WebhookCreateModal = ({ onSuccess, onCancel }) => {
|
|
|
33
33
|
proxy_authorization: false,
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
const handleSubmit =
|
|
36
|
+
const handleSubmit = values => {
|
|
37
|
+
setIsLoading(true);
|
|
37
38
|
dispatch(
|
|
38
|
-
|
|
39
|
+
APIActions.post({
|
|
39
40
|
url: foremanUrl(`/api${WEBHOOKS_PATH}`),
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
key: WEBHOOK_CREATE_MODAL_ID,
|
|
42
|
+
params: { ...values, controller: 'webhooks' },
|
|
43
|
+
successToast: () => __('Webhook was successfully created.'),
|
|
44
|
+
handleSuccess: () => {
|
|
45
|
+
onSuccess();
|
|
46
|
+
setIsLoading(false);
|
|
47
|
+
},
|
|
48
|
+
handleError: () => setIsLoading(false),
|
|
49
|
+
errorToast: ({ response }) =>
|
|
50
|
+
// eslint-disable-next-line camelcase
|
|
51
|
+
response?.data?.error?.full_messages?.[0] || response,
|
|
45
52
|
})
|
|
46
53
|
);
|
|
54
|
+
};
|
|
47
55
|
|
|
48
56
|
return (
|
|
49
|
-
<
|
|
57
|
+
<Modal
|
|
58
|
+
position="top"
|
|
59
|
+
variant={ModalVariant.medium}
|
|
60
|
+
isOpen={isOpen}
|
|
61
|
+
onClose={onCancel}
|
|
50
62
|
id={WEBHOOK_CREATE_MODAL_ID}
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
ouiaId={WEBHOOK_CREATE_MODAL_ID}
|
|
64
|
+
title={__('Create Webhook')}
|
|
53
65
|
>
|
|
54
|
-
<Modal.Header>
|
|
55
|
-
<Modal.Title>{__('Create Webhook')}</Modal.Title>
|
|
56
|
-
</Modal.Header>
|
|
57
66
|
<ConnectedWebhookForm
|
|
67
|
+
isLoading={isLoading}
|
|
58
68
|
handleSubmit={handleSubmit}
|
|
59
69
|
initialValues={initialWebhookValues}
|
|
60
70
|
onCancel={onCancel}
|
|
61
71
|
/>
|
|
62
|
-
</
|
|
72
|
+
</Modal>
|
|
63
73
|
);
|
|
64
74
|
};
|
|
65
75
|
|
|
66
76
|
WebhookCreateModal.propTypes = {
|
|
67
77
|
onSuccess: PropTypes.func.isRequired,
|
|
68
78
|
onCancel: PropTypes.func.isRequired,
|
|
79
|
+
isOpen: PropTypes.bool.isRequired,
|
|
69
80
|
};
|
|
70
81
|
|
|
71
82
|
export default WebhookCreateModal;
|
data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookDeleteModal.js
CHANGED
|
@@ -1,40 +1,74 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
|
|
4
|
+
import { APIActions } from 'foremanReact/redux/API';
|
|
5
|
+
import { useDispatch } from 'react-redux';
|
|
4
6
|
import { sprintf, translate as __ } from 'foremanReact/common/I18n';
|
|
5
|
-
import ForemanModal from 'foremanReact/components/ForemanModal';
|
|
6
7
|
import { foremanUrl } from 'foremanReact/common/helpers';
|
|
8
|
+
import { Modal, Button, ModalVariant } from '@patternfly/react-core';
|
|
7
9
|
|
|
8
10
|
import { WEBHOOK_DELETE_MODAL_ID } from '../../constants';
|
|
9
11
|
|
|
10
|
-
const WebhookDeleteModal = ({ toDelete, onSuccess }) => {
|
|
12
|
+
const WebhookDeleteModal = ({ toDelete, onSuccess, modalState }) => {
|
|
11
13
|
const { id, name } = toDelete;
|
|
12
14
|
|
|
15
|
+
const dispatch = useDispatch();
|
|
16
|
+
const handleSubmit = () =>
|
|
17
|
+
dispatch(
|
|
18
|
+
APIActions.delete({
|
|
19
|
+
url: foremanUrl(`/api/v2/webhooks/${id}`),
|
|
20
|
+
key: WEBHOOK_DELETE_MODAL_ID,
|
|
21
|
+
successToast: () =>
|
|
22
|
+
sprintf(__('Webhook %s was successfully deleted'), name),
|
|
23
|
+
errorToast: response =>
|
|
24
|
+
// eslint-disable-next-line camelcase
|
|
25
|
+
response?.response?.data?.error?.full_messages?.[0] || response,
|
|
26
|
+
handleSuccess: onSuccess,
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
|
|
13
30
|
return (
|
|
14
|
-
<
|
|
31
|
+
<Modal
|
|
32
|
+
position="top"
|
|
33
|
+
variant={ModalVariant.small}
|
|
15
34
|
id={WEBHOOK_DELETE_MODAL_ID}
|
|
35
|
+
ouiaId={WEBHOOK_DELETE_MODAL_ID}
|
|
16
36
|
title={__('Confirm Webhook Deletion')}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
isOpen={modalState.isOpen}
|
|
38
|
+
onClose={modalState.closeModal}
|
|
39
|
+
description={sprintf(
|
|
40
|
+
__('You are about to delete %s. Are you sure?'),
|
|
41
|
+
name
|
|
42
|
+
)}
|
|
43
|
+
actions={[
|
|
44
|
+
<Button
|
|
45
|
+
ouiaId="submitBtn"
|
|
46
|
+
key="confirm"
|
|
47
|
+
variant="danger"
|
|
48
|
+
onClick={handleSubmit}
|
|
49
|
+
>
|
|
50
|
+
{__('Delete')}
|
|
51
|
+
</Button>,
|
|
52
|
+
<Button
|
|
53
|
+
ouiaId="cancelBtn"
|
|
54
|
+
key="cancel"
|
|
55
|
+
variant="link"
|
|
56
|
+
onClick={modalState.closeModal}
|
|
57
|
+
>
|
|
58
|
+
{__('Cancel')}
|
|
59
|
+
</Button>,
|
|
60
|
+
]}
|
|
61
|
+
/>
|
|
32
62
|
);
|
|
33
63
|
};
|
|
34
64
|
|
|
35
65
|
WebhookDeleteModal.propTypes = {
|
|
36
66
|
toDelete: PropTypes.object,
|
|
37
67
|
onSuccess: PropTypes.func.isRequired,
|
|
68
|
+
modalState: PropTypes.shape({
|
|
69
|
+
isOpen: PropTypes.bool.isRequired,
|
|
70
|
+
closeModal: PropTypes.func.isRequired,
|
|
71
|
+
}).isRequired,
|
|
38
72
|
};
|
|
39
73
|
|
|
40
74
|
WebhookDeleteModal.defaultProps = {
|