foreman_webhooks 3.0.2 → 3.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (25) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/webhook.rb +4 -1
  3. data/app/views/api/v2/webhooks/show.json.rabl +4 -0
  4. data/lib/foreman_webhooks/engine.rb +1 -1
  5. data/lib/foreman_webhooks/version.rb +1 -1
  6. data/package.json +5 -5
  7. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/ForemanFormikField.js +36 -1
  8. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.js +8 -0
  9. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/WebhookForm.js +11 -0
  10. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/__snapshots__/WebhookForm.test.js.snap +2 -0
  11. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/index.js +16 -1
  12. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookCreateModal.js +4 -2
  13. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookEditModal.js +38 -17
  14. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/EmptyWebhooksTable/index.js +29 -0
  15. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/WebhooksTable.js +37 -18
  16. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/WebhooksIndexPage.js +26 -74
  17. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/WebhooksIndexPage.test.js +12 -2
  18. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/__snapshots__/WebhooksIndexPage.test.js.snap +10 -18
  19. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/__snapshots__/integration.test.js.snap +4 -16
  20. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/index.js +1 -41
  21. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksPageActions.js +1 -12
  22. data/webpack/ForemanWebhooks/Routes/Webhooks/constants.js +0 -3
  23. data/webpack/__mocks__/foremanReact/components/PF4/TableIndexPage/TableIndexPage.js +10 -0
  24. metadata +4 -3
  25. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/EmptyWebhooksIndexPage/index.js +0 -63
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e166211db618a40dd56da367296ff84e3ea3e43e1d7407cac3840a17eaac22ea
4
- data.tar.gz: cbfe61067b37c0172bcb02f516d28a582349c0a383b18e6b26406e58069253b5
3
+ metadata.gz: 35481704f9612f66d175bb0ad80ae7b7d09ff1ab5440cb1f8a0095e048192bb0
4
+ data.tar.gz: d2c446a00baabd248360cba079d3881c51f5041333d5d1289aaac26d5bbe66ef
5
5
  SHA512:
6
- metadata.gz: 537b2c507e961c15383bec14b3bf5e22bb13093a501310e1ba9fee8595ac4ff789d4afd8c4ac4e91832b60502f8daee14104dab87eb9b2771bd4b4ae518ba3dd
7
- data.tar.gz: 2c224320a96b0a9b55f9942152074e5063d4a5b9e5c84aca5f0d43823ce8fd8ebd91f45a595ccc694d5d09e70859eca3dc9481b4b113716bfc24971021fda39f
6
+ metadata.gz: 362b6a4ce67927ac55080d400a5fed50cd97427a4f07f71eceec9f33b3f03327cd01598ec0037eded63849b8cdf24eb007ca0bd2572c7238c1e4d66291d2356e
7
+ data.tar.gz: b49a3118a26cec78f9d4ba4856a3aba18f6af0cbfcd80fb362e9551d0a279481b858bc7a9cac1f636348385d56333d2d659e2a540f50c9eac0ecd49f24a0af95
@@ -79,7 +79,10 @@ class Webhook < ApplicationRecord
79
79
 
80
80
  def ca_certs_store
81
81
  store = OpenSSL::X509::Store.new
82
- return store if ssl_ca_certs.blank?
82
+ if ssl_ca_certs.blank?
83
+ store.set_default_paths
84
+ return store
85
+ end
83
86
 
84
87
  ssl_ca_certs.split(/(?=-----BEGIN)/).each do |cert|
85
88
  store.add_cert(OpenSSL::X509::Certificate.new(cert))
@@ -15,6 +15,10 @@ attributes :target_url,
15
15
  :ssl_ca_certs,
16
16
  :user
17
17
 
18
+ node :password_set do |webhook|
19
+ webhook.password.present?
20
+ end
21
+
18
22
  child :webhook_template do
19
23
  extends 'api/v2/webhook_templates/base'
20
24
  end
@@ -15,7 +15,7 @@ module ForemanWebhooks
15
15
 
16
16
  initializer 'foreman_webhooks.register_plugin', before: :finisher_hook do |_app|
17
17
  Foreman::Plugin.register :foreman_webhooks do
18
- requires_foreman '>= 3.2'
18
+ requires_foreman '>= 3.3'
19
19
 
20
20
  apipie_documented_controllers ["#{ForemanWebhooks::Engine.root}/app/controllers/api/v2/*.rb"]
21
21
  ApipieDSL.configuration.sections += ['webhooks']
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ForemanWebhooks
4
- VERSION = '3.0.2'
4
+ VERSION = '3.0.5'
5
5
  end
data/package.json CHANGED
@@ -25,11 +25,11 @@
25
25
  },
