foreman_webhooks 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/webhooks_controller.rb +7 -1
  3. data/app/controllers/concerns/foreman_webhooks/controller/parameters/webhook.rb +1 -0
  4. data/app/controllers/webhooks_controller.rb +1 -24
  5. data/app/services/foreman_webhooks/webhook_service.rb +1 -1
  6. data/app/views/foreman_webhooks/webhook_templates/remote_execution_-_host_job.erb +0 -1
  7. data/config/routes.rb +6 -2
  8. data/lib/foreman_webhooks/engine.rb +2 -2
  9. data/lib/foreman_webhooks/version.rb +1 -1
  10. data/package.json +6 -6
  11. data/webpack/ForemanWebhooks/Routes/ForemanWebhooksRoutes.js +2 -2
  12. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/ForemanFormikField.js +117 -0
  13. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.css +7 -0
  14. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.js +163 -0
  15. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/WebhookForm.js +69 -0
  16. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/WebhookFormSelectors.js +58 -0
  17. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/WebhookForm.test.js +51 -0
  18. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/__snapshots__/WebhookForm.test.js.snap +510 -0
  19. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/constants.js +9 -0
  20. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/index.js +73 -0
  21. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/EmptyWebhooksIndexPage/index.js +63 -0
  22. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookCreateModal.js +69 -0
  23. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookDeleteModal.js +1 -0
  24. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookEditModal.js +107 -0
  25. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookEditModalSelectors.js +22 -0
  26. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookModal.scss +6 -0
  27. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/index.js +1 -0
  28. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/nameToEditFormatter.js +19 -0
  29. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/NameToEditCell.js +35 -0
  30. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/WebhooksTable.js +31 -14
  31. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/WebhooksTableSchema.js +12 -4
  32. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/__tests__/WebhooksTable.test.js +13 -4
  33. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/__tests__/__snapshots__/WebhooksTable.test.js.snap +62 -19
  34. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/index.js +27 -5
  35. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/WebhooksIndexPage.js +61 -32
  36. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/WebhooksIndexPage.fixtures.js +36 -0
  37. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/WebhooksIndexPage.test.js +3 -1
  38. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/__snapshots__/WebhooksIndexPage.test.js.snap +18 -60
  39. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/__snapshots__/integration.test.js.snap +43 -0
  40. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/integration.test.js +34 -0
  41. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksPageActions.js +3 -0
  42. data/webpack/ForemanWebhooks/Routes/Webhooks/constants.js +11 -1
  43. data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext.js +1 -0
  44. data/webpack/__mocks__/foremanReact/common/HOC.js +26 -1
  45. data/webpack/__mocks__/foremanReact/common/urlHelpers.js +7 -0
  46. data/webpack/__mocks__/foremanReact/components/ForemanModal/index.js +6 -1
  47. data/webpack/__mocks__/foremanReact/components/Loading/Loading.js +2 -0
  48. data/webpack/__mocks__/foremanReact/components/Loading/index.js +3 -0
  49. data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +3 -1
  50. data/webpack/__mocks__/foremanReact/components/common/forms/ForemanForm.js +9 -0
  51. data/webpack/__mocks__/foremanReact/components/common/forms/FormField.js +3 -0
  52. data/webpack/__mocks__/foremanReact/components/common/table.js +21 -0
  53. data/webpack/__mocks__/foremanReact/history.js +3 -0
  54. data/webpack/__mocks__/foremanReact/redux/actions/common/forms.js +1 -0
  55. data/webpack/test_setup.js +15 -0
  56. metadata +30 -7
  57. data/app/views/webhooks/_form.html.erb +0 -38
  58. data/app/views/webhooks/_templates.html.erb +0 -5
  59. data/app/views/webhooks/edit.html.erb +0 -3
  60. data/app/views/webhooks/new.html.erb +0 -3
  61. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/EmptyWebhooksTable/index.js +0 -29
@@ -3,12 +3,14 @@ import {
3
3
  column,
4
4
  sortableColumn,
5
5
  headerFormatterWithProps,
6
- nameCellFormatter,
7
6
  deleteActionCellFormatter,
8
7
  cellFormatter,
9
8
  } from 'foremanReact/components/common/table';
10
9
 
