foreman_webhooks 5.0.1 → 5.0.2
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/version.rb +1 -1
- data/test/integration/webhooks_test.rb +102 -0
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/FieldConstructor.js +6 -15
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.js +5 -0
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/__tests__/FieldConstructor.test.js +2 -2
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/WebhookForm.js +14 -4
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/index.js +8 -3
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookCreateModal.js +4 -5
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookDeleteModal.js +8 -2
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookEditModal.js +9 -9
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/index.js +12 -3
- data/webpack/ForemanWebhooks/Routes/Webhooks/constants.js +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b0e6639b74aa9598f8a7961d48dd5c2bf81714bb6f6111e9f9bbb65a2393a4d2
|
|
4
|
+
data.tar.gz: fbe6d793527bf76fd9c3d73e357c7a4da87e3a77b1a8636652569e47955ef928
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7de4a54540a300b0d0f5de39b79275074c403f4d74ccf1a5d53fa22f77ae6bfba2788f26824b8642608da39ef09e4bd99be88fdf7a615e527cb1070066730631
|
|
7
|
+
data.tar.gz: 752cb5d63594e68e313b9a91a4fce2955a0fce47ad51a25351c0f9c43df4104ad2a456dd1e9651b2d7b51a65daef196c1f39ddf726e7b701ad146455238b00d0
|
|
@@ -118,6 +118,101 @@ class WebhooksIntegrationTest < IntegrationTestWithJavascript
|
|
|
118
118
|
assert webhook.password.present?, 'Password was cleared after update'
|
|
119
119
|
end
|
|
120
120
|
|
|
121
|
+
test 'edit webhook modal shows selected webhook instead of previous one' do
|
|
122
|
+
first_webhook = FactoryBot.create(:webhook,
|
|
123
|
+
name: 'FirstWebhook',
|
|
124
|
+
target_url: 'http://example.com/webhook1',
|
|
125
|
+
http_method: 'POST',
|
|
126
|
+
webhook_template: webhook_template)
|
|
127
|
+
second_webhook = FactoryBot.create(:webhook,
|
|
128
|
+
name: 'SecondWebhook',
|
|
129
|
+
target_url: 'http://example.com/webhook2',
|
|
130
|
+
http_method: 'POST',
|
|
131
|
+
webhook_template: webhook_template)
|
|
132
|
+
|
|
133
|
+
visit '/webhooks'
|
|
134
|
+
wait_for_ajax
|
|
135
|
+
click_webhook_name(first_webhook.name)
|
|
136
|
+
assert_selector '.pf-v5-c-modal-box'
|
|
137
|
+
click_cancel_button
|
|
138
|
+
wait_for_ajax
|
|
139
|
+
click_webhook_name(second_webhook.name)
|
|
140
|
+
assert_selector '.pf-v5-c-modal-box'
|
|
141
|
+
assert_field_value('id-name', second_webhook.name)
|
|
142
|
+
click_cancel_button
|
|
143
|
+
assert_no_selector '.pf-v5-c-modal-box'
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
test 'should update password' do
|
|
147
|
+
webhook = FactoryBot.create(:webhook,
|
|
148
|
+
name: 'PasswordUpdateWebhook',
|
|
149
|
+
target_url: 'http://example.com/webhook',
|
|
150
|
+
http_method: 'POST',
|
|
151
|
+
webhook_template: webhook_template,
|
|
152
|
+
password: 'oldpassword123')
|
|
153
|
+
new_password = 'newpassword456'
|
|
154
|
+
visit '/webhooks'
|
|
155
|
+
wait_for_ajax
|
|
156
|
+
click_webhook_name(webhook.name)
|
|
157
|
+
assert_selector '.pf-v5-c-modal-box'
|
|
158
|
+
click_tab('Credentials')
|
|
159
|
+
click_change_password_button
|
|
160
|
+
fill_in 'id-password', with: new_password
|
|
161
|
+
click_submit_button
|
|
162
|
+
wait_for_success_toast
|
|
163
|
+
webhook.reload
|
|
164
|
+
assert_equal new_password, webhook.password, 'Password was not updated in database'
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
test 'edit submission triggers PUT /api/webhooks/id and GET /api/webhooks only' do
|
|
168
|
+
first_webhook = FactoryBot.create(:webhook,
|
|
169
|
+
name: 'FirstWebhook',
|
|
170
|
+
target_url: 'http://example.com/webhook1',
|
|
171
|
+
http_method: 'POST',
|
|
172
|
+
webhook_template: webhook_template)
|
|
173
|
+
second_webhook = FactoryBot.create(:webhook,
|
|
174
|
+
name: 'SecondWebhook',
|
|
175
|
+
target_url: 'http://example.com/webhook2',
|
|
176
|
+
http_method: 'POST',
|
|
177
|
+
webhook_template: webhook_template)
|
|
178
|
+
|
|
179
|
+
visit '/webhooks'
|
|
180
|
+
wait_for_ajax
|
|
181
|
+
click_webhook_name(first_webhook.name)
|
|
182
|
+
assert_selector '.pf-v5-c-modal-box'
|
|
183
|
+
|
|
184
|
+
click_cancel_button
|
|
185
|
+
wait_for_ajax
|
|
186
|
+
click_webhook_name(second_webhook.name)
|
|
187
|
+
assert_selector '.pf-v5-c-modal-box'
|
|
188
|
+
|
|
189
|
+
fill_in 'id-name', with: '', fill_options: { clear: :backspace }
|
|
190
|
+
fill_in 'id-name', with: 'UpdatedAPITestWebhook'
|
|
191
|
+
|
|
192
|
+
api_requests = []
|
|
193
|
+
callback = lambda { |_name, _started, _finished, _unique_id, payload|
|
|
194
|
+
api_requests << { method: payload[:method], controller: payload[:controller], action: payload[:action], path: payload[:path] } if payload[:controller]&.start_with?('Api::')
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
ActiveSupport::Notifications.subscribed(callback, 'process_action.action_controller') do
|
|
198
|
+
click_submit_button
|
|
199
|
+
wait_for_success_toast
|
|
200
|
+
wait_for_ajax
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
assert api_requests.any? { |r| r[:method] == 'PUT' && r[:controller] == 'Api::V2::WebhooksController' && r[:action] == 'update' },
|
|
204
|
+
"Expected PUT webhooks#update, got: #{api_requests.inspect}"
|
|
205
|
+
assert api_requests.any? { |r| r[:method] == 'GET' && r[:controller] == 'Api::V2::WebhooksController' && r[:action] == 'index' },
|
|
206
|
+
"Expected GET webhooks#index, got: #{api_requests.inspect}"
|
|
207
|
+
|
|
208
|
+
assert api_requests.none? { |r| r[:controller] == 'Api::V2::WebhooksController' && r[:action] == 'events' },
|
|
209
|
+
"Unexpected call to webhooks#events: #{api_requests.inspect}"
|
|
210
|
+
assert api_requests.none? { |r| r[:controller] == 'Api::V2::WebhookTemplatesController' },
|
|
211
|
+
"Unexpected call to webhook_templates: #{api_requests.inspect}"
|
|
212
|
+
assert api_requests.none? { |r| r[:controller] == 'Api::V2::WebhooksController' && r[:action] == 'show' },
|
|
213
|
+
"Unexpected call to webhooks#show: #{api_requests.inspect}"
|
|
214
|
+
end
|
|
215
|
+
|
|
121
216
|
test 'delete webhook via UI' do
|
|
122
217
|
webhook = FactoryBot.create(:webhook,
|
|
123
218
|
name: 'WebhookToDelete',
|
|
@@ -190,6 +285,13 @@ class WebhooksIntegrationTest < IntegrationTestWithJavascript
|
|
|
190
285
|
page.execute_script('arguments[0].click()', button.native)
|
|
191
286
|
end
|
|
192
287
|
|
|
288
|
+
def click_change_password_button
|
|
289
|
+
modal = find('.pf-v5-c-modal-box', wait: 10)
|
|
290
|
+
button = modal.find(:ouia_component_id, 'reset-password', wait: 10, visible: :all)
|
|
291
|
+
page.execute_script('arguments[0].scrollIntoView({block: "center"})', button.native)
|
|
292
|
+
page.execute_script('arguments[0].click()', button.native)
|
|
293
|
+
end
|
|
294
|
+
|
|
193
295
|
def click_webhook_name(name)
|
|
194
296
|
row = find(:xpath, "//tbody//tr[contains(., '#{name}')]", wait: 15, match: :first)
|
|
195
297
|
within(row) do
|
data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/FieldConstructor.js
CHANGED
|
@@ -28,7 +28,6 @@ const FormField = ({
|
|
|
28
28
|
type,
|
|
29
29
|
required,
|
|
30
30
|
options,
|
|
31
|
-
isLoading,
|
|
32
31
|
validated,
|
|
33
32
|
value,
|
|
34
33
|
disabled,
|
|
@@ -36,11 +35,11 @@ const FormField = ({
|
|
|
36
35
|
placeholder,
|
|
37
36
|
errMsg,
|
|
38
37
|
fieldId,
|
|
38
|
+
setIsPasswordDisabled,
|
|
39
39
|
...props
|
|
40
40
|
}) => {
|
|
41
41
|
const [fieldValidated, setFieldValidated] = useState('default');
|
|
42
42
|
const [firstLoad, setFirstLoad] = useState(true);
|
|
43
|
-
const [isDisabled, setIsDisabled] = useState(disabled);
|
|
44
43
|
|
|
45
44
|
const requiredValidate = () => {
|
|
46
45
|
if (firstLoad || !required) return;
|
|
@@ -57,10 +56,6 @@ const FormField = ({
|
|
|
57
56
|
setFirstLoad(false);
|
|
58
57
|
}, []);
|
|
59
58
|
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
setIsDisabled(disabled);
|
|
62
|
-
}, [disabled]);
|
|
63
|
-
|
|
64
59
|
useEffect(() => {
|
|
65
60
|
requiredValidate();
|
|
66
61
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -76,7 +71,7 @@ const FormField = ({
|
|
|
76
71
|
onChange={(_, newValue) => {
|
|
77
72
|
setValue(name, newValue);
|
|
78
73
|
}}
|
|
79
|
-
isDisabled={
|
|
74
|
+
isDisabled={disabled}
|
|
80
75
|
isRequired={required}
|
|
81
76
|
type={type}
|
|
82
77
|
validated={fieldValidated}
|
|
@@ -94,7 +89,7 @@ const FormField = ({
|
|
|
94
89
|
value={value ?? ''}
|
|
95
90
|
onChange={(_, newValue) => setValue(name, newValue)}
|
|
96
91
|
isRequired={required}
|
|
97
|
-
isDisabled={
|
|
92
|
+
isDisabled={disabled}
|
|
98
93
|
type={type}
|
|
99
94
|
validated={fieldValidated}
|
|
100
95
|
placeholder={placeholder}
|
|
@@ -121,7 +116,7 @@ const FormField = ({
|
|
|
121
116
|
}
|
|
122
117
|
return (
|
|
123
118
|
<>
|
|
124
|
-
{name === 'password' &&
|
|
119
|
+
{name === 'password' && disabled && setIsPasswordDisabled ? (
|
|
125
120
|
<Grid hasGutter={false}>
|
|
126
121
|
<GridItem span={11}>
|
|
127
122
|
<TextInput
|
|
@@ -129,7 +124,7 @@ const FormField = ({
|
|
|
129
124
|
value={value ?? ''}
|
|
130
125
|
id={fieldId ?? `id-${name}`}
|
|
131
126
|
onChange={localHandler}
|
|
132
|
-
isDisabled={
|
|
127
|
+
isDisabled={disabled}
|
|
133
128
|
isRequired={required}
|
|
134
129
|
type={type}
|
|
135
130
|
validated={fieldValidated}
|
|
@@ -141,7 +136,7 @@ const FormField = ({
|
|
|
141
136
|
<GridItem span={1}>
|
|
142
137
|
<Button
|
|
143
138
|
ouiaId={`reset-${name}`}
|
|
144
|
-
onClick={() =>
|
|
139
|
+
onClick={() => setIsPasswordDisabled(!disabled)}
|
|
145
140
|
variant="control"
|
|
146
141
|
icon={<PencilAltIcon />}
|
|
147
142
|
/>
|
|
@@ -153,11 +148,9 @@ const FormField = ({
|
|
|
153
148
|
value={value ?? ''}
|
|
154
149
|
id={fieldId ?? `id-${name}`}
|
|
155
150
|
onChange={localHandler}
|
|
156
|
-
isDisabled={isLoading || isDisabled}
|
|
157
151
|
isRequired={required}
|
|
158
152
|
type={type}
|
|
159
153
|
validated={fieldValidated}
|
|
160
|
-
placeholder={placeholder}
|
|
161
154
|
onBlur={requiredValidate}
|
|
162
155
|
autoComplete={type === 'password' ? 'new-password' : null}
|
|
163
156
|
/>
|
|
@@ -283,7 +276,6 @@ FormField.propTypes = {
|
|
|
283
276
|
label: PropTypes.string.isRequired,
|
|
284
277
|
})
|
|
285
278
|
),
|
|
286
|
-
isLoading: PropTypes.bool,
|
|
287
279
|
validated: PropTypes.string,
|
|
288
280
|
placeholder: PropTypes.string,
|
|
289
281
|
errMsg: PropTypes.string,
|
|
@@ -303,7 +295,6 @@ FormField.defaultProps = {
|
|
|
303
295
|
required: false,
|
|
304
296
|
allowClear: false,
|
|
305
297
|
options: [],
|
|
306
|
-
isLoading: false,
|
|
307
298
|
disabled: false,
|
|
308
299
|
value: '',
|
|
309
300
|
fieldId: undefined,
|
data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.js
CHANGED
|
@@ -18,6 +18,7 @@ const WebhookFormTabs = ({
|
|
|
18
18
|
isTemplatesLoading,
|
|
19
19
|
isEventsLoading,
|
|
20
20
|
isPasswordDisabled,
|
|
21
|
+
setIsPasswordDisabled,
|
|
21
22
|
urlValidated,
|
|
22
23
|
}) => {
|
|
23
24
|
const updateFieldValue = (key, value) => {
|
|
@@ -141,6 +142,8 @@ const WebhookFormTabs = ({
|
|
|
141
142
|
labelHelp={__('Authentication credentials')}
|
|
142
143
|
disabled={isPasswordDisabled}
|
|
143
144
|
setValue={updateFieldValue}
|
|
145
|
+
setIsPasswordDisabled={setIsPasswordDisabled}
|
|
146
|
+
placeholder="********"
|
|
144
147
|
/>
|
|
145
148
|
<FieldConstructor
|
|
146
149
|
name="verify_ssl"
|
|
@@ -218,11 +221,13 @@ WebhookFormTabs.propTypes = {
|
|
|
218
221
|
isTemplatesLoading: PropTypes.bool.isRequired,
|
|
219
222
|
isEventsLoading: PropTypes.bool.isRequired,
|
|
220
223
|
isPasswordDisabled: PropTypes.bool,
|
|
224
|
+
setIsPasswordDisabled: PropTypes.func,
|
|
221
225
|
urlValidated: PropTypes.func.isRequired,
|
|
222
226
|
};
|
|
223
227
|
|
|
224
228
|
WebhookFormTabs.defaultProps = {
|
|
225
229
|
isPasswordDisabled: false,
|
|
230
|
+
setIsPasswordDisabled: null,
|
|
226
231
|
};
|
|
227
232
|
|
|
228
233
|
export default WebhookFormTabs;
|
|
@@ -60,10 +60,10 @@ describe('FieldConstructor RTL Tests', () => {
|
|
|
60
60
|
);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
test('renders as
|
|
63
|
+
test('renders as enabled when loading', () => {
|
|
64
64
|
render(<FieldConstructor {...defaultProps} type="text" isLoading />);
|
|
65
65
|
|
|
66
|
-
expect(document.getElementById('id-test-field')).
|
|
66
|
+
expect(document.getElementById('id-test-field')).toBeEnabled();
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
69
|
|
|
@@ -8,7 +8,6 @@ import { HTTP_METHODS } from './constants';
|
|
|
8
8
|
|
|
9
9
|
const WebhookForm = ({
|
|
10
10
|
onCancel,
|
|
11
|
-
isLoading,
|
|
12
11
|
handleSubmit,
|
|
13
12
|
initialValues,
|
|
14
13
|
templates,
|
|
@@ -16,6 +15,8 @@ const WebhookForm = ({
|
|
|
16
15
|
isTemplatesLoading,
|
|
17
16
|
isEventsLoading,
|
|
18
17
|
isPasswordDisabled,
|
|
18
|
+
setIsPasswordDisabled,
|
|
19
|
+
isSubmitting,
|
|
19
20
|
}) => {
|
|
20
21
|
const webhookTemplates = templates.map(t => ({ value: t.id, label: t.name }));
|
|
21
22
|
|
|
@@ -71,18 +72,24 @@ const WebhookForm = ({
|
|
|
71
72
|
isEventsLoading={isEventsLoading}
|
|
72
73
|
isTemplatesLoading={isTemplatesLoading}
|
|
73
74
|
isPasswordDisabled={isPasswordDisabled}
|
|
75
|
+
setIsPasswordDisabled={setIsPasswordDisabled}
|
|
74
76
|
urlValidated={urlValidated}
|
|
75
77
|
/>
|
|
76
78
|
<ActionGroup>
|
|
77
79
|
<Button
|
|
78
80
|
ouiaId="submit-webhook-form"
|
|
79
|
-
isDisabled={verifyFields() ||
|
|
81
|
+
isDisabled={verifyFields() || isSubmitting}
|
|
80
82
|
variant="primary"
|
|
81
83
|
onClick={() => handleSubmit(inputValues)}
|
|
82
84
|
>
|
|
83
85
|
{__('Submit')}
|
|
84
86
|
</Button>
|
|
85
|
-
<Button
|
|
87
|
+
<Button
|
|
88
|
+
ouiaId="cancel-webhook-form"
|
|
89
|
+
variant="link"
|
|
90
|
+
onClick={onCancel}
|
|
91
|
+
isDisabled={isSubmitting}
|
|
92
|
+
>
|
|
86
93
|
{__('Cancel')}
|
|
87
94
|
</Button>
|
|
88
95
|
</ActionGroup>
|
|
@@ -91,7 +98,6 @@ const WebhookForm = ({
|
|
|
91
98
|
};
|
|
92
99
|
|
|
93
100
|
WebhookForm.propTypes = {
|
|
94
|
-
isLoading: PropTypes.bool.isRequired,
|
|
95
101
|
onCancel: PropTypes.func.isRequired,
|
|
96
102
|
handleSubmit: PropTypes.func.isRequired,
|
|
97
103
|
initialValues: PropTypes.object.isRequired,
|
|
@@ -100,10 +106,14 @@ WebhookForm.propTypes = {
|
|
|
100
106
|
isEventsLoading: PropTypes.bool.isRequired,
|
|
101
107
|
isTemplatesLoading: PropTypes.bool.isRequired,
|
|
102
108
|
isPasswordDisabled: PropTypes.bool,
|
|
109
|
+
setIsPasswordDisabled: PropTypes.func,
|
|
110
|
+
isSubmitting: PropTypes.bool,
|
|
103
111
|
};
|
|
104
112
|
|
|
105
113
|
WebhookForm.defaultProps = {
|
|
106
114
|
isPasswordDisabled: false,
|
|
115
|
+
setIsPasswordDisabled: null,
|
|
116
|
+
isSubmitting: false,
|
|
107
117
|
};
|
|
108
118
|
|
|
109
119
|
export default WebhookForm;
|
|
@@ -25,11 +25,12 @@ import {
|
|
|
25
25
|
const params = { page: 1, search: 'snippet = false', per_page: 'all' };
|
|
26
26
|
|
|
27
27
|
const ConnectedWebhookForm = ({
|
|
28
|
-
isLoading,
|
|
29
28
|
onCancel,
|
|
30
29
|
handleSubmit,
|
|
31
30
|
initialValues,
|
|
32
31
|
isPasswordDisabled,
|
|
32
|
+
setIsPasswordDisabled,
|
|
33
|
+
isSubmitting,
|
|
33
34
|
}) => {
|
|
34
35
|
const dispatch = useDispatch();
|
|
35
36
|
|
|
@@ -59,7 +60,6 @@ const ConnectedWebhookForm = ({
|
|
|
59
60
|
|
|
60
61
|
return (
|
|
61
62
|
<WebhookForm
|
|
62
|
-
isLoading={isLoading}
|
|
63
63
|
templates={templates}
|
|
64
64
|
availableEvents={availableEvents}
|
|
65
65
|
onCancel={onCancel}
|
|
@@ -68,20 +68,25 @@ const ConnectedWebhookForm = ({
|
|
|
68
68
|
isTemplatesLoading={isTemplatesLoading}
|
|
69
69
|
isEventsLoading={isEventsLoading}
|
|
70
70
|
isPasswordDisabled={isPasswordDisabled}
|
|
71
|
+
setIsPasswordDisabled={setIsPasswordDisabled}
|
|
72
|
+
isSubmitting={isSubmitting}
|
|
71
73
|
/>
|
|
72
74
|
);
|
|
73
75
|
};
|
|
74
76
|
|
|
75
77
|
ConnectedWebhookForm.propTypes = {
|
|
76
|
-
isLoading: PropTypes.bool.isRequired,
|
|
77
78
|
onCancel: PropTypes.func.isRequired,
|
|
78
79
|
handleSubmit: PropTypes.func.isRequired,
|
|
79
80
|
initialValues: PropTypes.object.isRequired,
|
|
80
81
|
isPasswordDisabled: PropTypes.bool,
|
|
82
|
+
setIsPasswordDisabled: PropTypes.func,
|
|
83
|
+
isSubmitting: PropTypes.bool,
|
|
81
84
|
};
|
|
82
85
|
|
|
83
86
|
ConnectedWebhookForm.defaultProps = {
|
|
84
87
|
isPasswordDisabled: false,
|
|
88
|
+
setIsPasswordDisabled: null,
|
|
89
|
+
isSubmitting: false,
|
|
85
90
|
};
|
|
86
91
|
|
|
87
92
|
export default ConnectedWebhookForm;
|
data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookCreateModal.js
CHANGED
|
@@ -15,8 +15,8 @@ import './WebhookModal.scss';
|
|
|
15
15
|
|
|
16
16
|
const WebhookCreateModal = ({ onSuccess, onCancel, isOpen }) => {
|
|
17
17
|
const dispatch = useDispatch();
|
|
18
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
18
19
|
|
|
19
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
20
20
|
const initialWebhookValues = {
|
|
21
21
|
name: '',
|
|
22
22
|
target_url: '',
|
|
@@ -34,7 +34,7 @@ const WebhookCreateModal = ({ onSuccess, onCancel, isOpen }) => {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
const handleSubmit = values => {
|
|
37
|
-
|
|
37
|
+
setIsSubmitting(true);
|
|
38
38
|
dispatch(
|
|
39
39
|
APIActions.post({
|
|
40
40
|
url: foremanUrl(`/api${WEBHOOKS_PATH}`),
|
|
@@ -43,9 +43,8 @@ const WebhookCreateModal = ({ onSuccess, onCancel, isOpen }) => {
|
|
|
43
43
|
successToast: () => __('Webhook was successfully created.'),
|
|
44
44
|
handleSuccess: () => {
|
|
45
45
|
onSuccess();
|
|
46
|
-
setIsLoading(false);
|
|
47
46
|
},
|
|
48
|
-
handleError: () =>
|
|
47
|
+
handleError: () => setIsSubmitting(false),
|
|
49
48
|
errorToast: ({ response }) =>
|
|
50
49
|
// eslint-disable-next-line camelcase
|
|
51
50
|
response?.data?.error?.full_messages?.[0] || response,
|
|
@@ -64,10 +63,10 @@ const WebhookCreateModal = ({ onSuccess, onCancel, isOpen }) => {
|
|
|
64
63
|
title={__('Create Webhook')}
|
|
65
64
|
>
|
|
66
65
|
<ConnectedWebhookForm
|
|
67
|
-
isLoading={isLoading}
|
|
68
66
|
handleSubmit={handleSubmit}
|
|
69
67
|
initialValues={initialWebhookValues}
|
|
70
68
|
onCancel={onCancel}
|
|
69
|
+
isSubmitting={isSubmitting}
|
|
71
70
|
/>
|
|
72
71
|
</Modal>
|
|
73
72
|
);
|
data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookDeleteModal.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
|
|
4
4
|
import { APIActions } from 'foremanReact/redux/API';
|
|
@@ -12,8 +12,10 @@ import { WEBHOOK_DELETE_MODAL_ID } from '../../constants';
|
|
|
12
12
|
const WebhookDeleteModal = ({ toDelete, onSuccess, modalState }) => {
|
|
13
13
|
const { id, name } = toDelete;
|
|
14
14
|
|
|
15
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
15
16
|
const dispatch = useDispatch();
|
|
16
|
-
const handleSubmit = () =>
|
|
17
|
+
const handleSubmit = () => {
|
|
18
|
+
setIsSubmitting(true);
|
|
17
19
|
dispatch(
|
|
18
20
|
APIActions.delete({
|
|
19
21
|
url: foremanUrl(`/api/v2/webhooks/${id}`),
|
|
@@ -24,8 +26,10 @@ const WebhookDeleteModal = ({ toDelete, onSuccess, modalState }) => {
|
|
|
24
26
|
// eslint-disable-next-line camelcase
|
|
25
27
|
response?.response?.data?.error?.full_messages?.[0] || response,
|
|
26
28
|
handleSuccess: onSuccess,
|
|
29
|
+
handleError: () => setIsSubmitting(false),
|
|
27
30
|
})
|
|
28
31
|
);
|
|
32
|
+
};
|
|
29
33
|
|
|
30
34
|
return (
|
|
31
35
|
<Modal
|
|
@@ -45,6 +49,7 @@ const WebhookDeleteModal = ({ toDelete, onSuccess, modalState }) => {
|
|
|
45
49
|
ouiaId="submitBtn"
|
|
46
50
|
key="confirm"
|
|
47
51
|
variant="danger"
|
|
52
|
+
isDisabled={isSubmitting}
|
|
48
53
|
onClick={handleSubmit}
|
|
49
54
|
>
|
|
50
55
|
{__('Delete')}
|
|
@@ -53,6 +58,7 @@ const WebhookDeleteModal = ({ toDelete, onSuccess, modalState }) => {
|
|
|
53
58
|
ouiaId="cancelBtn"
|
|
54
59
|
key="cancel"
|
|
55
60
|
variant="link"
|
|
61
|
+
isDisabled={isSubmitting}
|
|
56
62
|
onClick={modalState.closeModal}
|
|
57
63
|
>
|
|
58
64
|
{__('Cancel')}
|
data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookEditModal.js
CHANGED
|
@@ -15,9 +15,11 @@ import {
|
|
|
15
15
|
WEBHOOK_API_REQUEST_KEY,
|
|
16
16
|
WEBHOOK_EDIT_MODAL_ID,
|
|
17
17
|
WEBHOOKS_API_PLAIN_PATH,
|
|
18
|
+
WEBHOOK_API_UPDATE_KEY,
|
|
18
19
|
} from '../../constants';
|
|
19
20
|
|
|
20
21
|
import {
|
|
22
|
+
selectIsLoading,
|
|
21
23
|
selectWebhookValues,
|
|
22
24
|
selectWebhookTemplateId,
|
|
23
25
|
} from './WebhookEditModalSelectors';
|
|
@@ -27,8 +29,10 @@ import './WebhookModal.scss';
|
|
|
27
29
|
const WebhookEditModal = ({ toEdit, onSuccess, modalState }) => {
|
|
28
30
|
const dispatch = useDispatch();
|
|
29
31
|
|
|
32
|
+
const isLoading = useSelector(selectIsLoading);
|
|
33
|
+
|
|
34
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
30
35
|
const [isPasswordDisabled, setIsPasswordDisabled] = useState(false);
|
|
31
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
32
36
|
const id = toEdit;
|
|
33
37
|
|
|
34
38
|
const isPasswordSet = useSelector(selectWebhookValues).passwordSet;
|
|
@@ -54,20 +58,20 @@ const WebhookEditModal = ({ toEdit, onSuccess, modalState }) => {
|
|
|
54
58
|
}, [isPasswordSet]);
|
|
55
59
|
|
|
56
60
|
const handleSubmit = values => {
|
|
57
|
-
|
|
61
|
+
setIsSubmitting(true);
|
|
58
62
|
if (isPasswordDisabled) {
|
|
59
63
|
delete values.password;
|
|
60
64
|
}
|
|
61
65
|
dispatch(
|
|
62
66
|
put({
|
|
63
67
|
url: foremanUrl(`/api${WEBHOOKS_PATH}/${id}`),
|
|
64
|
-
key:
|
|
68
|
+
key: WEBHOOK_API_UPDATE_KEY,
|
|
65
69
|
params: { ...values, controller: 'webhooks' },
|
|
66
70
|
successToast: () => __('Webhook was successfully updated.'),
|
|
67
71
|
handleSuccess: () => {
|
|
68
72
|
onSuccess();
|
|
69
|
-
setIsLoading(false);
|
|
70
73
|
},
|
|
74
|
+
handleError: () => setIsSubmitting(false),
|
|
71
75
|
errorToast: ({ response }) =>
|
|
72
76
|
// eslint-disable-next-line camelcase
|
|
73
77
|
response?.data?.error?.full_messages?.[0] || response,
|
|
@@ -75,10 +79,6 @@ const WebhookEditModal = ({ toEdit, onSuccess, modalState }) => {
|
|
|
75
79
|
);
|
|
76
80
|
};
|
|
77
81
|
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
if (initialWebhookValues.id) setIsLoading(false);
|
|
80
|
-
}, [initialWebhookValues.id]);
|
|
81
|
-
|
|
82
82
|
useEffect(() => {
|
|
83
83
|
if (id) {
|
|
84
84
|
dispatch(
|
|
@@ -111,12 +111,12 @@ const WebhookEditModal = ({ toEdit, onSuccess, modalState }) => {
|
|
|
111
111
|
<Loading />
|
|
112
112
|
) : (
|
|
113
113
|
<ConnectedWebhookForm
|
|
114
|
-
isLoading={isLoading}
|
|
115
114
|
handleSubmit={handleSubmit}
|
|
116
115
|
initialValues={initialWebhookValues}
|
|
117
116
|
onCancel={onEditCancel}
|
|
118
117
|
isPasswordDisabled={isPasswordDisabled}
|
|
119
118
|
setIsPasswordDisabled={setIsPasswordDisabled}
|
|
119
|
+
isSubmitting={isSubmitting}
|
|
120
120
|
/>
|
|
121
121
|
)}
|
|
122
122
|
</Modal>
|
data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/index.js
CHANGED
|
@@ -41,15 +41,24 @@ const WrappedWebhooksTable = props => {
|
|
|
41
41
|
modalsStates={{
|
|
42
42
|
testModal: {
|
|
43
43
|
isOpen: isTestModalOpen,
|
|
44
|
-
closeModal: () =>
|
|
44
|
+
closeModal: () => {
|
|
45
|
+
setToTest({});
|
|
46
|
+
setIsTestModalOpen(false);
|
|
47
|
+
},
|
|
45
48
|
},
|
|
46
49
|
deleteModal: {
|
|
47
50
|
isOpen: isDeleteModalOpen,
|
|
48
|
-
closeModal: () =>
|
|
51
|
+
closeModal: () => {
|
|
52
|
+
setToDelete({});
|
|
53
|
+
setIsDeleteModalOpen(false);
|
|
54
|
+
},
|
|
49
55
|
},
|
|
50
56
|
editModal: {
|
|
51
57
|
isOpen: isEditModalOpen,
|
|
52
|
-
closeModal: () =>
|
|
58
|
+
closeModal: () => {
|
|
59
|
+
setToEdit(0);
|
|
60
|
+
setIsEditModalOpen(false);
|
|
61
|
+
},
|
|
53
62
|
},
|
|
54
63
|
}}
|
|
55
64
|
webhookActions={webhookActions}
|
|
@@ -13,6 +13,7 @@ export const WEBHOOK_TEMPLATES_API_PATH =
|
|
|
13
13
|
export const WEBHOOKS_API_PLAIN_PATH = '/api/v2/webhooks';
|
|
14
14
|
export const WEBHOOK_TEMPLATES_API_REQUEST_KEY = 'WEBHOOK_TEMPLATES';
|
|
15
15
|
export const WEBHOOK_API_REQUEST_KEY = 'WEBHOOK';
|
|
16
|
+
export const WEBHOOK_API_UPDATE_KEY = 'WEBHOOK_UPDATE';
|
|
16
17
|
export const WEBHOOK_EVENTS_API_REQUEST_KEY = 'WEBHOOK_EVENTS';
|
|
17
18
|
|
|
18
19
|
export const WEBHOOK_CREATE_MODAL_ID = 'webhookCreateModal';
|