foreman_webhooks 3.0.2 → 3.0.5

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.
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;