foreman_openscap 5.0.0 → 5.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.
- checksums.yaml +4 -4
- data/app/graphql/mutations/oval_contents/delete.rb +9 -0
- data/app/graphql/mutations/oval_policies/delete.rb +9 -0
- data/app/graphql/mutations/oval_policies/update.rb +15 -0
- data/app/graphql/types/oval_check.rb +11 -0
- data/app/graphql/types/oval_content.rb +2 -0
- data/app/graphql/types/oval_policy.rb +3 -0
- data/app/models/concerns/foreman_openscap/host_extensions.rb +0 -6
- data/app/models/concerns/foreman_openscap/oval_facet_hostgroup_extensions.rb +15 -0
- data/app/models/foreman_openscap/oval_content.rb +2 -0
- data/app/services/foreman_openscap/oval/configure.rb +1 -1
- data/app/services/foreman_openscap/oval/setup.rb +5 -5
- data/app/services/foreman_openscap/oval/setup_check.rb +5 -2
- data/db/migrate/20210819143316_drop_unused_tables.rb +6 -0
- data/lib/foreman_openscap/engine.rb +6 -1
- data/lib/foreman_openscap/version.rb +1 -1
- data/package.json +3 -6
- data/test/graphql/mutations/oval_policies/delete_mutation_test.rb +63 -0
- data/test/graphql/queries/oval_content_query_test.rb +29 -0
- data/test/unit/services/hostgroup_overrider_test.rb +1 -1
- data/test/unit/services/oval/setup_check_test.rb +37 -0
- data/webpack/components/ConfirmModal.js +63 -0
- data/webpack/components/ConfirmModal.scss +3 -0
- data/webpack/components/EditableInput.js +157 -0
- data/webpack/components/EditableInput.scss +3 -0
- data/webpack/components/EmptyState.js +4 -1
- data/webpack/components/IndexLayout.js +11 -4
- data/webpack/components/IndexTable/index.js +17 -17
- data/webpack/components/LinkButton.js +26 -0
- data/webpack/components/withDeleteModal.js +51 -0
- data/webpack/components/withLoading.js +21 -3
- data/webpack/graphql/mutations/deleteOvalContent.gql +9 -0
- data/webpack/graphql/mutations/deleteOvalPolicy.gql +9 -0
- data/webpack/graphql/mutations/updateOvalPolicy.gql +14 -0
- data/webpack/graphql/queries/hostgroups.gql +14 -0
- data/webpack/graphql/queries/ovalContent.gql +8 -0
- data/webpack/graphql/queries/ovalContents.gql +3 -0
- data/webpack/graphql/queries/ovalPolicies.gql +3 -0
- data/webpack/helpers/formFieldsHelper.js +63 -0
- data/webpack/helpers/mutationHelper.js +68 -0
- data/webpack/helpers/pathsHelper.js +5 -0
- data/webpack/helpers/toastHelper.js +3 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsIndex.js +25 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsTable.js +41 -4
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.fixtures.js +105 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.test.js +124 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures.js +61 -59
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.test.js +29 -8
- data/webpack/routes/OvalContents/OvalContentsIndex/index.js +7 -1
- data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.js +138 -0
- data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.scss +3 -0
- data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNewHelper.js +73 -0
- data/webpack/routes/OvalContents/OvalContentsNew/__tests__/OvalContentsNew.test.js +104 -0
- data/webpack/routes/OvalContents/OvalContentsNew/index.js +13 -0
- data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.js +62 -0
- data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.test.js +45 -0
- data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShowHelper.js +0 -0
- data/webpack/routes/OvalContents/OvalContentsShow/index.js +35 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesIndex.js +17 -2
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesTable.js +16 -3
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.fixtures.js +101 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.test.js +117 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.fixtures.js +57 -41
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.test.js +14 -2
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/index.js +7 -1
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/DetailsTab.js +85 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTab.js +49 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTable.js +38 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShow.js +15 -11
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShowHelper.js +77 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.fixtures.js +48 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.test.js +175 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.fixtures.js +28 -1
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.test.js +47 -4
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/index.js +3 -0
- data/webpack/routes/routes.js +14 -0
- data/webpack/testHelper.js +9 -1
- metadata +46 -3
|
@@ -19,42 +19,6 @@ export const pageParamsHistoryMock = {
|
|
|
19
19
|
push: pushMock,
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
const policiesMocks = {
|
|
23
|
-
totalCount: 2,
|
|
24
|
-
nodes: [
|
|
25
|
-
{
|
|
26
|
-
__typename: 'ForemanOpenscap::OvalPolicy',
|
|
27
|
-
id: 'abc',
|
|
28
|
-
name: 'first policy',
|
|
29
|
-
ovalContent: { name: 'first content' },
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
__typename: 'ForemanOpenscap::OvalPolicy',
|
|
33
|
-
id: 'xyz',
|
|
34
|
-
name: 'second policy',
|
|
35
|
-
ovalContent: { name: 'second content' },
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const pagedPoliciesMocks = {
|
|
41
|
-
totalCount: 7,
|
|
42
|
-
nodes: [
|
|
43
|
-
{
|
|
44
|
-
__typename: 'ForemanOpenscap::OvalPolicy',
|
|
45
|
-
id: 'xyz',
|
|
46
|
-
name: 'sixth policy',
|
|
47
|
-
ovalContent: { name: 'sixth content' },
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
__typename: 'ForemanOpenscap::OvalPolicy',
|
|
51
|
-
id: 'abc',
|
|
52
|
-
name: 'seventh policy',
|
|
53
|
-
ovalContent: { name: 'seventh content' },
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
};
|
|
57
|
-
|
|
58
22
|
const viewer = userFactory('viewer', [
|
|
59
23
|
{
|
|
60
24
|
__typename: 'Permission',
|
|
@@ -63,16 +27,54 @@ const viewer = userFactory('viewer', [
|
|
|
63
27
|
},
|
|
64
28
|
]);
|
|
65
29
|
|
|
30
|
+
const firstPolicy = (meta = { canDestroy: true }) => ({
|
|
31
|
+
__typename: 'ForemanOpenscap::OvalPolicy',
|
|
32
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTE=',
|
|
33
|
+
name: 'first policy',
|
|
34
|
+
meta,
|
|
35
|
+
ovalContent: { name: 'first content' },
|
|
36
|
+
});
|
|
37
|
+
const secondPolicy = (meta = { canDestroy: true }) => ({
|
|
38
|
+
__typename: 'ForemanOpenscap::OvalPolicy',
|
|
39
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQw',
|
|
40
|
+
name: 'second policy',
|
|
41
|
+
meta,
|
|
42
|
+
ovalContent: { name: 'second content' },
|
|
43
|
+
});
|
|
44
|
+
const policiesData = {
|
|
45
|
+
totalCount: 2,
|
|
46
|
+
nodes: [firstPolicy(), secondPolicy()],
|
|
47
|
+
};
|
|
48
|
+
|
|
66
49
|
export const mocks = policiesMockFactory(
|
|
67
50
|
{ first: 20, last: 20 },
|
|
68
|
-
|
|
51
|
+
policiesData,
|
|
69
52
|
{ currentUser: admin }
|
|
70
53
|
);
|
|
71
54
|
export const pageParamsMocks = policiesMockFactory(
|
|
72
55
|
{ first: 10, last: 5 },
|
|
73
|
-
|
|
56
|
+
{
|
|
57
|
+
totalCount: 7,
|
|
58
|
+
nodes: [
|
|
59
|
+
{
|
|
60
|
+
__typename: 'ForemanOpenscap::OvalPolicy',
|
|
61
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQx',
|
|
62
|
+
name: 'sixth policy',
|
|
63
|
+
meta: { canDestroy: true },
|
|
64
|
+
ovalContent: { name: 'sixth content' },
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
__typename: 'ForemanOpenscap::OvalPolicy',
|
|
68
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQy',
|
|
69
|
+
name: 'seventh policy',
|
|
70
|
+
meta: { canDestroy: true },
|
|
71
|
+
ovalContent: { name: 'seventh content' },
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
},
|
|
74
75
|
{ currentUser: admin }
|
|
75
76
|
);
|
|
77
|
+
|
|
76
78
|
export const emptyMocks = policiesMockFactory(
|
|
77
79
|
{ first: 20, last: 20 },
|
|
78
80
|
{ totalCount: 0, nodes: [] },
|
|
@@ -81,15 +83,29 @@ export const emptyMocks = policiesMockFactory(
|
|
|
81
83
|
export const errorMocks = policiesMockFactory(
|
|
82
84
|
{ first: 20, last: 20 },
|
|
83
85
|
{ totalCount: 0, nodes: [] },
|
|
84
|
-
{
|
|
86
|
+
{
|
|
87
|
+
errors: [{ message: 'Something very bad happened.', path: 'base' }],
|
|
88
|
+
currentUser: admin,
|
|
89
|
+
}
|
|
85
90
|
);
|
|
86
91
|
export const viewerMocks = policiesMockFactory(
|
|
87
92
|
{ first: 20, last: 20 },
|
|
88
|
-
|
|
93
|
+
policiesData,
|
|
89
94
|
{ currentUser: viewer }
|
|
90
95
|
);
|
|
91
96
|
export const unauthorizedMocks = policiesMockFactory(
|
|
92
97
|
{ first: 20, last: 20 },
|
|
93
|
-
|
|
98
|
+
policiesData,
|
|
94
99
|
{ currentUser: intruder }
|
|
95
100
|
);
|
|
101
|
+
export const noDeleteMocks = policiesMockFactory(
|
|
102
|
+
{ first: 20, last: 20 },
|
|
103
|
+
{
|
|
104
|
+
totalCount: 2,
|
|
105
|
+
nodes: [
|
|
106
|
+
firstPolicy({ canDestroy: false }),
|
|
107
|
+
secondPolicy({ canDestroy: false }),
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
{ currentUser: admin }
|
|
111
|
+
);
|
|
@@ -7,6 +7,7 @@ import '@testing-library/jest-dom';
|
|
|
7
7
|
import {
|
|
8
8
|
withMockedProvider,
|
|
9
9
|
withRouter,
|
|
10
|
+
withRedux,
|
|
10
11
|
tick,
|
|
11
12
|
historyMock,
|
|
12
13
|
} from '../../../../testHelper';
|
|
@@ -22,10 +23,12 @@ import {
|
|
|
22
23
|
unauthorizedMocks,
|
|
23
24
|
} from './OvalPoliciesIndex.fixtures';
|
|
24
25
|
|
|
25
|
-
import OvalPoliciesIndex from '../
|
|
26
|
+
import OvalPoliciesIndex from '../index';
|
|
26
27
|
import { ovalPoliciesPath } from '../../../../helpers/pathsHelper';
|
|
27
28
|
|
|
28
|
-
const TestComponent = withRouter(
|
|
29
|
+
const TestComponent = withRouter(
|
|
30
|
+
withRedux(withMockedProvider(OvalPoliciesIndex))
|
|
31
|
+
);
|
|
29
32
|
|
|
30
33
|
describe('OvalPoliciesIndex', () => {
|
|
31
34
|
it('should load page', async () => {
|
|
@@ -42,6 +45,15 @@ describe('OvalPoliciesIndex', () => {
|
|
|
42
45
|
expect(within(pageItems).getByText(/1 - 2/)).toBeInTheDocument();
|
|
43
46
|
expect(within(pageItems).getByText('of')).toBeInTheDocument();
|
|
44
47
|
expect(within(pageItems).getByText('2')).toBeInTheDocument();
|
|
48
|
+
|
|
49
|
+
expect(screen.getByText('first policy').closest('a')).toHaveAttribute(
|
|
50
|
+
'href',
|
|
51
|
+
'/experimental/compliance/oval_policies/1'
|
|
52
|
+
);
|
|
53
|
+
expect(screen.getByText('second policy').closest('a')).toHaveAttribute(
|
|
54
|
+
'href',
|
|
55
|
+
'/experimental/compliance/oval_policies/40'
|
|
56
|
+
);
|
|
45
57
|
});
|
|
46
58
|
it('should load page with page params', async () => {
|
|
47
59
|
const { container } = render(
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { useDispatch } from 'react-redux';
|
|
3
|
+
import { showToast } from '../../../helpers/toastHelper';
|
|
2
4
|
|
|
3
5
|
import OvalPoliciesIndex from './OvalPoliciesIndex';
|
|
4
6
|
|
|
5
|
-
const WrappedOvalPoliciesIndex = props =>
|
|
7
|
+
const WrappedOvalPoliciesIndex = props => {
|
|
8
|
+
const dispatch = useDispatch();
|
|
9
|
+
|
|
10
|
+
return <OvalPoliciesIndex {...props} showToast={showToast(dispatch)} />;
|
|
11
|
+
};
|
|
6
12
|
|
|
7
13
|
export default WrappedOvalPoliciesIndex;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { useMutation } from '@apollo/client';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
TextList,
|
|
7
|
+
TextContent,
|
|
8
|
+
TextArea,
|
|
9
|
+
TextListItem,
|
|
10
|
+
TextListVariants,
|
|
11
|
+
TextListItemVariants,
|
|
12
|
+
TextInput,
|
|
13
|
+
} from '@patternfly/react-core';
|
|
14
|
+
|
|
15
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
16
|
+
|
|
17
|
+
import EditableInput from '../../../components/EditableInput';
|
|
18
|
+
|
|
19
|
+
import { onAttrUpdate, policySchedule } from './OvalPoliciesShowHelper';
|
|
20
|
+
import updateOvalPolicyMutation from '../../../graphql/mutations/updateOvalPolicy.gql';
|
|
21
|
+
|
|
22
|
+
const DetailsTab = props => {
|
|
23
|
+
const { policy, showToast } = props;
|
|
24
|
+
|
|
25
|
+
const [callMutation] = useMutation(updateOvalPolicyMutation);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<TextContent className="pf-u-pt-md">
|
|
29
|
+
<TextList component={TextListVariants.dl}>
|
|
30
|
+
<TextListItem component={TextListItemVariants.dt}>
|
|
31
|
+
{__('Name')}
|
|
32
|
+
</TextListItem>
|
|
33
|
+
<TextListItem
|
|
34
|
+
aria-label="label text value"
|
|
35
|
+
component={TextListItemVariants.dd}
|
|
36
|
+
className="foreman-spaced-list"
|
|
37
|
+
>
|
|
38
|
+
<EditableInput
|
|
39
|
+
value={policy.name}
|
|
40
|
+
onConfirm={onAttrUpdate('name', policy, callMutation, showToast)}
|
|
41
|
+
component={TextInput}
|
|
42
|
+
attrName="name"
|
|
43
|
+
/>
|
|
44
|
+
</TextListItem>
|
|
45
|
+
<TextListItem component={TextListItemVariants.dt}>
|
|
46
|
+
{__('Period')}
|
|
47
|
+
</TextListItem>
|
|
48
|
+
<TextListItem
|
|
49
|
+
aria-label="label text value"
|
|
50
|
+
component={TextListItemVariants.dd}
|
|
51
|
+
className="foreman-spaced-list"
|
|
52
|
+
>
|
|
53
|
+
{policySchedule(policy)}
|
|
54
|
+
</TextListItem>
|
|
55
|
+
<TextListItem component={TextListItemVariants.dt}>
|
|
56
|
+
{__('Description')}
|
|
57
|
+
</TextListItem>
|
|
58
|
+
<TextListItem
|
|
59
|
+
aria-label="label text value"
|
|
60
|
+
component={TextListItemVariants.dd}
|
|
61
|
+
className="foreman-spaced-list"
|
|
62
|
+
>
|
|
63
|
+
<EditableInput
|
|
64
|
+
value={policy.description}
|
|
65
|
+
onConfirm={onAttrUpdate(
|
|
66
|
+
'description',
|
|
67
|
+
policy,
|
|
68
|
+
callMutation,
|
|
69
|
+
showToast
|
|
70
|
+
)}
|
|
71
|
+
component={TextArea}
|
|
72
|
+
attrName="description"
|
|
73
|
+
/>
|
|
74
|
+
</TextListItem>
|
|
75
|
+
</TextList>
|
|
76
|
+
</TextContent>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
DetailsTab.propTypes = {
|
|
81
|
+
policy: PropTypes.object.isRequired,
|
|
82
|
+
showToast: PropTypes.func.isRequired,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export default DetailsTab;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
4
|
+
|
|
5
|
+
import { useQuery } from '@apollo/client';
|
|
6
|
+
|
|
7
|
+
import HostgroupsTable from './HostgroupsTable';
|
|
8
|
+
|
|
9
|
+
import hostgroups from '../../../graphql/queries/hostgroups.gql';
|
|
10
|
+
import {
|
|
11
|
+
useParamsToVars,
|
|
12
|
+
useCurrentPagination,
|
|
13
|
+
} from '../../../helpers/pageParamsHelper';
|
|
14
|
+
|
|
15
|
+
const HostgroupsTab = props => {
|
|
16
|
+
const useFetchFn = componentProps =>
|
|
17
|
+
useQuery(hostgroups, {
|
|
18
|
+
variables: {
|
|
19
|
+
search: `oval_policy_id = ${componentProps.match.params.id}`,
|
|
20
|
+
...useParamsToVars(componentProps.history),
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const renameData = data => ({
|
|
25
|
+
hostgroups: data.hostgroups.nodes,
|
|
26
|
+
totalCount: data.hostgroups.totalCount,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const pagination = useCurrentPagination(props.history);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<HostgroupsTable
|
|
33
|
+
{...props}
|
|
34
|
+
fetchFn={useFetchFn}
|
|
35
|
+
renameData={renameData}
|
|
36
|
+
resultPath="hostgroups.nodes"
|
|
37
|
+
pagination={pagination}
|
|
38
|
+
emptyStateTitle={__('No Hostgroups found.')}
|
|
39
|
+
permissions={['view_hostgroups']}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
HostgroupsTab.propTypes = {
|
|
45
|
+
match: PropTypes.object.isRequired,
|
|
46
|
+
history: PropTypes.object.isRequired,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default HostgroupsTab;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
4
|
+
|
|
5
|
+
import withLoading from '../../../components/withLoading';
|
|
6
|
+
import IndexTable from '../../../components/IndexTable';
|
|
7
|
+
|
|
8
|
+
const CvesTable = props => {
|
|
9
|
+
const columns = [{ title: __('Name') }];
|
|
10
|
+
|
|
11
|
+
const rows = props.hostgroups.map(hostgroup => ({
|
|
12
|
+
cells: [{ title: hostgroup.name }],
|
|
13
|
+
hostgroup,
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
const actions = [];
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<IndexTable
|
|
20
|
+
columns={columns}
|
|
21
|
+
rows={rows}
|
|
22
|
+
actions={actions}
|
|
23
|
+
pagination={props.pagination}
|
|
24
|
+
totalCount={props.totalCount}
|
|
25
|
+
history={props.history}
|
|
26
|
+
ariaTableLabel={__('Table of hostgroups for OVAL policy')}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
CvesTable.propTypes = {
|
|
32
|
+
hostgroups: PropTypes.array.isRequired,
|
|
33
|
+
pagination: PropTypes.object.isRequired,
|
|
34
|
+
totalCount: PropTypes.number.isRequired,
|
|
35
|
+
history: PropTypes.object.isRequired,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default withLoading(CvesTable);
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
Button,
|
|
8
8
|
Grid,
|
|
9
9
|
GridItem,
|
|
10
|
-
TextContent,
|
|
11
10
|
Text,
|
|
12
11
|
TextVariants,
|
|
13
12
|
Tabs,
|
|
@@ -16,10 +15,11 @@ import {
|
|
|
16
15
|
} from '@patternfly/react-core';
|
|
17
16
|
|
|
18
17
|
import withLoading from '../../../components/withLoading';
|
|
19
|
-
|
|
20
18
|
import CvesTab from './CvesTab';
|
|
19
|
+
import HostgroupsTab from './HostgroupsTab';
|
|
20
|
+
import DetailsTab from './DetailsTab';
|
|
21
21
|
|
|
22
|
-
import {
|
|
22
|
+
import { newJobFormPath } from './OvalPoliciesShowHelper';
|
|
23
23
|
import { resolvePath } from '../../../helpers/pathsHelper';
|
|
24
24
|
|
|
25
25
|
const OvalPoliciesShow = props => {
|
|
@@ -50,18 +50,22 @@ const OvalPoliciesShow = props => {
|
|
|
50
50
|
<Tabs mountOnEnter activeKey={activeTab} onSelect={handleTabSelect}>
|
|
51
51
|
<Tab
|
|
52
52
|
eventKey="details"
|
|
53
|
-
title={<TabTitleText>Details</TabTitleText>}
|
|
53
|
+
title={<TabTitleText>{__('Details')}</TabTitleText>}
|
|
54
54
|
>
|
|
55
|
-
<
|
|
56
|
-
<Text component={TextVariants.h3}>Period</Text>
|
|
57
|
-
<Text component={TextVariants.p}>{policySchedule(policy)}</Text>
|
|
58
|
-
<Text component={TextVariants.h3}>Description</Text>
|
|
59
|
-
<Text component={TextVariants.p}>{policy.description}</Text>
|
|
60
|
-
</TextContent>
|
|
55
|
+
<DetailsTab {...props} />
|
|
61
56
|
</Tab>
|
|
62
|
-
<Tab
|
|
57
|
+
<Tab
|
|
58
|
+
eventKey="cves"
|
|
59
|
+
title={<TabTitleText>{__('CVEs')}</TabTitleText>}
|
|
60
|
+
>
|
|
63
61
|
<CvesTab {...props} />
|
|
64
62
|
</Tab>
|
|
63
|
+
<Tab
|
|
64
|
+
eventKey="hostgroups"
|
|
65
|
+
title={<TabTitleText>{__('Hostgroups')}</TabTitleText>}
|
|
66
|
+
>
|
|
67
|
+
<HostgroupsTab {...props} />
|
|
68
|
+
</Tab>
|
|
65
69
|
</Tabs>
|
|
66
70
|
</GridItem>
|
|
67
71
|
</Grid>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { translate as __, sprintf } from 'foremanReact/common/I18n';
|
|
2
|
+
|
|
1
3
|
import { decodeId } from '../../../helpers/globalIdHelper';
|
|
2
4
|
import { addSearch } from '../../../helpers/pageParamsHelper';
|
|
3
5
|
import { newJobPath } from '../../../helpers/pathsHelper';
|
|
@@ -37,3 +39,78 @@ export const newJobFormPath = (policy, policyId) =>
|
|
|
37
39
|
host_ids: targetingScopedSearchQuery(policy),
|
|
38
40
|
'inputs[oval_policies]': policyId,
|
|
39
41
|
});
|
|
42
|
+
|
|
43
|
+
const policyToAttrs = (policy, attrs) =>
|
|
44
|
+
Object.entries(policy).reduce((memo, [key, value]) => {
|
|
45
|
+
if (attrs.includes(key)) {
|
|
46
|
+
memo[key] = value;
|
|
47
|
+
}
|
|
48
|
+
return memo;
|
|
49
|
+
}, {});
|
|
50
|
+
|
|
51
|
+
const onUpdateSuccess = (
|
|
52
|
+
closeEditable,
|
|
53
|
+
stopSubmitting,
|
|
54
|
+
showToast,
|
|
55
|
+
attr,
|
|
56
|
+
onValidationError
|
|
57
|
+
) => result => {
|
|
58
|
+
const { errors } = result.data.updateOvalPolicy;
|
|
59
|
+
if (Array.isArray(errors) && errors.length > 0) {
|
|
60
|
+
stopSubmitting();
|
|
61
|
+
if (
|
|
62
|
+
errors.length === 1 &&
|
|
63
|
+
errors[0].path.join(' ') === `attributes ${attr}`
|
|
64
|
+
) {
|
|
65
|
+
onValidationError(errors[0].message);
|
|
66
|
+
} else {
|
|
67
|
+
showToast({
|
|
68
|
+
type: 'error',
|
|
69
|
+
message: formatError(joinErrors(errors)),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
closeEditable();
|
|
74
|
+
showToast({
|
|
75
|
+
type: 'success',
|
|
76
|
+
message: __('OVAL policy was successfully updated.'),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const formatError = error =>
|
|
82
|
+
sprintf(
|
|
83
|
+
__('There was a following error when updating OVAL policy: %s'),
|
|
84
|
+
error
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const joinErrors = errors => errors.map(err => err.message).join(', ');
|
|
88
|
+
|
|
89
|
+
const onUpdateError = (showToast, stopSubmitting) => error => {
|
|
90
|
+
stopSubmitting();
|
|
91
|
+
showToast({ type: 'error', message: formatError(error.message) });
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const onAttrUpdate = (attr, policy, callMutation, showToast) => (
|
|
95
|
+
newValue,
|
|
96
|
+
closeEditable,
|
|
97
|
+
stopSubmitting,
|
|
98
|
+
onValidationError
|
|
99
|
+
) => {
|
|
100
|
+
const vars = policyToAttrs(policy, ['id', 'name', 'description', 'cronLine']);
|
|
101
|
+
vars[attr] = newValue;
|
|
102
|
+
return (
|
|
103
|
+
callMutation({ variables: vars })
|
|
104
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
105
|
+
.then(
|
|
106
|
+
onUpdateSuccess(
|
|
107
|
+
closeEditable,
|
|
108
|
+
stopSubmitting,
|
|
109
|
+
showToast,
|
|
110
|
+
attr,
|
|
111
|
+
onValidationError
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
.catch(onUpdateError(showToast, stopSubmitting))
|
|
115
|
+
);
|
|
116
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { mockFactory } from '../../../../testHelper';
|
|
2
|
+
import updateOvalPolicyMutation from '../../../../graphql/mutations/updateOvalPolicy.gql';
|
|
3
|
+
import { ovalPolicy } from './OvalPoliciesShow.fixtures';
|
|
4
|
+
|
|
5
|
+
const updateOvalPolicyMockFactory = mockFactory(
|
|
6
|
+
'updateOvalPolicy',
|
|
7
|
+
updateOvalPolicyMutation
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
export const updatedName = 'updated policy name';
|
|
11
|
+
|
|
12
|
+
const variables = {
|
|
13
|
+
id: ovalPolicy.id,
|
|
14
|
+
name: updatedName,
|
|
15
|
+
cronLine: ovalPolicy.cronLine,
|
|
16
|
+
description: ovalPolicy.description,
|
|
17
|
+
};
|
|
18
|
+
const responsePolicy = {
|
|
19
|
+
ovalPolicy: {
|
|
20
|
+
__typename: 'ForemanOpenscap::OvalPolicy',
|
|
21
|
+
id: ovalPolicy.id,
|
|
22
|
+
name: updatedName,
|
|
23
|
+
description: ovalPolicy.description,
|
|
24
|
+
cronLine: ovalPolicy.cronLine,
|
|
25
|
+
},
|
|
26
|
+
errors: [],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const policyUpdateMock = updateOvalPolicyMockFactory(
|
|
30
|
+
variables,
|
|
31
|
+
responsePolicy
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
export const policyUpdateErrorMock = updateOvalPolicyMockFactory(
|
|
35
|
+
variables,
|
|
36
|
+
responsePolicy,
|
|
37
|
+
{ errors: [{ message: 'This is an unexpected failure.' }] }
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export const policyUpdateValidationMock = updateOvalPolicyMockFactory(
|
|
41
|
+
variables,
|
|
42
|
+
{
|
|
43
|
+
ovalPolicy,
|
|
44
|
+
errors: [
|
|
45
|
+
{ path: ['attributes', 'name'], message: 'has already been taken' },
|
|
46
|
+
],
|
|
47
|
+
}
|
|
48
|
+
);
|