26
26
  "devDependencies": {
27
27
  "@babel/core": "^7.7.0",
28
- "@theforeman/builder": "^8.15.0",
29
- "@theforeman/stories": "^8.15.0",
30
- "@theforeman/test": "^8.15.0",
31
- "@theforeman/vendor-dev": "^8.15.0",
32
- "@theforeman/eslint-plugin-foreman": "^8.15.0",
28
+ "@theforeman/builder": ">= 0",
29
+ "@theforeman/stories": ">= 0",
30
+ "@theforeman/test": ">= 0",
31
+ "@theforeman/vendor-dev": ">= 0",
32
+ "@theforeman/eslint-plugin-foreman": ">= 0",
33
33
  "babel-eslint": "^10.0.0",
34
34
  "eslint": "^6.8.0",
35
35
  "eslint-plugin-spellcheck": "^0.0.17",
@@ -21,6 +21,8 @@ const ForemanFormikField = ({
21
21
  placeholder,
22
22
  options,
23
23
  isLoading,
24
+ disabled,
25
+ setDisabled,
24
26
  }) => (
25
27
  <FormikField name={name}>
26
28
  {({
@@ -32,6 +34,15 @@ const ForemanFormikField = ({
32
34
 
33
35
  return filter(initialOptions, o => o.value === fieldValue);
34
36
  };
37
+ const passwordInput = (
38
+ <input
39
+ {...field}
40
+ placeholder={disabled ? '********' : ''}
41
+ type={type}
42
+ disabled={disabled}
43
+ className="form-control"
44
+ />
45
+ );
35
46
  let content = null;
36
47
  switch (type) {
37
48
  case 'textarea':
@@ -56,10 +67,30 @@ const ForemanFormikField = ({
56
67
  onChange={selected =>
57
68
  setFieldValue(field.name, selected[0]?.value)
58
69
  }
59
- // onBlur={e => setFieldTouched(field.name, true)}
60
70
  />
61
71
  );
62
72
  break;
73
+ case 'password':
74
+ content = setDisabled ? (
75
+ <div className="input-group">
76
+ {passwordInput}
77
+ <span className="input-group-btn">
78
+ <button
79
+ className="btn btn-default"
80
+ onClick={e => {
81
+ e.preventDefault();
82
+ setDisabled(!disabled);
83
+ }}
84
+ title={__('Change the password')}
85
+ >
86
+ <span className="pficon pficon-edit" />
87
+ </button>
88
+ </span>
89
+ </div>
90
+ ) : (
91
+ passwordInput
92
+ );
93
+ break;
63
94
  default:
64
95
  content = (
65
96
  <input
@@ -101,6 +132,8 @@ ForemanFormikField.propTypes = {
101
132
  placeholder: PropTypes.string,
102
133
  options: PropTypes.array,
103
134
  isLoading: PropTypes.bool,
135
+ disabled: PropTypes.bool,
136
+ setDisabled: PropTypes.func,
104
137
  };
105
138
 
106
139
  ForemanFormikField.defaultProps = {
@@ -112,6 +145,8 @@ ForemanFormikField.defaultProps = {
112
145
  placeholder: '',
113
146
  options: null,
114
147
  isLoading: false,
148
+ disabled: false,
149
+ setDisabled: undefined,
115
150
  };
116
151
 
117
152
  export default ForemanFormikField;
@@ -18,6 +18,8 @@ const WebhookFormTabs = ({
18
18
  availableEvents,
19
19
  isTemplatesLoading,
20
20
  isEventsLoading,
21
+ isPasswordDisabled,
22
+ setIsPasswordDisabled,
21
23
  }) => (
22
24
  <Tabs activeKey={activeTab} onSelect={handleTabClick} isFilled>
23
25
  <Tab
@@ -90,6 +92,8 @@ const WebhookFormTabs = ({
90
92
  type="password"
91
93
  label={__('Password')}
92
94
  labelHelp={__('Authentication credentials')}
95
+ disabled={isPasswordDisabled}
96
+ setDisabled={setIsPasswordDisabled}
93
97
  />
94
98
  <ForemanFormikField
95
99
  name="verify_ssl"
@@ -153,11 +157,15 @@ WebhookFormTabs.propTypes = {
153
157
  availableEvents: PropTypes.array.isRequired,
154
158
  isTemplatesLoading: PropTypes.bool.isRequired,
155
159
  isEventsLoading: PropTypes.bool.isRequired,
160
+ isPasswordDisabled: PropTypes.bool,
161
+ setIsPasswordDisabled: PropTypes.func,
156
162
  };
157
163
 
158
164
  WebhookFormTabs.defaultProps = {
159
165
  disabled: false,
160
166
  formProps: {},
167
+ isPasswordDisabled: false,
168
+ setIsPasswordDisabled: undefined,
161
169
  };
162
170
 
163
171
  export default WebhookFormTabs;
@@ -25,6 +25,8 @@ const WebhookForm = ({
25
25
  availableEvents,
26
26
  isTemplatesLoading,
27
27
  isEventsLoading,
28
+ isPasswordDisabled,
29
+ setIsPasswordDisabled,
28
30
  }) => {
29
31
  const webhookTemplates = templates.map(t => ({ value: t.id, label: t.name }));
30
32
 
@@ -51,6 +53,8 @@ const WebhookForm = ({
51
53
  availableEvents={availableEvents}
52
54
  isEventsLoading={isEventsLoading}
53
55
  isTemplatesLoading={isTemplatesLoading}
56
+ isPasswordDisabled={isPasswordDisabled}
57
+ setIsPasswordDisabled={setIsPasswordDisabled}
54
58
  />
55
59
  </ForemanForm>
56
60
  );
@@ -64,6 +68,13 @@ WebhookForm.propTypes = {
64
68
  availableEvents: PropTypes.array.isRequired,
65
69
  isEventsLoading: PropTypes.bool.isRequired,
66
70
  isTemplatesLoading: PropTypes.bool.isRequired,
71
+ isPasswordDisabled: PropTypes.bool,
72
+ setIsPasswordDisabled: PropTypes.func,
73
+ };
74
+
75
+ WebhookForm.defaultProps = {
76
+ isPasswordDisabled: false,
77
+ setIsPasswordDisabled: undefined,
67
78
  };
68
79
 
69
80
  export default WebhookForm;
@@ -241,6 +241,7 @@ exports[`WebhookForm rendering should render for edit page 1`] = `
241
241
  ]
242
242
  }
243
243
  isEventsLoading={false}
244
+ isPasswordDisabled={false}
244
245
  isTemplatesLoading={false}
245
246
  webhookTemplates={
246
247
  Array [
@@ -492,6 +493,7 @@ exports[`WebhookForm rendering should render for new page 1`] = `
492
493
  ]
493
494
  }
494
495
  isEventsLoading={false}
496
+ isPasswordDisabled={false}
495
497
  isTemplatesLoading={false}
496
498
  webhookTemplates={
497
499
  Array [
@@ -24,7 +24,13 @@ import {
24
24
 
25
25
  const params = { page: 1, search: 'snippet = false', per_page: 'all' };
26
26
 
27
- const ConnectedWebhookForm = ({ onCancel, handleSubmit, initialValues }) => {
27
+ const ConnectedWebhookForm = ({
28
+ onCancel,
29
+ handleSubmit,
30
+ initialValues,
31
+ isPasswordDisabled,
32
+ setIsPasswordDisabled,
33
+ }) => {
28
34
  const dispatch = useDispatch();
29
35
 
30
36
  const templates = useSelector(selectWebhookTemplates);
@@ -60,6 +66,8 @@ const ConnectedWebhookForm = ({ onCancel, handleSubmit, initialValues }) => {
60
66
  initialValues={initialValues}
61
67
  isTemplatesLoading={isTemplatesLoading}
62
68
  isEventsLoading={isEventsLoading}
69
+ isPasswordDisabled={isPasswordDisabled}
70
+ setIsPasswordDisabled={setIsPasswordDisabled}
63
71
  />
64
72
  );
65
73
  };
@@ -68,6 +76,13 @@ ConnectedWebhookForm.propTypes = {
68
76
  onCancel: PropTypes.func.isRequired,
69
77
  handleSubmit: PropTypes.func.isRequired,
70
78
  initialValues: PropTypes.object.isRequired,
79
+ isPasswordDisabled: PropTypes.bool,
80
+ setIsPasswordDisabled: PropTypes.func,
81
+ };
82
+
83
+ ConnectedWebhookForm.defaultProps = {
84
+ isPasswordDisabled: false,
85
+ setIsPasswordDisabled: undefined,
71
86
  };
72
87
 
73
88
  export default ConnectedWebhookForm;
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import { Modal } from 'patternfly-react';
3
4
  import { useDispatch } from 'react-redux';
4
5
 
5
6
  import { translate as __ } from 'foremanReact/common/I18n';
@@ -47,11 +48,12 @@ const WebhookCreateModal = ({ onSuccess, onCancel }) => {
47
48
  return (
48
49
  <ForemanModal
49
50
  id={WEBHOOK_CREATE_MODAL_ID}
50
- title={__('Create Webhook')}
51
51
  backdrop="static"
52
52
  className="webhooks-modal"
53
53
  >
54
- <ForemanModal.Header />
54
+ <Modal.Header>
55
+ <Modal.Title>{__('Create Webhook')}</Modal.Title>
56
+ </Modal.Header>
55
57
  <ConnectedWebhookForm
56
58
  handleSubmit={handleSubmit}
57
59
  initialValues={initialWebhookValues}
@@ -1,4 +1,5 @@
1
- import React, { useEffect } from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
+ import { Modal } from 'patternfly-react';
2
3
  import { useSelector, useDispatch } from 'react-redux';
3
4
  import PropTypes from 'prop-types';
4
5
 
@@ -29,22 +30,11 @@ import './WebhookModal.scss';
29
30
  const WebhookEditModal = ({ toEdit, onSuccess, onCancel }) => {
30
31
  const dispatch = useDispatch();
31
32
 
33
+ const [isPasswordDisabled, setIsPasswordDisabled] = useState(false);
32
34
  const id = toEdit;
33
35
 
34
- const handleSubmit = (values, actions) =>
35
- dispatch(
36
- submitForm({
37
- url: foremanUrl(`/api${WEBHOOKS_PATH}/${id}`),
38
- values: { ...values, controller: 'webhooks' },
39
- item: 'Webhook',
40
- message: __('Webhook was successfully updated.'),
41
- method: 'put',
42
- successCallback: onSuccess,
43
- actions,
44
- })
45
- );
46
-
47
36
  const isLoading = useSelector(selectIsLoading);
37
+ const isPasswordSet = useSelector(selectWebhookValues).passwordSet;
48
38
  const initialWebhookValues = {
49
39
  id: useSelector(selectWebhookValues).id,
50
40
  name: useSelector(selectWebhookValues).name,
@@ -62,6 +52,27 @@ const WebhookEditModal = ({ toEdit, onSuccess, onCancel }) => {
62
52
  proxy_authorization: useSelector(selectWebhookValues).proxyAuthorization,
63
53
  };
64
54
 
55
+ useEffect(() => {
56
+ setIsPasswordDisabled(isPasswordSet);
57
+ }, [isPasswordSet]);
58
+
59
+ const handleSubmit = (values, actions) => {
60
+ if (isPasswordDisabled) {
61
+ delete values.password;
62
+ }
63
+ dispatch(
64
+ submitForm({
65
+ url: foremanUrl(`/api${WEBHOOKS_PATH}/${id}`),
66
+ values: { ...values, controller: 'webhooks' },
67
+ item: 'Webhook',
68
+ message: __('Webhook was successfully updated.'),
69
+ method: 'put',
70
+ successCallback: onSuccess,
71
+ actions,
72
+ })
73
+ );
74
+ };
75
+
65
76
  useEffect(() => {
66
77
  if (id) {
67
78
  dispatch(
@@ -73,21 +84,31 @@ const WebhookEditModal = ({ toEdit, onSuccess, onCancel }) => {
73
84
  }
74
85
  }, [id, dispatch]);
75
86
 
87
+ const onEditCancel = () => {
88
+ if (isPasswordSet) setIsPasswordDisabled(true);
89
+ onCancel();
90
+ };
91
+
76
92
  return (
77
93
  <ForemanModal
78
94
  id={WEBHOOK_EDIT_MODAL_ID}
79
- title={`${__('Edit')} ${initialWebhookValues.name}`}
80
95
  backdrop="static"
81
96
  className="webhooks-modal"
82
97
  >
83
- <ForemanModal.Header />
98
+ <Modal.Header>
99
+ <Modal.Title>
100
+ {`${__('Edit')} ${initialWebhookValues.name}`}
101
+ </Modal.Title>
102
+ </Modal.Header>
84
103
  {isLoading ? (
85
104
  <Loading />
86
105
  ) : (
87
106
  <ConnectedWebhookForm
88
107
  handleSubmit={handleSubmit}
89
108
  initialValues={initialWebhookValues}
90
- onCancel={onCancel}
109
+ onCancel={onEditCancel}
110
+ isPasswordDisabled={isPasswordDisabled}
111
+ setIsPasswordDisabled={setIsPasswordDisabled}
91
112
  />
92
113
  )}
93
114
  </ForemanModal>
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { translate as __ } from 'foremanReact/common/I18n';
4
+ import DefaultEmptyState from 'foremanReact/components/common/EmptyState';
5
+
6
+ const EmptyWebhooksTable = ({ message: { type, text } }) => (
7
+ <DefaultEmptyState
8
+ icon={type === 'error' ? 'error-circle-o' : 'add-circle-o'}
9
+ header={type === 'error' ? __('Error') : __('No Results')}
10
+ description={text}
11
+ documentation={null}
12
+ />
13
+ );
14
+
15
+ EmptyWebhooksTable.propTypes = {
16
+ message: PropTypes.shape({
17
+ type: PropTypes.oneOf(['empty', 'error']),
18
+ text: PropTypes.string,
19
+ }),
20
+ };
21
+
22
+ EmptyWebhooksTable.defaultProps = {
23
+ message: {
24
+ type: 'empty',
25
+ text: __('Try to create a new webhook'),
26
+ },
27
+ };
28
+
29
+ export default EmptyWebhooksTable;
@@ -1,33 +1,56 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import { useSelector } from 'react-redux';
4
+ import { isEmpty } from 'lodash';
3
5
 
4
6
  import { Table } from 'foremanReact/components/common/table';
5
7
  import Pagination from 'foremanReact/components/Pagination';
8
+ import Loading from 'foremanReact/components/Loading';
6
9
  import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
7
10
 
8
11
  import WebhookDeleteModal from '../WebhookDeleteModal';
9
12
  import WebhookEditModal from '../WebhookEditModal';
13
+ import EmptyWebhooksTable from './Components/EmptyWebhooksTable';
10
14
 
11
15
  import createWebhooksTableSchema from './WebhooksTableSchema';
12
16
 
13
17
  import { WEBHOOK_EDIT_MODAL_ID } from '../../../constants';
14
18
 
19
+ import {
20
+ selectWebhooks,
21
+ selectPage,
22
+ selectPerPage,
23
+ selectSearch,
24
+ selectSort,
25
+ selectHasData,
26
+ selectHasError,
27
+ selectIsLoading,
28
+ selectSubtotal,
29
+ selectMessage,
30
+ } from '../../../WebhooksPageSelectors';
31
+
15
32
  const WebhooksTable = ({
16
33
  fetchAndPush,
17
- itemCount,
18
- results,
19
- sort,
20
- pagination,
21
34
  toDelete,
22
35
  onDeleteClick,
23
36
  toEdit,
24
37
  onEditClick,
25
38
  reloadWithSearch,
26
- search,
27
39
  }) => {
40
+ const webhooks = useSelector(selectWebhooks);
41
+ const page = useSelector(selectPage);
42
+ const perPage = useSelector(selectPerPage);
43
+ const search = useSelector(selectSearch);
44
+ const sort = useSelector(selectSort);
45
+ const isLoading = useSelector(selectIsLoading);
46
+ const hasData = useSelector(selectHasData);
47
+ const hasError = useSelector(selectHasError);
48
+ const itemCount = useSelector(selectSubtotal);
49
+ const message = useSelector(selectMessage);
50
+
28
51
  const onDeleteSuccess = () => {
29
- const currentPage = pagination.page;
30
- const maxPage = Math.ceil((itemCount - 1) / pagination.perPage);
52
+ const currentPage = page;
53
+ const maxPage = Math.ceil((itemCount - 1) / perPage);
31
54
  fetchAndPush({ page: maxPage < currentPage ? maxPage : currentPage });
32
55
  };
33
56
 
@@ -35,6 +58,12 @@ const WebhooksTable = ({
35
58
  id: WEBHOOK_EDIT_MODAL_ID,
36
59
  });
37
60
 
61
+ if (isLoading && !hasError) return <Loading />;
62
+
63
+ if (!isLoading && !hasData && isEmpty(search)) {
64
+ return <EmptyWebhooksTable message={message} />;
65
+ }
66
+
38
67
  return (
39
68
  <React.Fragment>
40
69
  <WebhookDeleteModal toDelete={toDelete} onSuccess={onDeleteSuccess} />
@@ -55,7 +84,7 @@ const WebhooksTable = ({
55
84
  onDeleteClick,
56
85
  onEditClick
57
86
  )}
58
- rows={results}
87
+ rows={webhooks}
59
88
  id="webhooks-table"
60
89
  />
61
90
  <Pagination itemCount={itemCount} onChange={fetchAndPush} />
@@ -64,22 +93,12 @@ const WebhooksTable = ({
64
93
  };
65
94
 
66
95
  WebhooksTable.propTypes = {
67
- results: PropTypes.array.isRequired,
68
96
  fetchAndPush: PropTypes.func.isRequired,
69
97
  onDeleteClick: PropTypes.func.isRequired,
70
98
  onEditClick: PropTypes.func.isRequired,
71
- itemCount: PropTypes.number.isRequired,
72
- sort: PropTypes.object,
73
- pagination: PropTypes.object.isRequired,
74
99
  toDelete: PropTypes.object.isRequired,
75
100
  toEdit: PropTypes.number.isRequired,
76
101
  reloadWithSearch: PropTypes.func.isRequired,
77
- search: PropTypes.string,
78
- };
79
-
80
- WebhooksTable.defaultProps = {
81
- sort: { by: '', order: '' },
82
- search: '',
83
102
  };
84
103
 
85
104
  export default WebhooksTable;
@@ -1,33 +1,28 @@
1
1
  import React, { useState } from 'react';
2
- import PropTypes from 'prop-types';
3
- import { Button } from 'patternfly-react';
2
+ import { useSelector, useDispatch } from 'react-redux';
4
3
 
4
+ import TableIndexPage from 'foremanReact/components/PF4/TableIndexPage/TableIndexPage';
5
5
  import { translate as __ } from 'foremanReact/common/I18n';
6
- import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
7
6
  import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
8
- import { withRenderHandler } from 'foremanReact/common/HOC';
9
7
 
10
- import { WEBHOOKS_SEARCH_PROPS, WEBHOOK_CREATE_MODAL_ID } from '../constants';
8
+ import {
9
+ WEBHOOKS_API_PATH,
10
+ WEBHOOKS_API_REQUEST_KEY,
11
+ WEBHOOK_CREATE_MODAL_ID,
12
+ } from '../constants';
13
+
14
+ import { selectSearch } from '../WebhooksPageSelectors';
11
15
 
12
16
  import WebhooksTable from './Components/WebhooksTable';
13
17
  import WebhookCreateModal from './Components/WebhookCreateModal';
14
- import EmptyWebhooksIndexPage from './Components/EmptyWebhooksIndexPage';
15
18
 
16
- const WebhooksIndexPage = ({
17
- fetchAndPush,
18
- search,
19
- isLoading,
20
- hasData,
21
- webhooks,
22
- page,
23
- perPage,
24
- sort,
25
- hasError,
26
- itemCount,
27
- message,
28
- canCreate,
29
- reloadWithSearch,
30
- }) => {
19
+ import { reloadWithSearch, fetchAndPush } from '../WebhooksPageActions';
20
+
21
+ const WebhooksIndexPage = () => {
22
+ const dispatch = useDispatch();
23
+
24
+ const search = useSelector(selectSearch);
25
+
31
26
  const [toDelete, setToDelete] = useState({});
32
27
  const [toEdit, setToEdit] = useState(0);
33
28
 
@@ -38,76 +33,33 @@ const WebhooksIndexPage = ({
38
33
  id: WEBHOOK_CREATE_MODAL_ID,
39
34
  });
40
35
 
41
- const createBtn = (
42
- <Button onClick={setCreateModalOpen} bsStyle="primary">
43
- {__('Create Webhook')}
44
- </Button>
45
- );
46
-
47
36
  return (
48
37
  <>
49
38
  <WebhookCreateModal
50
39
  onSuccess={() => {
51
40
  setCreateModalClosed();
52
- reloadWithSearch(search);
41
+ dispatch(reloadWithSearch(search));
53
42
  }}
54
43
  onCancel={setCreateModalClosed}
55
44
  />
56
- <PageLayout
45
+ <TableIndexPage
57
46
  header={__('Webhooks')}
58
- searchable={!isLoading}
59
- searchProps={WEBHOOKS_SEARCH_PROPS}
60
- searchQuery={search}
61
- isLoading={isLoading && hasData}
62
- onSearch={reloadWithSearch}
63
- onBookmarkClick={reloadWithSearch}
64
- toolbarButtons={canCreate && createBtn}
47
+ controller="webhooks"
48
+ apiUrl={WEBHOOKS_API_PATH}
49
+ apiOptions={{ key: WEBHOOKS_API_REQUEST_KEY }}
50
+ customCreateAction={() => setCreateModalOpen}
65
51
  >
66
52
  <WebhooksTable
67
- results={webhooks}
68
- fetchAndPush={fetchAndPush}
69
- pagination={{ page, perPage }}
70
- itemCount={itemCount}
71
- sort={sort}
53
+ fetchAndPush={params => dispatch(fetchAndPush(params))}
72
54
  toDelete={toDelete}
73
55
  setToDelete={setToDelete}
74
- hasData={hasData}
75
- hasError={hasError}
76
- isLoading={isLoading}
77
56
  toEdit={toEdit}
78
57
  setToEdit={setToEdit}
79
- reloadWithSearch={reloadWithSearch}
58
+ reloadWithSearch={query => dispatch(reloadWithSearch(query))}
80
59
  />
81
- </PageLayout>
60
+ </TableIndexPage>
82
61
  </>
83
62
  );
84
63
  };
85
64
 
86
- WebhooksIndexPage.propTypes = {
87
- fetchAndPush: PropTypes.func.isRequired,
88
- search: PropTypes.string,
89
- isLoading: PropTypes.bool.isRequired,
90
- hasData: PropTypes.bool.isRequired,
91
- webhooks: PropTypes.array.isRequired,
92
- page: PropTypes.number,
93
- perPage: PropTypes.number,
94
- sort: PropTypes.object.isRequired,
95
- hasError: PropTypes.bool.isRequired,
96
- itemCount: PropTypes.number.isRequired,
97
- message: PropTypes.object,
98
- canCreate: PropTypes.bool.isRequired,
99
- reloadWithSearch: PropTypes.func.isRequired,
100
- };
101
-
102
- WebhooksIndexPage.defaultProps = {
103
- page: null,
104
- perPage: null,
105
- search: '',
106
- message: { type: 'empty', text: __('Try to create a new Webhook') },
107
- };
108
-
109
- export default withRenderHandler({
110
- Component: WebhooksIndexPage,
111
- EmptyComponent: EmptyWebhooksIndexPage,
112
- ErrorComponent: EmptyWebhooksIndexPage,
113
- });
65
+ export default WebhooksIndexPage;
@@ -1,4 +1,8 @@
1
+ import React from 'react';
1
2
  import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
3
+ import { Provider } from 'react-redux';
4
+ import { createStore } from 'redux';
5
+
2
6
  import WebhooksIndexPage from '../WebhooksIndexPage';
3
7
 
4
8
  const fixtures = {
@@ -17,6 +21,12 @@ const fixtures = {
17
21
  };
18
22
 
19
23
  describe('WebhooksIndexPage', () => {
20
- describe('redering', () =>
21
- testComponentSnapshotsWithFixtures(WebhooksIndexPage, fixtures));
24
+ describe('redering', () => {
25
+ const webhooksPage = () => (
26
+ <Provider store={createStore((state = [], action) => state)}>
27
+ <WebhooksIndexPage />
28
+ </Provider>
29
+ );
30
+ testComponentSnapshotsWithFixtures(webhooksPage, fixtures);
31
+ });
22
32
  });
@@ -1,25 +1,17 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`WebhooksIndexPage redering render with minimal props 1`] = `
4
- <WebhooksIndexPage
5
- canCreate={true}
6
- fetchAndPush={[MockFunction]}
7
- handleCreateSubmit={[MockFunction]}
8
- hasData={false}
9
- hasError={false}
10
- isLoading={false}
11
- itemCount={0}
12
- message={
4
+ <Provider
5
+ store={
13
6
  Object {
14
- "text": "Try to create a new Webhook",
15
- "type": "empty",
7
+ "dispatch": [Function],
8
+ "getState": [Function],
9
+ "replaceReducer": [Function],
10
+ "subscribe": [Function],
11
+ Symbol(Symbol.observable): [Function],
16
12
  }
17
13
  }
18
- page={null}
19
- perPage={null}
20
- reloadWithSearch={[MockFunction]}
21
- search=""
22
- sort={Object {}}
23
- webhooks={Array []}
24
- />
14
+ >
15
+ <WebhooksIndexPage />
16
+ </Provider>
25
17
  `;
@@ -5,15 +5,9 @@ Object {
5
5
  "action": Array [
6
6
  Array [
7
7
  Object {
8
- "key": "WEBHOOKS",
9
- "params": Object {
10
- "order": "",
11
- "page": 1,
12
- "per_page": 20,
13
- "search": "",
14
- },
8
+ "key": "WEBHOOK_EVENTS",
15
9
  "type": "get-some-type",
16
- "url": "/api/v2/webhooks?include_permissions=true",
10
+ "url": "",
17
11
  },
18
12
  ],
19
13
  ],
@@ -26,15 +20,9 @@ Object {
26
20
  "action": Array [
27
21
  Array [
28
22
  Object {
29
- "key": "WEBHOOKS",
30
- "params": Object {
31
- "order": "",
32
- "page": 1,
33
- "per_page": 20,
34
- "search": "",
35
- },
23
+ "key": "WEBHOOK_EVENTS",
36
24
  "type": "get-some-type",
37
- "url": "/api/v2/webhooks?include_permissions=true",
25
+ "url": "",
38
26
  },
39
27
  ],
40
28
  ],
@@ -1,43 +1,3 @@
1
- import { connect } from 'react-redux';
2
- import { compose, bindActionCreators } from 'redux';
3
-
4
- import { callOnMount, callOnPopState } from 'foremanReact/common/HOC';
5
-
6
1
  import WebhooksIndexPage from './WebhooksIndexPage';
7
- import * as actions from '../WebhooksPageActions';
8
-
9
- import {
10
- selectWebhooks,
11
- selectPage,
12
- selectPerPage,
13
- selectSearch,
14
- selectSort,
15
- selectHasData,
16
- selectHasError,
17
- selectIsLoading,
18
- selectSubtotal,
19
- selectMessage,
20
- selectCanCreate,
21
- } from '../WebhooksPageSelectors';
22
-
23
- const mapStateToProps = state => ({
24
- webhooks: selectWebhooks(state),
25
- page: selectPage(state),
26
- perPage: selectPerPage(state),
27
- search: selectSearch(state),
28
- sort: selectSort(state),
29
- isLoading: selectIsLoading(state),
30
- hasData: selectHasData(state),
31
- hasError: selectHasError(state),
32
- itemCount: selectSubtotal(state),
33
- message: selectMessage(state),
34
- canCreate: selectCanCreate(state),
35
- });
36
-
37
- const mapDispatchToProps = dispatch => bindActionCreators(actions, dispatch);
38
2
 
39
- export default compose(
40
- connect(mapStateToProps, mapDispatchToProps),
41
- callOnMount(({ initializeWebhooks }) => initializeWebhooks()),
42
- callOnPopState(({ fetchWebhooks }) => fetchWebhooks())
43
- )(WebhooksIndexPage);
3
+ export default WebhooksIndexPage;
@@ -1,6 +1,6 @@
1
1
  import history from 'foremanReact/history';
2
2
  import { get } from 'foremanReact/redux/API';
3
- import { stringifyParams, getParams } from 'foremanReact/common/urlHelpers';
3
+ import { stringifyParams } from 'foremanReact/common/urlHelpers';
4
4
 
5
5
  import { buildQuery } from './WebhooksPageHelpers';
6
6
  import {
@@ -9,17 +9,6 @@ import {
9
9
  WEBHOOKS_API_REQUEST_KEY,
10
10
  } from './constants';
11
11
 
12
- export const initializeWebhooks = () => dispatch => {
13
- const params = getParams();
14
- dispatch(fetchWebhooks({ per_page: params.perPage, ...params }));
15
- if (!history.action === 'POP') {
16
- history.replace({
17
- pathname: WEBHOOKS_PATH,
18
- search: stringifyParams(params),
19
- });
20
- }
21
- };
22
-
23
12
  export const fetchWebhooks = (
24
13
  /* eslint-disable-next-line camelcase */
25
14
  { page, per_page, searchQuery, sort },
@@ -1,12 +1,9 @@
1
- import { getControllerSearchProps } from 'foremanReact/constants';
2
-
3
1
  export const WEBHOOKS_PAGE_DATA_RESOLVED = 'WEBHOOKS_PAGE_DATA_RESOLVED';
4
2
  export const WEBHOOKS_PAGE_DATA_FAILED = 'WEBHOOKS_PAGE_DATA_FAILED';
5
3
  export const WEBHOOKS_PAGE_HIDE_LOADING = 'WEBHOOKS_PAGE_HIDE_LOADING';
6
4
  export const WEBHOOKS_PAGE_SHOW_LOADING = 'WEBHOOKS_PAGE_SHOW_LOADING';
7
5
  export const WEBHOOKS_PAGE_CLEAR_ERROR = 'WEBHOOKS_PAGE_CLEAR_ERROR';
8
6
 
9
- export const WEBHOOKS_SEARCH_PROPS = getControllerSearchProps('webhooks');
10
7
  export const WEBHOOKS_API_PATH = '/api/v2/webhooks?include_permissions=true';
11
8
  export const WEBHOOKS_PATH = '/webhooks';
12
9
  export const WEBHOOKS_API_REQUEST_KEY = 'WEBHOOKS';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ const TableIndexPage = ({ children }) => <div>{children}</div>;
5
+
6
+ TableIndexPage.propTypes = {
7
+ children: PropTypes.node.isRequired,
8
+ };
9
+
10
+ export default TableIndexPage;
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_webhooks
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2
4
+ version: 3.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Timo Goebel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-01 00:00:00.000000000 Z
11
+ date: 2022-09-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Plugin for Foreman that allows to configure Webhooks.
14
14
  email:
@@ -94,12 +94,12 @@ files:
94
94
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/__snapshots__/WebhookForm.test.js.snap
95
95
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/constants.js
96
96
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/index.js
97
- - webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/EmptyWebhooksIndexPage/index.js
98
97
  - webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookCreateModal.js
99
98
  - webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookDeleteModal.js
100
99
  - webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookEditModal.js
101
100
  - webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookEditModalSelectors.js
102
101
  - webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookModal.scss
102
+ - webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/EmptyWebhooksTable/index.js
103
103
  - webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/EnabledCell.js
104
104
  - webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/__tests__/__snapshots__/enabledCellFormatter.test.js.snap
105
105
  - webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/__tests__/enabledCellFormatter.test.js
@@ -139,6 +139,7 @@ files:
139
139
  - webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js
140
140
  - webpack/__mocks__/foremanReact/components/Loading/Loading.js
141
141
  - webpack/__mocks__/foremanReact/components/Loading/index.js
142
+ - webpack/__mocks__/foremanReact/components/PF4/TableIndexPage/TableIndexPage.js
142
143
  - webpack/__mocks__/foremanReact/components/Pagination/index.js
143
144
  - webpack/__mocks__/foremanReact/components/common/EmptyState.js
144
145
  - webpack/__mocks__/foremanReact/components/common/forms/ForemanForm.js
@@ -1,63 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import { translate as __ } from 'foremanReact/common/I18n';
4
- import DefaultEmptyState from 'foremanReact/components/common/EmptyState';
5
-
6
- import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
7
-
8
- import WebhookCreateModal from '../WebhookCreateModal';
9
- import { WEBHOOK_CREATE_MODAL_ID } from '../../../constants';
10
-
11
- const EmptyWebhooksIndexPage = ({
12
- search,
13
- reloadWithSearch,
14
- message: { type, text },
15
- }) => {
16
- const {
17
- setModalOpen: setCreateModalOpen,
18
- setModalClosed: setCreateModalClosed,
19
- } = useForemanModal({
20
- id: WEBHOOK_CREATE_MODAL_ID,
21
- });
22
-
23
- return (
24
- <>
25
- <WebhookCreateModal
26
- onSuccess={() => {
27
- setCreateModalClosed();
28
- reloadWithSearch(search);
29
- }}
30
- onCancel={setCreateModalClosed}
31
- />
32
- <DefaultEmptyState
33
- icon={type === 'error' ? 'error-circle-o' : 'add-circle-o'}
34
- header={type === 'error' ? __('Error') : __('No Results')}
35
- description={text}
36
- documentation={null}
37
- action={{
38
- title: __('Create Webhook'),
39
- onClick: () => setCreateModalOpen(),
40
- }}
41
- />
42
- </>
43
- );
44
- };
45
-
46
- EmptyWebhooksIndexPage.propTypes = {
47
- message: PropTypes.shape({
48
- type: PropTypes.oneOf(['empty', 'error']),
49
- text: PropTypes.string,
50
- }),
51
- search: PropTypes.string,
52
- reloadWithSearch: PropTypes.func.isRequired,
53
- };
54
-
55
- EmptyWebhooksIndexPage.defaultProps = {
56
- message: {
57
- type: 'empty',
58
- text: __('Try to create a new Webhook'),
59
- },
60
- search: '',
61
- };
62
-
63
- export default EmptyWebhooksIndexPage;