11
- import { enabledCellFormatter } from './Components/Formatters';
10
+ import {
11
+ enabledCellFormatter,
12
+ nameToEditFormatter,
13
+ } from './Components/Formatters';
12
14
 
13
15
  const sortControllerFactory = (apiCall, sortBy, sortOrder) => ({
14
16
  apply: (by, order) => {
@@ -18,12 +20,18 @@ const sortControllerFactory = (apiCall, sortBy, sortOrder) => ({
18
20
  order: sortOrder,
19
21
  });
20
22
 
21
- const createWebhooksTableSchema = (apiCall, by, order, onDeleteClick) => {
23
+ const createWebhooksTableSchema = (
24
+ apiCall,
25
+ by,
26
+ order,
27
+ onDeleteClick,
28
+ onEditClick
29
+ ) => {
22
30
  const sortController = sortControllerFactory(apiCall, by, order);
23
31
 
24
32
  return [
25
33
  sortableColumn('name', __('Name'), 4, sortController, [
26
- nameCellFormatter('webhooks'),
34
+ nameToEditFormatter('webhooks', onEditClick),
27
35
  ]),
28
36
  sortableColumn('targetUrl', __('Target URL'), 4, sortController),
29
37
  sortableColumn('enabled', __('Enabled'), 2, sortController, [
@@ -7,11 +7,20 @@ import {
7
7
  import WrappedWebhooksTable from '../index';
8
8
 
9
9
  const props = {
10
- fetchAndPush: () => {},
11
- onDeleteClick: () => {},
12
- setToDelete: () => {},
10
+ fetchAndPush: jest.fn(),
11
+ onDeleteClick: jest.fn(),
12
+ setToDelete: jest.fn(),
13
+ setToEdit: jest.fn(),
14
+ reloadWithSearch: jest.fn(),
13
15
  itemCount: 0,
14
16
  canCreate: true,
17
+ results: [],
18
+ pagination: {
19
+ page: 1,
20
+ perPage: 20,
21
+ },
22
+ toDelete: {},
23
+ toEdit: 0,
15
24
  };
16
25
 
17
26
  const fixtures = {
@@ -46,7 +55,7 @@ const fixtures = {
46
55
  hasError: false,
47
56
  hasData: true,
48
57
  toasts: [],
49
- webhooks,
58
+ results: webhooks,
50
59
  itemCount: webhooks.length,
51
60
  }),
52
61
  };
@@ -1,16 +1,25 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`WebhooksTable rendering should render when loading 1`] = `
4
- <Component
4
+ <WebhooksTable
5
5
  canCreate={true}
6
- fetchAndPush={[Function]}
6
+ fetchAndPush={[MockFunction]}
7
7
  hasData={false}
8
8
  hasError={false}
9
9
  isLoading={true}
10
10
  itemCount={0}
11
- onDeleteClick={[Function]}
11
+ onDeleteClick={[MockFunction]}
12
+ onEditClick={[Function]}
12
13
  page={5}
14
+ pagination={
15
+ Object {
16
+ "page": 1,
17
+ "perPage": 20,
18
+ }
19
+ }
13
20
  perPage={42}
21
+ reloadWithSearch={[MockFunction]}
22
+ results={Array []}
14
23
  search="name=foo"
15
24
  sort={
16
25
  Object {
@@ -18,14 +27,16 @@ exports[`WebhooksTable rendering should render when loading 1`] = `
18
27
  "order": "ASC",
19
28
  }
20
29
  }
30
+ toDelete={Object {}}
31
+ toEdit={0}
21
32
  toasts={Array []}
22
33
  />
23
34
  `;
24
35
 
25
36
  exports[`WebhooksTable rendering should render with error 1`] = `
26
- <Component
37
+ <WebhooksTable
27
38
  canCreate={true}
28
- fetchAndPush={[Function]}
39
+ fetchAndPush={[MockFunction]}
29
40
  hasData={false}
30
41
  hasError={true}
31
42
  isLoading={false}
@@ -36,9 +47,18 @@ exports[`WebhooksTable rendering should render with error 1`] = `
36
47
  "type": "error",
37
48
  }
38
49
  }
39
- onDeleteClick={[Function]}
50
+ onDeleteClick={[MockFunction]}
51
+ onEditClick={[Function]}
40
52
  page={5}
53
+ pagination={
54
+ Object {
55
+ "page": 1,
56
+ "perPage": 20,
57
+ }
58
+ }
41
59
  perPage={42}
60
+ reloadWithSearch={[MockFunction]}
61
+ results={Array []}
42
62
  search="name=foo"
43
63
  sort={
44
64
  Object {
@@ -46,21 +66,32 @@ exports[`WebhooksTable rendering should render with error 1`] = `
46
66
  "order": "ASC",
47
67
  }
48
68
  }
69
+ toDelete={Object {}}
70
+ toEdit={0}
49
71
  toasts={Array []}
50
72
  />
51
73
  `;
52
74
 
53
75
  exports[`WebhooksTable rendering should render with no data 1`] = `
54
- <Component
76
+ <WebhooksTable
55
77
  canCreate={true}
56
- fetchAndPush={[Function]}
78
+ fetchAndPush={[MockFunction]}
57
79
  hasData={false}
58
80
  hasError={false}
59
81
  isLoading={false}
60
82
  itemCount={0}
61
- onDeleteClick={[Function]}
83
+ onDeleteClick={[MockFunction]}
84
+ onEditClick={[Function]}
62
85
  page={5}
86
+ pagination={
87
+ Object {
88
+ "page": 1,
89
+ "perPage": 20,
90
+ }
91
+ }
63
92
  perPage={42}
93
+ reloadWithSearch={[MockFunction]}
94
+ results={Array []}
64
95
  search="name=foo"
65
96
  sort={
66
97
  Object {
@@ -68,30 +99,32 @@ exports[`WebhooksTable rendering should render with no data 1`] = `
68
99
  "order": "ASC",
69
100
  }
70
101
  }
102
+ toDelete={Object {}}
103
+ toEdit={0}
71
104
  toasts={Array []}
72
105
  />
73
106
  `;
74
107
 
75
108
  exports[`WebhooksTable rendering should render with webhooks 1`] = `
76
- <Component
109
+ <WebhooksTable
77
110
  canCreate={true}
78
- fetchAndPush={[Function]}
111
+ fetchAndPush={[MockFunction]}
79
112
  hasData={true}
80
113
  hasError={false}
81
114
  isLoading={false}
82
115
  itemCount={2}
83
- onDeleteClick={[Function]}
116
+ onDeleteClick={[MockFunction]}
117
+ onEditClick={[Function]}
84
118
  page={5}
85
- perPage={42}
86
- search="name=foo"
87
- sort={
119
+ pagination={
88
120
  Object {
89
- "by": "defaultName",
90
- "order": "ASC",
121
+ "page": 1,
122
+ "perPage": 20,
91
123
  }
92
124
  }
93
- toasts={Array []}
94
- webhooks={
125
+ perPage={42}
126
+ reloadWithSearch={[MockFunction]}
127
+ results={
95
128
  Array [
96
129
  Object {
97
130
  "canDelete": true,
@@ -111,5 +144,15 @@ exports[`WebhooksTable rendering should render with webhooks 1`] = `
111
144
  },
112
145
  ]
113
146
  }
147
+ search="name=foo"
148
+ sort={
149
+ Object {
150
+ "by": "defaultName",
151
+ "order": "ASC",
152
+ }
153
+ }
154
+ toDelete={Object {}}
155
+ toEdit={0}
156
+ toasts={Array []}
114
157
  />
115
158
  `;
@@ -4,22 +4,44 @@ import PropTypes from 'prop-types';
4
4
  import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
5
5
 
6
6
  import WebhooksTable from './WebhooksTable';
7
- import { WEBHOOK_DELETE_MODAL_ID } from '../../../constants';
7
+ import {
8
+ WEBHOOK_DELETE_MODAL_ID,
9
+ WEBHOOK_EDIT_MODAL_ID,
10
+ } from '../../../constants';
8
11
 
9
12
  const WrappedWebhooksTable = props => {
10
- const { setModalOpen } = useForemanModal({ id: WEBHOOK_DELETE_MODAL_ID });
11
- const { setToDelete, ...rest } = props;
13
+ const { setModalOpen: setDeleteModalOpen } = useForemanModal({
14
+ id: WEBHOOK_DELETE_MODAL_ID,
15
+ });
16
+
17
+ const { setModalOpen: setEditModalOpen } = useForemanModal({
18
+ id: WEBHOOK_EDIT_MODAL_ID,
19
+ });
20
+
21
+ const { setToDelete, setToEdit, ...rest } = props;
12
22
 
13
23
  const onDeleteClick = rowData => {
14
24
  setToDelete(rowData);
15
- setModalOpen();
25
+ setDeleteModalOpen();
26
+ };
27
+
28
+ const onEditClick = rowData => {
29
+ setToEdit(rowData);
30
+ setEditModalOpen();
16
31
  };
17
32
 
18
- return <WebhooksTable {...rest} onDeleteClick={onDeleteClick} />;
33
+ return (
34
+ <WebhooksTable
35
+ onDeleteClick={onDeleteClick}
36
+ onEditClick={onEditClick}
37
+ {...rest}
38
+ />
39
+ );
19
40
  };
20
41
 
21
42
  WrappedWebhooksTable.propTypes = {
22
43
  setToDelete: PropTypes.func.isRequired,
44
+ setToEdit: PropTypes.func.isRequired,
23
45
  };
24
46
 
25
47
  export default WrappedWebhooksTable;
@@ -1,14 +1,17 @@
1
1
  import React, { useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Link } from 'react-router-dom';
4
3
  import { Button } from 'patternfly-react';
5
4
 
6
5
  import { translate as __ } from 'foremanReact/common/I18n';
7
6
  import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
8
- import { foremanUrl } from 'foremanReact/common/helpers';
7
+ import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
8
+ import { withRenderHandler } from 'foremanReact/common/HOC';
9
+
10
+ import { WEBHOOKS_SEARCH_PROPS, WEBHOOK_CREATE_MODAL_ID } from '../constants';
9
11
 
10
- import { WEBHOOKS_SEARCH_PROPS, WEBHOOKS_PATH } from '../constants';
11
12
  import WebhooksTable from './Components/WebhooksTable';
13
+ import WebhookCreateModal from './Components/WebhookCreateModal';
14
+ import EmptyWebhooksIndexPage from './Components/EmptyWebhooksIndexPage';
12
15
 
13
16
  const WebhooksIndexPage = ({
14
17
  fetchAndPush,
@@ -24,40 +27,61 @@ const WebhooksIndexPage = ({
24
27
  message,
25
28
  canCreate,
26
29
  toasts,
30
+ reloadWithSearch,
27
31
  }) => {
28
- const handleSearch = query => fetchAndPush({ searchQuery: query, page: 1 });
29
32
  const [toDelete, setToDelete] = useState({});
33
+ const [toEdit, setToEdit] = useState(0);
34
+
35
+ const {
36
+ setModalOpen: setCreateModalOpen,
37
+ setModalClosed: setCreateModalClosed,
38
+ } = useForemanModal({
39
+ id: WEBHOOK_CREATE_MODAL_ID,
40
+ });
41
+
30
42
  const createBtn = (
31
- <Link to={foremanUrl(`${WEBHOOKS_PATH}/new`)}>
32
- <Button bsStyle="primary">{__('Create Webhook')}</Button>
33
- </Link>
43
+ <Button onClick={setCreateModalOpen} bsStyle="primary">
44
+ {__('Create Webhook')}
45
+ </Button>
34
46
  );
47
+
35
48
  return (
36
- <PageLayout
37
- header={__('Webhooks')}
38
- searchable={!isLoading}
39
- searchProps={WEBHOOKS_SEARCH_PROPS}
40
- searchQuery={search}
41
- isLoading={isLoading && hasData}
42
- onSearch={handleSearch}
43
- onBookmarkClick={handleSearch}
44
- toastNotifications={toasts}
45
- toolbarButtons={canCreate && createBtn}
46
- >
47
- <WebhooksTable
48
- results={webhooks}
49
- fetchAndPush={fetchAndPush}
50
- pagination={{ page, perPage }}
51
- itemCount={itemCount}
52
- sort={sort}
53
- toDelete={toDelete}
54
- setToDelete={setToDelete}
55
- message={message}
56
- hasData={hasData}
57
- hasError={hasError}
58
- isLoading={isLoading}
49
+ <>
50
+ <WebhookCreateModal
51
+ onSuccess={() => {
52
+ setCreateModalClosed();
53
+ reloadWithSearch(search);
54
+ }}
55
+ onCancel={setCreateModalClosed}
59
56
  />
60
- </PageLayout>
57
+ <PageLayout
58
+ header={__('Webhooks')}
59
+ searchable={!isLoading}
60
+ searchProps={WEBHOOKS_SEARCH_PROPS}
61
+ searchQuery={search}
62
+ isLoading={isLoading && hasData}
63
+ onSearch={reloadWithSearch}
64
+ onBookmarkClick={reloadWithSearch}
65
+ toastNotifications={toasts}
66
+ toolbarButtons={canCreate && createBtn}
67
+ >
68
+ <WebhooksTable
69
+ results={webhooks}
70
+ fetchAndPush={fetchAndPush}
71
+ pagination={{ page, perPage }}
72
+ itemCount={itemCount}
73
+ sort={sort}
74
+ toDelete={toDelete}
75
+ setToDelete={setToDelete}
76
+ hasData={hasData}
77
+ hasError={hasError}
78
+ isLoading={isLoading}
79
+ toEdit={toEdit}
80
+ setToEdit={setToEdit}
81
+ reloadWithSearch={reloadWithSearch}
82
+ />
83
+ </PageLayout>
84
+ </>
61
85
  );
62
86
  };
63
87
 
@@ -75,6 +99,7 @@ WebhooksIndexPage.propTypes = {
75
99
  message: PropTypes.object,
76
100
  canCreate: PropTypes.bool.isRequired,
77
101
  toasts: PropTypes.array.isRequired,
102
+ reloadWithSearch: PropTypes.func.isRequired,
78
103
  };
79
104
 
80
105
  WebhooksIndexPage.defaultProps = {
@@ -84,4 +109,8 @@ WebhooksIndexPage.defaultProps = {
84
109
  message: { type: 'empty', text: __('Try to create a new Webhook') },
85
110
  };
86
111
 
87
- export default WebhooksIndexPage;
112
+ export default withRenderHandler({
113
+ Component: WebhooksIndexPage,
114
+ EmptyComponent: EmptyWebhooksIndexPage,
115
+ ErrorComponent: EmptyWebhooksIndexPage,
116
+ });
@@ -72,3 +72,39 @@ export const webhooks = [
72
72
  enabled: false,
73
73
  },
74
74
  ];
75
+
76
+ export const spySelector = selectors => {
77
+ jest.spyOn(selectors, 'selectIsLoading');
78
+ jest.spyOn(selectors, 'selectHasError');
79
+ jest.spyOn(selectors, 'selectWebhooks');
80
+ jest.spyOn(selectors, 'selectHasData');
81
+ jest.spyOn(selectors, 'selectPage');
82
+ jest.spyOn(selectors, 'selectPerPage');
83
+ jest.spyOn(selectors, 'selectSearch');
84
+ jest.spyOn(selectors, 'selectSort');
85
+ jest.spyOn(selectors, 'selectCanCreate');
86
+ jest.spyOn(selectors, 'selectSubtotal');
87
+ jest.spyOn(selectors, 'selectMessage');
88
+
89
+ selectors.selectIsLoading.mockImplementation(() => false);
90
+ selectors.selectHasError.mockImplementation(() => false);
91
+ selectors.selectWebhooks.mockImplementation(() => []);
92
+ selectors.selectHasData.mockImplementation(() => true);
93
+ selectors.selectPage.mockImplementation(() => 1);
94
+ selectors.selectPerPage.mockImplementation(() => 20);
95
+ selectors.selectSearch.mockImplementation(() => '');
96
+ selectors.selectSort.mockImplementation(() => ({ by: '', order: '' }));
97
+ selectors.selectCanCreate.mockImplementation(() => true);
98
+ selectors.selectSubtotal.mockImplementation(() => 0);
99
+ selectors.selectMessage.mockImplementation(() => ({}));
100
+ };
101
+
102
+ export const spyEditSelector = selectors => {
103
+ jest.spyOn(selectors, 'selectIsLoading');
104
+ jest.spyOn(selectors, 'selectWebhookValues');
105
+ jest.spyOn(selectors, 'selectWebhookTemplateId');
106
+
107
+ selectors.selectIsLoading.mockImplementation(() => false);
108
+ selectors.selectWebhookValues.mockImplementation(() => ({}));
109
+ selectors.selectWebhookTemplateId.mockImplementation(() => 1);
110
+ };