foreman_openscap 13.0.0 → 13.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f58e733e3dd18596f3c7e16c4015103df7729a629577735ccb67ed7912172c3f
4
- data.tar.gz: c87d597fcae523beb2f8607bc4a88bb77ee2508ce127b19761f6030d6eefd3e5
3
+ metadata.gz: 31e3721d3419a9695bb06d27b4dc1f79daf7aa825fe091afdd5ce3ab84fec6d5
4
+ data.tar.gz: '09f41b5c13f636a9227ace3b2e2b7c27f5341d22400d98afdcddc597c5650af6'
5
5
  SHA512:
6
- metadata.gz: 5a7eb9acc11cac66fc65081c49bb6bfb4cbe58fbc5accadf5896ba19af130d9237a8825215c6a866d68252dc51f5cdd28516b072494f8c969960b47e63221e1f
7
- data.tar.gz: c68a53b00952b47560b977ebb6a661c24a3ab7a064f8ddfc05ba4a1c26e97882796db01556bd57fa705a6637a1df4073ff083716ff7909a9c11d1a333a9ac735
6
+ metadata.gz: 508fba001dca06e61ae108a10ca54193eec9ab3ba4f26b310481713a852aef69d09c720fdd44ea8ecf89a741a906c30b415640497894f4e0da92b14f0bb5de15
7
+ data.tar.gz: ffcc23a0d7bd9ecf624ee81f6e46eadd02fc1bf9111e8bd15648590e9f435a7872e6791f2197d024f680628bdf9de5f6e602c5b13f156984130b2865ad381d34
@@ -1,3 +1,3 @@
1
1
  module ForemanOpenscap
2
- VERSION = "13.0.0".freeze
2
+ VERSION = "13.0.1".freeze
3
3
  end
@@ -1,5 +1,17 @@
1
1
  /** Backend sends [label, values, color]; legacy Foreman charts used spread columns. */
2
2
  import { chart_color_black_500 as chartColorBlack500 } from '@patternfly/react-tokens';
3
+ import {
4
+ MS_PER_SECOND,
5
+ SECONDS_PER_MINUTE,
6
+ MINUTES_PER_HOUR,
7
+ HOURS_PER_HALF_DAY,
8
+ } from 'foremanReact/constants';
9
+
10
+ const TIMESTAMP_THRESHOLD = 1e12;
11
+ const DEFAULT_TICK_MID = 0.5;
12
+ const MIN_TICK_STEP = 0.1;
13
+ const EXPONENTIAL_THRESHOLD = 1e21;
14
+ const CLAMP_SCALE_FACTOR = 0.9;
3
15
 
4
16
  const getColumnValues = col => {
5
17
  if (Array.isArray(col[1])) return col[1];
@@ -32,7 +44,7 @@ const getSeriesColor = col => {
32
44
 
33
45
  const toMs = val => {
34
46
  const n = Number(val);
35
- return n >= 1e12 ? n : n * 1000;
47
+ return n >= TIMESTAMP_THRESHOLD ? n : n * MS_PER_SECOND;
36
48
  };
37
49
 
38
50
  /** Process raw backend data into chart series for PatternFly line charts. */
@@ -101,9 +113,9 @@ export const getYTickValues = (chartData, hiddenSeries = new Set()) => {
101
113
  });
102
114
  });
103
115
 
104
- if (maxY <= 0) return [0, 0.5, 1.0];
116
+ if (maxY <= 0) return [0, DEFAULT_TICK_MID, 1.0];
105
117
 
106
- const step = Math.max(0.1, Math.ceil((maxY / 4) * 10) / 10);
118
+ const step = Math.max(MIN_TICK_STEP, Math.ceil((maxY / 4) * 10) / 10);
107
119
  return [0, 1, 2, 3, 4, 5].map(i => Math.round(i * step * 10) / 10);
108
120
  };
109
121
 
@@ -111,7 +123,7 @@ export const getYTickValues = (chartData, hiddenSeries = new Set()) => {
111
123
  export const formatTooltipValue = value => {
112
124
  const num = Number(value);
113
125
  if (!Number.isFinite(num)) return '';
114
- if (Math.abs(num) >= 1e21) {
126
+ if (Math.abs(num) >= EXPONENTIAL_THRESHOLD) {
115
127
  return num.toExponential(1);
116
128
  }
117
129
  return String(Math.round(num));
@@ -128,14 +140,14 @@ export const clampChartPadding = (padding, width, height) => {
128
140
 
129
141
  const horizontalTotal = left + right;
130
142
  if (horizontalTotal >= width) {
131
- const scale = (width * 0.9) / horizontalTotal;
143
+ const scale = (width * CLAMP_SCALE_FACTOR) / horizontalTotal;
132
144
  left *= scale;
133
145
  right *= scale;
134
146
  }
135
147
 
136
148
  const verticalTotal = top + bottom;
137
149
  if (verticalTotal >= height) {
138
- const scale = (height * 0.9) / verticalTotal;
150
+ const scale = (height * CLAMP_SCALE_FACTOR) / verticalTotal;
139
151
  top *= scale;
140
152
  bottom *= scale;
141
153
  }
@@ -154,7 +166,11 @@ export const getTimeseriesXDomain = chartData => {
154
166
  const max = Math.max(...times);
155
167
 
156
168
  if (min === max) {
157
- const offset = 12 * 60 * 60 * 1000;
169
+ const offset =
170
+ HOURS_PER_HALF_DAY *
171
+ MINUTES_PER_HOUR *
172
+ SECONDS_PER_MINUTE *
173
+ MS_PER_SECOND;
158
174
  return [new Date(min - offset), new Date(max + offset)];
159
175
  }
160
176
 
@@ -15,6 +15,9 @@ export const JOB_INVOCATION_API_REQUEST_KEY = 'OPENSCAP_REX_JOB_INVOCATIONS';
15
15
  export const SNIPPET_SH = 'urn:xccdf:fix:script:sh';
16
16
  export const SNIPPET_ANSIBLE = 'urn:xccdf:fix:script:ansible';
17
17
 
18
+ export const TOOLTIP_COPIED_EXIT_DELAY_MS = 1500;
19
+ export const TOOLTIP_DEFAULT_EXIT_DELAY_MS = 600;
20
+
18
21
  export const WIZARD_TITLES = {
19
22
  snippetSelect: __('Select snippet'),
20
23
  reviewHosts: __('Review hosts'),
@@ -6,7 +6,7 @@ import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons';
6
6
 
7
7
  import { translate as __ } from 'foremanReact/common/I18n';
8
8
  import { foremanUrl } from 'foremanReact/common/helpers';
9
- import { STATUS } from 'foremanReact/constants';
9
+ import { STATUS, HTTP_STATUS_CODES } from 'foremanReact/constants';
10
10
  import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
11
11
  import Loading from 'foremanReact/components/Loading';
12
12
  import PermissionDenied from 'foremanReact/components/PermissionDenied';
@@ -91,7 +91,7 @@ const Finish = ({ onClose }) => {
91
91
  </Button>
92
92
  );
93
93
  const errorComponent =
94
- statusCode === 403 ? (
94
+ statusCode === HTTP_STATUS_CODES.FORBIDDEN ? (
95
95
  <PermissionDenied
96
96
  missingPermissions={data?.error?.missing_permissions}
97
97
  primaryButton={closeBtn}
@@ -24,7 +24,11 @@ import {
24
24
  import OpenscapRemediationWizardContext from '../OpenscapRemediationWizardContext';
25
25
  import WizardHeader from '../WizardHeader';
26
26
  import ViewSelectedHostsLink from '../ViewSelectedHostsLink';
27
- import { FAIL_RULE_SEARCH } from '../constants';
27
+ import {
28
+ FAIL_RULE_SEARCH,
29
+ TOOLTIP_COPIED_EXIT_DELAY_MS,
30
+ TOOLTIP_DEFAULT_EXIT_DELAY_MS,
31
+ } from '../constants';
28
32
  import { findFixBySnippet } from '../helpers';
29
33
 
30
34
  import './ReviewRemediation.scss';
@@ -78,7 +82,11 @@ const ReviewRemediation = () => {
78
82
  textId="code-content"
79
83
  aria-label="Copy to clipboard"
80
84
  onClick={e => onCopyClick(e, snippetText)}
81
- exitDelay={copied ? 1500 : 600}
85
+ exitDelay={
86
+ copied
87
+ ? TOOLTIP_COPIED_EXIT_DELAY_MS
88
+ : TOOLTIP_DEFAULT_EXIT_DELAY_MS
89
+ }
82
90
  maxWidth="110px"
83
91
  variant="plain"
84
92
  onTooltipHidden={() => setCopied(false)}
@@ -18,6 +18,8 @@ import WizardHeader from '../WizardHeader';
18
18
  import EmptyState from '../../EmptyState';
19
19
  import { errorMsg, supportedRemediationSnippets } from '../helpers';
20
20
 
21
+ const URN_TAIL_SEGMENTS = -2;
22
+
21
23
  const SnippetSelect = () => {
22
24
  const {
23
25
  fixes,
@@ -44,7 +46,7 @@ const SnippetSelect = () => {
44
46
  if (mapped) return mapped;
45
47
 
46
48
  return join(
47
- map(slice(split(system, ':'), -2), n => capitalize(n)),
49
+ map(slice(split(system, ':'), URN_TAIL_SEGMENTS), n => capitalize(n)),
48
50
  ' '
49
51
  );
50
52
  };
@@ -2,9 +2,11 @@ import React from 'react';
2
2
  import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
3
3
  import HostKebabItems from './components/HostExtentions/HostKebabItems';
4
4
 
5
+ const OPENSCAP_KEBAB_WEIGHT = 400;
6
+
5
7
  addGlobalFill(
6
8
  'host-details-kebab',
7
9
  `openscap-kebab-items`,
8
10
  <HostKebabItems key="openscap-host-kebab" />,
9
- 400
11
+ OPENSCAP_KEBAB_WEIGHT
10
12
  );
@@ -0,0 +1 @@
1
+ import 'foremanJSTestSetup';
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_openscap
3
3
  version: !ruby/object:Gem::Version
4
- version: 13.0.0
4
+ version: 13.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - slukasik@redhat.com
@@ -362,20 +362,14 @@ files:
362
362
  - test/unit/services/lookup_key_overrider_test.rb
363
363
  - test/unit/services/report_dashboard/data_test.rb
364
364
  - test/unit/tailoring_file_test.rb
365
- - webpack/components/ConfirmModal.js
366
- - webpack/components/ConfirmModal.scss
367
365
  - webpack/components/EmptyState.js
368
366
  - webpack/components/HostExtentions/HostKebabItems.js
369
- - webpack/components/IndexLayout.js
370
367
  - webpack/components/IndexLayout.scss
371
- - webpack/components/IndexTable/IndexTableHelper.js
372
- - webpack/components/IndexTable/index.js
373
368
  - webpack/components/LineChart/LineChart.fixtures.js
374
369
  - webpack/components/LineChart/LineChart.scss
375
370
  - webpack/components/LineChart/LineChart.test.js
376
371
  - webpack/components/LineChart/LineChartHelpers.js
377
372
  - webpack/components/LineChart/index.js
378
- - webpack/components/LinkButton.js
379
373
  - webpack/components/OpenscapRemediationWizard/Footer.js
380
374
  - webpack/components/OpenscapRemediationWizard/OpenscapRemediationSelectors.js
381
375
  - webpack/components/OpenscapRemediationWizard/OpenscapRemediationWizardContext.js
@@ -399,18 +393,9 @@ files:
399
393
  - webpack/components/RuleSeverity/i_severity-med.svg
400
394
  - webpack/components/RuleSeverity/i_unknown.svg
401
395
  - webpack/components/RuleSeverity/index.js
402
- - webpack/components/withDeleteModal.js
403
- - webpack/components/withLoading.js
404
396
  - webpack/global_index.js
405
- - webpack/helpers/commonHelper.js
406
- - webpack/helpers/globalIdHelper.js
407
- - webpack/helpers/mutationHelper.js
408
- - webpack/helpers/pageParamsHelper.js
409
- - webpack/helpers/permissionsHelper.js
410
- - webpack/helpers/tableHelper.js
411
- - webpack/helpers/toastHelper.js
412
397
  - webpack/index.js
413
- - webpack/testHelper.js
398
+ - webpack/test_setup.js
414
399
  homepage: https://github.com/theforeman/foreman_openscap
415
400
  licenses:
416
401
  - GPL-3.0
@@ -429,7 +414,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
429
414
  - !ruby/object:Gem::Version
430
415
  version: '0'
431
416
  requirements: []
432
- rubygems_version: 4.0.10
417
+ rubygems_version: 4.0.16
433
418
  specification_version: 4
434
419
  summary: Foreman plug-in for displaying OpenSCAP audit reports
435
420
  test_files:
@@ -1,66 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import { Modal, Button, ModalVariant, Spinner } from '@patternfly/react-core';
4
-
5
- import { translate as __ } from 'foremanReact/common/I18n';
6
-
7
- import './ConfirmModal.scss';
8
-
9
- const ConfirmModal = props => {
10
- const [callMutation, { loading }] = props.prepareMutation();
11
-
12
- const actions = [
13
- <Button
14
- ouiaId={`oscap-conf-modal-${props.record?.id}-confirm`}
15
- key="confirm"
16
- variant="primary"
17
- onClick={() => props.onConfirm(callMutation, props.record.id)}
18
- isDisabled={loading}
19
- >
20
- {__('Confirm')}
21
- </Button>,
22
- <Button
23
- ouiaId={`oscap-conf-modal-${props.record?.id}-cancel`}
24
- key="cancel"
25
- variant="link"
26
- onClick={event => props.onClose()}
27
- isDisabled={loading}
28
- >
29
- {__('Cancel')}
30
- </Button>,
31
- ];
32
-
33
- if (loading) {
34
- actions.push(<Spinner key="spinner" size="lg" />);
35
- }
36
-
37
- return (
38
- <Modal
39
- ouiaId={`oscap-conf-modal-${props.record?.id}`}
40
- variant={ModalVariant.medium}
41
- title={props.title}
42
- isOpen={props.isOpen}
43
- className="foreman-modal"
44
- showClose={false}
45
- actions={actions}
46
- >
47
- {props.text}
48
- </Modal>
49
- );
50
- };
51
-
52
- ConfirmModal.propTypes = {
53
- prepareMutation: PropTypes.func.isRequired,
54
- onConfirm: PropTypes.func.isRequired,
55
- record: PropTypes.object,
56
- onClose: PropTypes.func.isRequired,
57
- title: PropTypes.string.isRequired,
58
- isOpen: PropTypes.bool.isRequired,
59
- text: PropTypes.string.isRequired,
60
- };
61
-
62
- ConfirmModal.defaultProps = {
63
- record: null,
64
- };
65
-
66
- export default ConfirmModal;
@@ -1,3 +0,0 @@
1
- .pf-v5-c-backdrop {
2
- z-index: 1040;
3
- }
@@ -1,44 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import { Helmet } from 'react-helmet';
4
- import ToastsList from 'foremanReact/components/ToastsList';
5
- import {
6
- Grid,
7
- GridItem,
8
- TextContent,
9
- Text,
10
- TextVariants,
11
- } from '@patternfly/react-core';
12
-
13
- import './IndexLayout.scss';
14
-
15
- const IndexLayout = ({ pageTitle, children, contentWidthSpan }) => (
16
- <React.Fragment>
17
- <Helmet>
18
- <title>{pageTitle}</title>
19
- </Helmet>
20
- <ToastsList />
21
- <Grid className="scap-page-grid">
22
- <GridItem span={12} className="pf-v5-u-pb-xl">
23
- <TextContent>
24
- <Text ouiaId="oscap-index-title" component={TextVariants.h1}>
25
- {pageTitle}
26
- </Text>
27
- </TextContent>
28
- </GridItem>
29
- <GridItem span={contentWidthSpan}>{children}</GridItem>
30
- </Grid>
31
- </React.Fragment>
32
- );
33
-
34
- IndexLayout.propTypes = {
35
- pageTitle: PropTypes.string.isRequired,
36
- children: PropTypes.oneOfType([PropTypes.node, PropTypes.object]).isRequired,
37
- contentWidthSpan: PropTypes.number,
38
- };
39
-
40
- IndexLayout.defaultProps = {
41
- contentWidthSpan: 12,
42
- };
43
-
44
- export default IndexLayout;
@@ -1,6 +0,0 @@
1
- import { addSearch } from '../../helpers/pageParamsHelper';
2
-
3
- export const refreshPage = (history, params = {}) => {
4
- const url = addSearch(history.location.pathname, params);
5
- history.push(url);
6
- };
@@ -1,73 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import {
4
- Table,
5
- TableHeader,
6
- TableBody,
7
- } from '@patternfly/react-table/deprecated';
8
- import { Flex, FlexItem } from '@patternfly/react-core';
9
- import Pagination from 'foremanReact/components/Pagination';
10
- import { refreshPage } from './IndexTableHelper';
11
-
12
- const IndexTable = ({
13
- history,
14
- pagination,
15
- totalCount,
16
- toolbarBtns,
17
- ariaTableLabel,
18
- ouiaTableId,
19
- columns,
20
- ...rest
21
- }) => {
22
- const handlePerPageSelected = perPage => {
23
- refreshPage(history, { page: 1, perPage });
24
- };
25
-
26
- const handlePageSelected = page => {
27
- refreshPage(history, { ...pagination, page });
28
- };
29
-
30
- return (
31
- <React.Fragment>
32
- <Flex className="pf-v5-u-pt-md">
33
- <FlexItem>{toolbarBtns}</FlexItem>
34
- <FlexItem align={{ default: 'alignRight' }}>
35
- <Pagination
36
- itemCount={totalCount}
37
- page={pagination.page}
38
- perPage={pagination.perPage}
39
- onSetPage={handlePageSelected}
40
- onPerPageSelect={handlePerPageSelected}
41
- variant="top"
42
- />
43
- </FlexItem>
44
- </Flex>
45
- <Table
46
- ouiaId={ouiaTableId}
47
- aria-label={ariaTableLabel}
48
- cells={columns}
49
- {...rest}
50
- variant="compact"
51
- >
52
- <TableHeader />
53
- <TableBody />
54
- </Table>
55
- </React.Fragment>
56
- );
57
- };
58
-
59
- IndexTable.propTypes = {
60
- history: PropTypes.object.isRequired,
61
- pagination: PropTypes.object.isRequired,
62
- toolbarBtns: PropTypes.node,
63
- totalCount: PropTypes.number.isRequired,
64
- ariaTableLabel: PropTypes.string.isRequired,
65
- ouiaTableId: PropTypes.string.isRequired,
66
- columns: PropTypes.array.isRequired,
67
- };
68
-
69
- IndexTable.defaultProps = {
70
- toolbarBtns: null,
71
- };
72
-
73
- export default IndexTable;
@@ -1,42 +0,0 @@
1
- import React from 'react';
2
- import { Link } from 'react-router-dom';
3
- import { Button } from '@patternfly/react-core';
4
- import PropTypes from 'prop-types';
5
-
6
- const LinkButton = ({
7
- path,
8
- btnVariant,
9
- btnText,
10
- isDisabled,
11
- btnAriaLabel,
12
- ouiaId,
13
- }) => (
14
- <Link to={path}>
15
- <Button
16
- ouiaId={ouiaId}
17
- variant={btnVariant}
18
- isDisabled={isDisabled}
19
- aria-label={btnAriaLabel}
20
- >
21
- {btnText}
22
- </Button>
23
- </Link>
24
- );
25
-
26
- LinkButton.propTypes = {
27
- path: PropTypes.string.isRequired,
28
- btnText: PropTypes.string.isRequired,
29
- btnVariant: PropTypes.string,
30
- isDisabled: PropTypes.bool,
31
- btnAriaLabel: PropTypes.string,
32
- ouiaId: PropTypes.string,
33
- };
34
-
35
- LinkButton.defaultProps = {
36
- btnVariant: 'primary',
37
- isDisabled: false,
38
- btnAriaLabel: null,
39
- ouiaId: 'oscap-link-button',
40
- };
41
-
42
- export default LinkButton;
@@ -1,51 +0,0 @@
1
- import React, { useState } from 'react';
2
- import PropTypes from 'prop-types';
3
- import { translate as __, sprintf } from 'foremanReact/common/I18n';
4
- import ConfirmModal from './ConfirmModal';
5
-
6
- const withDelete = Component => {
7
- const Subcomponent = ({
8
- confirmDeleteTitle,
9
- submitDelete,
10
- prepareMutation,
11
- ...rest
12
- }) => {
13
- const [toDelete, setToDelete] = useState(null);
14
-
15
- const toggleModal = (item = null) => {
16
- setToDelete(item);
17
- };
18
-
19
- return (
20
- <React.Fragment>
21
- <Component {...rest} toggleModal={toggleModal} />
22
- <ConfirmModal
23
- title={confirmDeleteTitle}
24
- text={
25
- toDelete
26
- ? sprintf(
27
- __('Are you sure you want to delete %s?'),
28
- toDelete.name
29
- )
30
- : ''
31
- }
32
- onClose={toggleModal}
33
- isOpen={!!toDelete}
34
- onConfirm={submitDelete}
35
- prepareMutation={() => prepareMutation(toggleModal)}
36
- record={toDelete}
37
- />
38
- </React.Fragment>
39
- );
40
- };
41
-
42
- Subcomponent.propTypes = {
43
- confirmDeleteTitle: PropTypes.string.isRequired,
44
- submitDelete: PropTypes.func.isRequired,
45
- prepareMutation: PropTypes.func.isRequired,
46
- };
47
-
48
- return Subcomponent;
49
- };
50
-
51
- export default withDelete;
@@ -1,107 +0,0 @@
1
- import React, { useEffect } from 'react';
2
- import PropTypes from 'prop-types';
3
- import { translate as __ } from 'foremanReact/common/I18n';
4
- import Loading from 'foremanReact/components/Loading';
5
- import {
6
- permissionCheck,
7
- permissionDeniedMsg,
8
- } from '../helpers/permissionsHelper';
9
-
10
- import EmptyState from './EmptyState';
11
-
12
- const errorStateTitle = __('Error!');
13
-
14
- const pluckData = (data, path) => {
15
- const split = path.split('.');
16
- return split.reduce((memo, item) => {
17
- if (item) {
18
- return memo[item];
19
- }
20
- throw new Error('Unexpected empty segment in response data path');
21
- }, data);
22
- };
23
-
24
- const withLoading = Component => {
25
- const Subcomponent = ({
26
- fetchFn,
27
- resultPath,
28
- renameData,
29
- emptyStateTitle,
30
- emptyStateBody,
31
- permissions,
32
- primaryButton,
33
- shouldRefetch,
34
- ...rest
35
- }) => {
36
- const { loading, error, data, refetch } = fetchFn(rest);
37
-
38
- useEffect(() => {
39
- if (shouldRefetch) {
40
- refetch();
41
- }
42
- }, [shouldRefetch, refetch]);
43
-
44
- if (loading) {
45
- return <Loading />;
46
- }
47
-
48
- if (error) {
49
- return (
50
- <EmptyState
51
- error={error}
52
- title={errorStateTitle}
53
- body={error.message}
54
- />
55
- );
56
- }
57
-
58
- const check = permissionCheck(data.currentUser, permissions);
59
-
60
- if (!check.allowed) {
61
- return (
62
- <EmptyState
63
- lock
64
- title={__('Permission denied')}
65
- body={permissionDeniedMsg(check.permissions.map(item => item.name))}
66
- />
67
- );
68
- }
69
-
70
- const result = pluckData(data, resultPath);
71
-
72
- if ((Array.isArray(result) && result.length === 0) || !result) {
73
- return (
74
- <EmptyState
75
- title={emptyStateTitle}
76
- body={emptyStateBody}
77
- primaryButton={primaryButton}
78
- />
79
- );
80
- }
81
-
82
- return <Component {...rest} {...renameData(data)} />;
83
- };
84
-
85
- Subcomponent.propTypes = {
86
- fetchFn: PropTypes.func.isRequired,
87
- resultPath: PropTypes.string.isRequired,
88
- renameData: PropTypes.func,
89
- emptyStateTitle: PropTypes.string.isRequired,
90
- emptyStateBody: PropTypes.string,
91
- permissions: PropTypes.array,
92
- primaryButton: PropTypes.node,
93
- shouldRefetch: PropTypes.bool,
94
- };
95
-
96
- Subcomponent.defaultProps = {
97
- renameData: data => data,
98
- permissions: [],
99
- primaryButton: null,
100
- shouldRefetch: false,
101
- emptyStateBody: '',
102
- };
103
-
104
- return Subcomponent;
105
- };
106
-
107
- export default withLoading;
@@ -1 +0,0 @@
1
- export const last = array => array[array.length - 1];
@@ -1,15 +0,0 @@
1
- import { last } from './commonHelper';
2
-
3
- const idSeparator = '-';
4
- const versionSeparator = ':';
5
- const defaultVersion = '01';
6
-
7
- export const decodeModelId = model => decodeId(model.id);
8
-
9
- export const decodeId = globalId => {
10
- const split = atob(globalId).split(idSeparator);
11
- return parseInt(last(split), 10);
12
- };
13
-
14
- export const encodeId = (typename, id) =>
15
- btoa([defaultVersion, versionSeparator, typename, idSeparator, id].join(''));
@@ -1,68 +0,0 @@
1
- import { useMutation } from '@apollo/client';
2
- import { translate as __, sprintf } from 'foremanReact/common/I18n';
3
-
4
- import { useCurrentPagination, pageToVars } from './pageParamsHelper';
5
-
6
- const formatError = (error, name) =>
7
- sprintf(__('There was a following error when deleting %(name)s: %(error)s'), {
8
- name,
9
- error,
10
- });
11
-
12
- const joinErrors = errors => errors.map(err => err.message).join(', ');
13
-
14
- const onError = (showToast, resourceName) => error => {
15
- showToast({ type: 'error', message: formatError(error, resourceName) });
16
- };
17
-
18
- const onCompleted = (
19
- toggleModal,
20
- showToast,
21
- mutationName,
22
- successMsg,
23
- resourceName
24
- ) => data => {
25
- toggleModal();
26
- const { errors } = data[mutationName];
27
- if (Array.isArray(errors) && errors.length > 0) {
28
- showToast({
29
- type: 'error',
30
- message: formatError(joinErrors(errors), resourceName),
31
- });
32
- } else {
33
- showToast({
34
- type: 'success',
35
- message: successMsg,
36
- });
37
- }
38
- };
39
-
40
- export const prepareMutation = (
41
- history,
42
- showToast,
43
- refetchQuery,
44
- mutationName,
45
- successMsg,
46
- mutation,
47
- resourceName
48
- ) => toggleModal => {
49
- const pagination = pageToVars(useCurrentPagination(history));
50
-
51
- const options = {
52
- refetchQueries: [{ query: refetchQuery, variables: pagination }],
53
- onCompleted: onCompleted(
54
- toggleModal,
55
- showToast,
56
- mutationName,
57
- successMsg,
58
- resourceName
59
- ),
60
- onError: onError(showToast, resourceName),
61
- };
62
-
63
- return useMutation(mutation, options);
64
- };
65
-
66
- export const submitDelete = (mutation, id) => {
67
- mutation({ variables: { id } });
68
- };
@@ -1,31 +0,0 @@
1
- import URI from 'urijs';
2
- import { useForemanSettings } from 'foremanReact/Root/Context/ForemanContext';
3
-
4
- const parsePageParams = history => URI.parseQuery(history.location.search);
5
-
6
- export const addSearch = (basePath, params) => {
7
- let stringyfied = '';
8
- if (Object.keys(params).length > 0) {
9
- stringyfied = `?${URI.buildQuery(params)}`;
10
- }
11
-
12
- return `${basePath}${stringyfied}`;
13
- };
14
-
15
- export const useCurrentPagination = history => {
16
- const pageParams = parsePageParams(history);
17
- const uiSettings = useForemanSettings();
18
-
19
- return {
20
- page: parseInt(pageParams.page, 10) || 1,
21
- perPage: parseInt(pageParams.perPage, 10) || uiSettings.perPage,
22
- };
23
- };
24
-
25
- export const pageToVars = pagination => ({
26
- first: pagination.page * pagination.perPage,
27
- last: pagination.perPage,
28
- });
29
-
30
- export const useParamsToVars = history =>
31
- pageToVars(useCurrentPagination(history));
@@ -1,42 +0,0 @@
1
- import { translate as __, sprintf } from 'foremanReact/common/I18n';
2
-
3
- export const permissionCheck = (user, permissionsRequired) => {
4
- if (permissionsRequired.length === 0) {
5
- return { allowed: true };
6
- }
7
-
8
- if (!user) {
9
- throw new Error(
10
- 'No user data when loading the page - cannot determine if current user is allowed to view the page.'
11
- );
12
- }
13
-
14
- if (user.admin) {
15
- return { allowed: true };
16
- }
17
-
18
- const permList = permissionsRequired.reduce((memo, item) => {
19
- const found = user.permissions.nodes.find(
20
- permission => permission.name === item
21
- );
22
- memo.push({ name: item, present: !!found });
23
- return memo;
24
- }, []);
25
-
26
- if (permList.reduce((memo, item) => memo && item.present, true)) {
27
- return { allowed: true, permissions: permList };
28
- }
29
-
30
- return { allowed: false, permissions: permList };
31
- };
32
-
33
- export const permissionDeniedMsg = permissions => {
34
- let msg = __('You are not authorized to view the page. ');
35
- if (permissions?.length > 0) {
36
- msg += sprintf(
37
- __('Request the following permissions from administrator: %s.'),
38
- permissions.join(', ')
39
- );
40
- }
41
- return msg;
42
- };
@@ -1,9 +0,0 @@
1
- import React from 'react';
2
- import { Link } from 'react-router-dom';
3
- import { TableText } from '@patternfly/react-table';
4
-
5
- export const linkCell = (path, text) => (
6
- <TableText>
7
- <Link to={path}>{text}</Link>
8
- </TableText>
9
- );
@@ -1,3 +0,0 @@
1
- import { addToast } from 'foremanReact/components/ToastsList';
2
-
3
- export const showToast = dispatch => toast => dispatch(addToast(toast));
@@ -1,127 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { Provider } from 'react-redux';
3
- import store from 'foremanReact/redux';
4
- import { MockedProvider } from '@apollo/react-testing';
5
- import { MemoryRouter } from 'react-router-dom';
6
- import { getForemanContext } from 'foremanReact/Root/Context/ForemanContext';
7
- import { waitFor } from '@testing-library/react';
8
-
9
- export const withRedux = Component => props => (
10
- <Provider store={store}>
11
- <Component {...props} />
12
- </Provider>
13
- );
14
-
15
- export const withRouter = Component => props => (
16
- <MemoryRouter>
17
- <Component {...props} />
18
- </MemoryRouter>
19
- );
20
-
21
- export const withMockedProvider = Component => props => {
22
- // eslint-disable-next-line react/prop-types
23
- const { mocks, ...rest } = props;
24
-
25
- const [context, setContext] = useState({
26
- metadata: {
27
- UISettings: {
28
- perPage: 20,
29
- },
30
- },
31
- });
32
-
33
- const contextData = { context, setContext };
34
- const ForemanContext = getForemanContext(contextData);
35
- return (
36
- <ForemanContext.Provider value={contextData}>
37
- <MockedProvider mocks={mocks}>
38
- <Component {...rest} />
39
- </MockedProvider>
40
- </ForemanContext.Provider>
41
- );
42
- };
43
-
44
- // use to resolve async mock requests for apollo MockedProvider
45
- export const tick = () => new Promise(resolve => setTimeout(resolve, 0));
46
-
47
- export const wait = async (tickCount = 1) => {
48
- for (let i = 1; i < tickCount; i++) {
49
- // eslint-disable-next-line no-await-in-loop
50
- await waitFor(tick);
51
- }
52
- return waitFor(tick);
53
- };
54
-
55
- export const historyMock = {
56
- location: {
57
- search: '',
58
- },
59
- };
60
-
61
- export const admin = {
62
- __typename: 'User',
63
- id: 'MDE6VXNlci00',
64
- login: 'admin',
65
- admin: true,
66
- permissions: {
67
- nodes: [],
68
- },
69
- };
70
-
71
- export const userFactory = (login, permissions = []) => ({
72
- __typename: 'User',
73
- id: 'MDE6VXNlci01',
74
- login,
75
- admin: false,
76
- permissions: {
77
- nodes: permissions,
78
- },
79
- });
80
-
81
- export const intruder = userFactory('intruder', [
82
- {
83
- __typename: 'Permission',
84
- id: 'MDE6UGVybWlzc2lvbi0x',
85
- name: 'view_architectures',
86
- },
87
- ]);
88
-
89
- export const viewer = userFactory('viewer', [
90
- {
91
- __typename: 'Permission',
92
- id: 'MDE6UGVybWlzc2lvbi0yOTY=',
93
- name: 'view_oval_contents',
94
- },
95
- {
96
- __typename: 'Permission',
97
- id: 'MDE6UGVybWlzc2lvbi0yNzU=',
98
- name: 'view_oval_policies',
99
- },
100
- ]);
101
-
102
- export const mockFactory = (resultName, query) => (
103
- variables,
104
- modelResults,
105
- { errors = [], currentUser = null } = {}
106
- ) => {
107
- const mock = {
108
- request: {
109
- query,
110
- variables,
111
- },
112
- result: {
113
- data: {
114
- [resultName]: modelResults,
115
- },
116
- },
117
- };
118
-
119
- if (errors.length !== 0) {
120
- mock.result.errors = errors;
121
- }
122
-
123
- if (currentUser) {
124
- mock.result.data.currentUser = currentUser;
125
- }
126
- return [mock];
127
- };