mirror of
https://github.com/ansible/awx.git
synced 2026-02-26 15:36:04 -03:30
Merge pull request #10376 from mabashian/3831-settings-changes
Changes settings revert all to send DELETE on individual endpoint rather than PATCHing SUMMARY Resolves #3831 #3831 (comment) is a good explanation of the state of the new UI as well as the old. The Misc System settings were the most problematic part of this since that form pulled fields from both /api/v2/settings/system and /api/v2/settings/authentication. In order to revert these fields with a DELETE request I needed to split this form up into Miscellaneous System and Miscellaneous Authentication. The Activity Stream Settings also needed to be absorbed by the Miscellaneous System Settings. All settings forms (except noted below) will now issue a DELETE request to revert all the fields for a particular settings subsection. There was one form that I did not change the Revert All functionality on and that was the LDAP Settings form(s). The UI splits these fields out into 5 different subforms and reverting one (issuing a DELETE on /api/v2/settings/ldap) would revert all the ldap settings which I think is undesirable. This fix has the added benefit of cleaning up future activity stream entries for reverting an entire settings category. I also consulted with @wenottingham and @gamuniz on some fields that were present in the api but missing in the ui and I added those. ISSUE TYPE Feature Pull Request COMPONENT NAME UI Reviewed-by: Alan Rominger <arominge@redhat.com> Reviewed-by: Jake McDermott <yo@jakemcdermott.me> Reviewed-by: Tiago Góes <tiago.goes2009@gmail.com>
This commit is contained in:
@@ -29,6 +29,10 @@ class Settings extends Base {
|
||||
createTest(category, data) {
|
||||
return this.http.post(`${this.baseUrl}${category}/test/`, data);
|
||||
}
|
||||
|
||||
revertCategory(category) {
|
||||
return this.http.delete(`${this.baseUrl}${category}/`);
|
||||
}
|
||||
}
|
||||
|
||||
export default Settings;
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../../../testUtils/enzymeHelpers';
|
||||
import { SettingsProvider } from '../../../../contexts/Settings';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
import { assertDetail } from '../../shared/settingTestUtils';
|
||||
import mockAllOptions from '../../shared/data.allSettingOptions.json';
|
||||
import ActivityStreamDetail from './ActivityStreamDetail';
|
||||
|
||||
jest.mock('../../../../api');
|
||||
|
||||
describe('<ActivityStreamDetail />', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeAll(async () => {
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
ACTIVITY_STREAM_ENABLED: true,
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: false,
|
||||
},
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<ActivityStreamDetail />
|
||||
</SettingsProvider>
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wrapper.unmount();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('initially renders without crashing', () => {
|
||||
expect(wrapper.find('ActivityStreamDetail').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should render expected tabs', () => {
|
||||
const expectedTabs = ['Back to Settings', 'Details'];
|
||||
wrapper.find('RoutedTabs li').forEach((tab, index) => {
|
||||
expect(tab.text()).toEqual(expectedTabs[index]);
|
||||
});
|
||||
});
|
||||
|
||||
test('should render expected details', () => {
|
||||
assertDetail(wrapper, 'Enable Activity Stream', 'On');
|
||||
assertDetail(wrapper, 'Enable Activity Stream for Inventory Sync', 'Off');
|
||||
});
|
||||
|
||||
test('should hide edit button from non-superusers', async () => {
|
||||
const config = {
|
||||
me: {
|
||||
is_superuser: false,
|
||||
},
|
||||
};
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<ActivityStreamDetail />
|
||||
</SettingsProvider>,
|
||||
{
|
||||
context: { config },
|
||||
}
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('Button[aria-label="Edit"]').exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should display content error when api throws error on initial render', async () => {
|
||||
SettingsAPI.readCategory.mockRejectedValue(new Error());
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<ActivityStreamDetail />
|
||||
</SettingsProvider>
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('ContentError').length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './ActivityStreamDetail';
|
||||
@@ -1,130 +0,0 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Formik } from 'formik';
|
||||
import { Form } from '@patternfly/react-core';
|
||||
import { CardBody } from '../../../../components/Card';
|
||||
import ContentError from '../../../../components/ContentError';
|
||||
import ContentLoading from '../../../../components/ContentLoading';
|
||||
import { FormSubmitError } from '../../../../components/FormField';
|
||||
import { FormColumnLayout } from '../../../../components/FormLayout';
|
||||
import { useSettings } from '../../../../contexts/Settings';
|
||||
import {
|
||||
BooleanField,
|
||||
RevertAllAlert,
|
||||
RevertFormActionGroup,
|
||||
} from '../../shared';
|
||||
import useModal from '../../../../util/useModal';
|
||||
import useRequest from '../../../../util/useRequest';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
|
||||
function ActivityStreamEdit() {
|
||||
const history = useHistory();
|
||||
const { isModalOpen, toggleModal, closeModal } = useModal();
|
||||
const { PUT: options } = useSettings();
|
||||
|
||||
const {
|
||||
isLoading,
|
||||
error,
|
||||
request,
|
||||
result: {
|
||||
ACTIVITY_STREAM_ENABLED,
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC,
|
||||
},
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
const { data } = await SettingsAPI.readCategory('system');
|
||||
return {
|
||||
ACTIVITY_STREAM_ENABLED: {
|
||||
...options.ACTIVITY_STREAM_ENABLED,
|
||||
value: data.ACTIVITY_STREAM_ENABLED,
|
||||
},
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: {
|
||||
...options.ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC,
|
||||
value: data.ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC,
|
||||
},
|
||||
};
|
||||
}, [options]),
|
||||
{}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
request();
|
||||
}, [request]);
|
||||
|
||||
const { error: submitError, request: submitForm } = useRequest(
|
||||
useCallback(
|
||||
async values => {
|
||||
await SettingsAPI.updateAll(values);
|
||||
history.push('/settings/activity_stream/details');
|
||||
},
|
||||
[history]
|
||||
),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm(form);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.push('/settings/activity_stream/details');
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = {
|
||||
ACTIVITY_STREAM_ENABLED: ACTIVITY_STREAM_ENABLED.default,
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC:
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC.default,
|
||||
};
|
||||
await submitForm(defaultValues);
|
||||
closeModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<CardBody>
|
||||
{isLoading && <ContentLoading />}
|
||||
{!isLoading && error && <ContentError error={error} />}
|
||||
{!isLoading && ACTIVITY_STREAM_ENABLED && (
|
||||
<Formik
|
||||
initialValues={{
|
||||
ACTIVITY_STREAM_ENABLED: ACTIVITY_STREAM_ENABLED.value,
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC:
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC.value,
|
||||
}}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{formik => {
|
||||
return (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<BooleanField
|
||||
name="ACTIVITY_STREAM_ENABLED"
|
||||
config={ACTIVITY_STREAM_ENABLED}
|
||||
/>
|
||||
<BooleanField
|
||||
name="ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC"
|
||||
config={ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
onRevert={toggleModal}
|
||||
/>
|
||||
{isModalOpen && (
|
||||
<RevertAllAlert
|
||||
onClose={closeModal}
|
||||
onRevertAll={handleRevertAll}
|
||||
/>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
)}
|
||||
</CardBody>
|
||||
);
|
||||
}
|
||||
|
||||
export default ActivityStreamEdit;
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './ActivityStreamEdit';
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './ActivityStream';
|
||||
@@ -55,6 +55,13 @@ function AzureADEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('azuread-oauth2');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
@@ -68,13 +75,11 @@ function AzureADEdit() {
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(azure).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/azure/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -120,6 +125,7 @@ function AzureADEdit() {
|
||||
config={azure.SOCIAL_AUTH_AZUREAD_OAUTH2_TEAM_MAP}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('<AzureADEdit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
@@ -60,7 +61,7 @@ describe('<AzureADEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -75,13 +76,8 @@ describe('<AzureADEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_AZUREAD_OAUTH2_KEY: '',
|
||||
SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET: '',
|
||||
SOCIAL_AUTH_AZUREAD_OAUTH2_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_AZUREAD_OAUTH2_TEAM_MAP: null,
|
||||
});
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('azuread-oauth2');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -55,6 +55,13 @@ function GitHubEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('github');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
@@ -66,13 +73,11 @@ function GitHubEdit() {
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(github).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/github/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -118,6 +123,7 @@ function GitHubEdit() {
|
||||
config={github.SOCIAL_AUTH_GITHUB_TEAM_MAP}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('<GitHubEdit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
@@ -72,7 +73,7 @@ describe('<GitHubEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -87,13 +88,8 @@ describe('<GitHubEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GITHUB_KEY: '',
|
||||
SOCIAL_AUTH_GITHUB_SECRET: '',
|
||||
SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GITHUB_TEAM_MAP: null,
|
||||
});
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('github');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -55,6 +55,13 @@ function GitHubEnterpriseEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('github-enterprise');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
@@ -68,13 +75,11 @@ function GitHubEnterpriseEdit() {
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(github).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/github/enterprise/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -128,6 +133,7 @@ function GitHubEnterpriseEdit() {
|
||||
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_MAP}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('<GitHubEnterpriseEdit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
@@ -82,7 +83,7 @@ describe('<GitHubEnterpriseEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -97,15 +98,10 @@ describe('<GitHubEnterpriseEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_URL: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_MAP: null,
|
||||
});
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith(
|
||||
'github-enterprise'
|
||||
);
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -55,6 +55,13 @@ function GitHubEnterpriseOrgEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('github-enterprise-org');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
@@ -68,13 +75,11 @@ function GitHubEnterpriseOrgEdit() {
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(github).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/github/enterprise_organization/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -134,6 +139,7 @@ function GitHubEnterpriseOrgEdit() {
|
||||
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_TEAM_MAP}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('<GitHubEnterpriseOrgEdit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
@@ -94,7 +95,7 @@ describe('<GitHubEnterpriseOrgEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -109,16 +110,10 @@ describe('<GitHubEnterpriseOrgEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SECRET: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_TEAM_MAP: null,
|
||||
});
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith(
|
||||
'github-enterprise-org'
|
||||
);
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -55,6 +55,13 @@ function GitHubEnterpriseTeamEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('github-enterprise-team');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
@@ -68,13 +75,11 @@ function GitHubEnterpriseTeamEdit() {
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(github).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/github/enterprise_team/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -134,6 +139,7 @@ function GitHubEnterpriseTeamEdit() {
|
||||
config={github.SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_TEAM_MAP}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('<GitHubEnterpriseTeamEdit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
@@ -88,7 +89,7 @@ describe('<GitHubEnterpriseTeamEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -103,16 +104,10 @@ describe('<GitHubEnterpriseTeamEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SECRET: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID: '',
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_TEAM_MAP: null,
|
||||
});
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith(
|
||||
'github-enterprise-team'
|
||||
);
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -55,6 +55,13 @@ function GitHubOrgEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('github-org');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
@@ -68,13 +75,11 @@ function GitHubOrgEdit() {
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(github).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/github/organization/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -124,6 +129,7 @@ function GitHubOrgEdit() {
|
||||
config={github.SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('<GitHubOrgEdit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
@@ -79,7 +80,7 @@ describe('<GitHubOrgEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -94,14 +95,8 @@ describe('<GitHubOrgEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GITHUB_ORG_KEY: '',
|
||||
SOCIAL_AUTH_GITHUB_ORG_SECRET: '',
|
||||
SOCIAL_AUTH_GITHUB_ORG_NAME: '',
|
||||
SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP: null,
|
||||
});
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('github-org');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -55,6 +55,13 @@ function GitHubTeamEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('github-team');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
@@ -68,13 +75,11 @@ function GitHubTeamEdit() {
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(github).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/github/team/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -124,6 +129,7 @@ function GitHubTeamEdit() {
|
||||
config={github.SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('<GitHubTeamEdit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
@@ -74,7 +75,7 @@ describe('<GitHubTeamEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -89,14 +90,8 @@ describe('<GitHubTeamEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GITHUB_TEAM_KEY: '',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_SECRET: '',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ID: '',
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP: null,
|
||||
});
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('github-team');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -60,6 +60,13 @@ function GoogleOAuth2Edit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('google-oauth2');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
@@ -79,13 +86,11 @@ function GoogleOAuth2Edit() {
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(googleOAuth2).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/google_oauth2/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -148,6 +153,7 @@ function GoogleOAuth2Edit() {
|
||||
config={googleOAuth2.SOCIAL_AUTH_GOOGLE_OAUTH2_TEAM_MAP}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('<GoogleOAuth2Edit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
@@ -82,7 +83,7 @@ describe('<GoogleOAuth2Edit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -97,15 +98,8 @@ describe('<GoogleOAuth2Edit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY: '',
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET: '',
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS: [],
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS: {},
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_TEAM_MAP: null,
|
||||
});
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('google-oauth2');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -63,6 +63,13 @@ function JobsEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('jobs');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
@@ -76,12 +83,11 @@ function JobsEdit() {
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = {};
|
||||
Object.entries(jobs).forEach(([key, value]) => {
|
||||
defaultValues[key] = value.default;
|
||||
});
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/jobs/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -181,6 +187,7 @@ function JobsEdit() {
|
||||
/>
|
||||
<ObjectField name="AWX_TASK_ENV" config={jobs.AWX_TASK_ENV} />
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
} from '../../../../../testUtils/enzymeHelpers';
|
||||
import mockAllOptions from '../../shared/data.allSettingOptions.json';
|
||||
import mockJobSettings from '../../shared/data.jobSettings.json';
|
||||
import mockDefaultJobSettings from './data.defaultJobSettings.json';
|
||||
import { SettingsProvider } from '../../../../contexts/Settings';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
import JobsEdit from './JobsEdit';
|
||||
@@ -19,6 +18,7 @@ describe('<JobsEdit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: mockJobSettings,
|
||||
@@ -51,7 +51,7 @@ describe('<JobsEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -66,8 +66,8 @@ describe('<JobsEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith(mockDefaultJobSettings);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('jobs');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -75,14 +75,19 @@ function LoggingEdit() {
|
||||
});
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = {};
|
||||
Object.entries(logging).forEach(([key, value]) => {
|
||||
defaultValues[key] = value.default;
|
||||
});
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('logging');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
await revertAll();
|
||||
|
||||
await submitForm(defaultValues);
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/logging/details');
|
||||
};
|
||||
|
||||
const {
|
||||
@@ -221,6 +226,7 @@ function LoggingEdit() {
|
||||
config={logging.LOG_AGGREGATOR_LOGGERS}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
|
||||
@@ -35,29 +35,6 @@ const mockSettings = {
|
||||
LOG_AGGREGATOR_MAX_DISK_USAGE_PATH: '/var/lib/awx',
|
||||
LOG_AGGREGATOR_RSYSLOGD_DEBUG: false,
|
||||
};
|
||||
const mockDefaultSettings = {
|
||||
LOG_AGGREGATOR_HOST: null,
|
||||
LOG_AGGREGATOR_PORT: null,
|
||||
LOG_AGGREGATOR_TYPE: null,
|
||||
LOG_AGGREGATOR_USERNAME: '',
|
||||
LOG_AGGREGATOR_PASSWORD: '',
|
||||
LOG_AGGREGATOR_LOGGERS: [
|
||||
'awx',
|
||||
'activity_stream',
|
||||
'job_events',
|
||||
'system_tracking',
|
||||
],
|
||||
LOG_AGGREGATOR_INDIVIDUAL_FACTS: false,
|
||||
LOG_AGGREGATOR_ENABLED: false,
|
||||
LOG_AGGREGATOR_TOWER_UUID: '',
|
||||
LOG_AGGREGATOR_PROTOCOL: 'https',
|
||||
LOG_AGGREGATOR_TCP_TIMEOUT: 5,
|
||||
LOG_AGGREGATOR_VERIFY_CERT: true,
|
||||
LOG_AGGREGATOR_LEVEL: 'INFO',
|
||||
LOG_AGGREGATOR_MAX_DISK_USAGE_GB: 1,
|
||||
LOG_AGGREGATOR_MAX_DISK_USAGE_PATH: '/var/lib/awx',
|
||||
LOG_AGGREGATOR_RSYSLOGD_DEBUG: false,
|
||||
};
|
||||
|
||||
describe('<LoggingEdit />', () => {
|
||||
let wrapper;
|
||||
@@ -68,6 +45,7 @@ describe('<LoggingEdit />', () => {
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: mockSettings,
|
||||
@@ -227,7 +205,7 @@ describe('<LoggingEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -242,8 +220,8 @@ describe('<LoggingEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith(mockDefaultSettings);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('logging');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import React from 'react';
|
||||
import { Link, Redirect, Route, Switch } from 'react-router-dom';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
import { PageSection, Card } from '@patternfly/react-core';
|
||||
import ContentError from '../../../components/ContentError';
|
||||
import { useConfig } from '../../../contexts/Config';
|
||||
import ActivityStreamDetail from './ActivityStreamDetail';
|
||||
import ActivityStreamEdit from './ActivityStreamEdit';
|
||||
import MiscAuthenticationDetail from './MiscAuthenticationDetail';
|
||||
import MiscAuthenticationEdit from './MiscAuthenticationEdit';
|
||||
|
||||
function ActivityStream() {
|
||||
const baseURL = '/settings/activity_stream';
|
||||
function MiscAuthentication() {
|
||||
const baseURL = '/settings/miscellaneous_authentication';
|
||||
const { me } = useConfig();
|
||||
|
||||
return (
|
||||
@@ -18,11 +17,11 @@ function ActivityStream() {
|
||||
<Switch>
|
||||
<Redirect from={baseURL} to={`${baseURL}/details`} exact />
|
||||
<Route path={`${baseURL}/details`}>
|
||||
<ActivityStreamDetail />
|
||||
<MiscAuthenticationDetail />
|
||||
</Route>
|
||||
<Route path={`${baseURL}/edit`}>
|
||||
{me?.is_superuser ? (
|
||||
<ActivityStreamEdit />
|
||||
<MiscAuthenticationEdit />
|
||||
) : (
|
||||
<Redirect to={`${baseURL}/details`} />
|
||||
)}
|
||||
@@ -30,7 +29,7 @@ function ActivityStream() {
|
||||
<Route key="not-found" path={`${baseURL}/*`}>
|
||||
<ContentError isNotFound>
|
||||
<Link to={`${baseURL}/details`}>
|
||||
{t`View Activity Stream settings`}
|
||||
{t`View Miscellaneous Authentication settings`}
|
||||
</Link>
|
||||
</ContentError>
|
||||
</Route>
|
||||
@@ -40,4 +39,4 @@ function ActivityStream() {
|
||||
);
|
||||
}
|
||||
|
||||
export default ActivityStream;
|
||||
export default MiscAuthentication;
|
||||
@@ -5,55 +5,57 @@ import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../../testUtils/enzymeHelpers';
|
||||
import ActivityStream from './ActivityStream';
|
||||
import { SettingsAPI } from '../../../api';
|
||||
import MiscAuthentication from './MiscAuthentication';
|
||||
|
||||
jest.mock('../../../api/models/Settings');
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
ACTIVITY_STREAM_ENABLED: true,
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: false,
|
||||
},
|
||||
});
|
||||
jest.mock('../../../api');
|
||||
|
||||
describe('<ActivityStream />', () => {
|
||||
describe('<MiscAuthentication />', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.unmount();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('should render activity stream details', async () => {
|
||||
test('should render miscellaneous authentication details', async () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/settings/activity_stream/details'],
|
||||
initialEntries: ['/settings/miscellaneous_authentication/details'],
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<ActivityStream />, {
|
||||
wrapper = mountWithContexts(<MiscAuthentication />, {
|
||||
context: { router: { history } },
|
||||
});
|
||||
});
|
||||
expect(wrapper.find('ActivityStreamDetail').length).toBe(1);
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('MiscAuthenticationDetail').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should render activity stream edit', async () => {
|
||||
test('should render miscellaneous authentication edit', async () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/settings/activity_stream/edit'],
|
||||
initialEntries: ['/settings/miscellaneous_authentication/edit'],
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<ActivityStream />, {
|
||||
wrapper = mountWithContexts(<MiscAuthentication />, {
|
||||
context: { router: { history } },
|
||||
});
|
||||
});
|
||||
expect(wrapper.find('ActivityStreamEdit').length).toBe(1);
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('MiscAuthenticationEdit').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should show content error when user navigates to erroneous route', async () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/settings/activity_stream/foo'],
|
||||
initialEntries: ['/settings/miscellaneous_authentication/foo'],
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<ActivityStream />, {
|
||||
wrapper = mountWithContexts(<MiscAuthentication />, {
|
||||
context: { router: { history } },
|
||||
});
|
||||
});
|
||||
@@ -62,10 +64,10 @@ describe('<ActivityStream />', () => {
|
||||
|
||||
test('should redirect to details for users without system admin permissions', async () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/settings/activity_stream/edit'],
|
||||
initialEntries: ['/settings/miscellaneous_authentication/edit'],
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<ActivityStream />, {
|
||||
wrapper = mountWithContexts(<MiscAuthentication />, {
|
||||
context: {
|
||||
router: {
|
||||
history,
|
||||
@@ -79,7 +81,7 @@ describe('<ActivityStream />', () => {
|
||||
});
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('ActivityStreamDetail').length).toBe(1);
|
||||
expect(wrapper.find('ActivityStreamEdit').length).toBe(0);
|
||||
expect(wrapper.find('MiscAuthenticationDetail').length).toBe(1);
|
||||
expect(wrapper.find('MiscAuthenticationEdit').length).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -1,36 +1,27 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
import { CaretLeftIcon } from '@patternfly/react-icons';
|
||||
import { Button } from '@patternfly/react-core';
|
||||
import { CaretLeftIcon } from '@patternfly/react-icons';
|
||||
import { CardBody, CardActionsRow } from '../../../../components/Card';
|
||||
import ContentLoading from '../../../../components/ContentLoading';
|
||||
import ContentError from '../../../../components/ContentError';
|
||||
import { DetailList } from '../../../../components/DetailList';
|
||||
import RoutedTabs from '../../../../components/RoutedTabs';
|
||||
import useRequest from '../../../../util/useRequest';
|
||||
import { useConfig } from '../../../../contexts/Config';
|
||||
import { useSettings } from '../../../../contexts/Settings';
|
||||
import useRequest from '../../../../util/useRequest';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
import { SettingDetail } from '../../shared';
|
||||
|
||||
function ActivityStreamDetail() {
|
||||
function MiscAuthenticationDetail() {
|
||||
const { me } = useConfig();
|
||||
const { GET: options } = useSettings();
|
||||
|
||||
const { isLoading, error, request, result: activityStream } = useRequest(
|
||||
const { isLoading, error, request, result: authentication } = useRequest(
|
||||
useCallback(async () => {
|
||||
const {
|
||||
data: {
|
||||
ACTIVITY_STREAM_ENABLED,
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC,
|
||||
},
|
||||
} = await SettingsAPI.readCategory('system');
|
||||
return {
|
||||
ACTIVITY_STREAM_ENABLED,
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC,
|
||||
};
|
||||
const { data } = await SettingsAPI.readCategory('authentication');
|
||||
return data;
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
@@ -52,7 +43,7 @@ function ActivityStreamDetail() {
|
||||
},
|
||||
{
|
||||
name: t`Details`,
|
||||
link: `/settings/activity_stream/details`,
|
||||
link: `/settings/miscellaneous_authentication/details`,
|
||||
id: 0,
|
||||
},
|
||||
];
|
||||
@@ -63,9 +54,9 @@ function ActivityStreamDetail() {
|
||||
<CardBody>
|
||||
{isLoading && <ContentLoading />}
|
||||
{!isLoading && error && <ContentError error={error} />}
|
||||
{!isLoading && activityStream && (
|
||||
{!isLoading && authentication && (
|
||||
<DetailList>
|
||||
{Object.keys(activityStream).map(key => {
|
||||
{Object.keys(authentication).map(key => {
|
||||
const record = options?.[key];
|
||||
return (
|
||||
<SettingDetail
|
||||
@@ -75,7 +66,7 @@ function ActivityStreamDetail() {
|
||||
label={record?.label}
|
||||
type={record?.type}
|
||||
unit={record?.unit}
|
||||
value={activityStream?.[key]}
|
||||
value={authentication?.[key]}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -84,10 +75,10 @@ function ActivityStreamDetail() {
|
||||
{me?.is_superuser && (
|
||||
<CardActionsRow>
|
||||
<Button
|
||||
ouiaId="activity-stream-detail-edit-button"
|
||||
ouiaId="authentication-detail-edit-button"
|
||||
aria-label={t`Edit`}
|
||||
component={Link}
|
||||
to="/settings/activity_stream/edit"
|
||||
to="/settings/miscellaneous_authentication/edit"
|
||||
>
|
||||
{t`Edit`}
|
||||
</Button>
|
||||
@@ -98,4 +89,4 @@ function ActivityStreamDetail() {
|
||||
);
|
||||
}
|
||||
|
||||
export default ActivityStreamDetail;
|
||||
export default MiscAuthenticationDetail;
|
||||
@@ -0,0 +1,128 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../../../testUtils/enzymeHelpers';
|
||||
import { SettingsProvider } from '../../../../contexts/Settings';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
import {
|
||||
assertDetail,
|
||||
assertVariableDetail,
|
||||
} from '../../shared/settingTestUtils';
|
||||
import mockAllOptions from '../../shared/data.allSettingOptions.json';
|
||||
import MiscAuthenticationDetail from './MiscAuthenticationDetail';
|
||||
|
||||
jest.mock('../../../../api');
|
||||
|
||||
describe('<MiscAuthenticationDetail />', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(async () => {
|
||||
SettingsAPI.readCategory = jest.fn();
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
SESSION_COOKIE_AGE: 1800,
|
||||
SESSIONS_PER_USER: -1,
|
||||
DISABLE_LOCAL_AUTH: false,
|
||||
AUTH_BASIC_ENABLED: true,
|
||||
OAUTH2_PROVIDER: {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS: 31536000000,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS: 2628000,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS: 600,
|
||||
},
|
||||
ALLOW_OAUTH2_FOR_EXTERNAL_USERS: false,
|
||||
LOGIN_REDIRECT_OVERRIDE: 'https://foohost',
|
||||
AUTHENTICATION_BACKENDS: [
|
||||
'awx.sso.backends.TACACSPlusBackend',
|
||||
'awx.main.backends.AWXModelBackend',
|
||||
],
|
||||
SOCIAL_AUTH_ORGANIZATION_MAP: {},
|
||||
SOCIAL_AUTH_TEAM_MAP: {},
|
||||
SOCIAL_AUTH_USER_FIELDS: [],
|
||||
},
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<MiscAuthenticationDetail />
|
||||
</SettingsProvider>
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
});
|
||||
|
||||
test('initially renders without crashing', () => {
|
||||
expect(wrapper.find('MiscAuthenticationDetail').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should render expected tabs', () => {
|
||||
const expectedTabs = ['Back to Settings', 'Details'];
|
||||
wrapper.find('RoutedTabs li').forEach((tab, index) => {
|
||||
expect(tab.text()).toEqual(expectedTabs[index]);
|
||||
});
|
||||
});
|
||||
|
||||
test('should render expected details', () => {
|
||||
assertDetail(wrapper, 'Disable the built-in authentication system', 'Off');
|
||||
assertVariableDetail(
|
||||
wrapper,
|
||||
'OAuth 2 Timeout Settings',
|
||||
'{\n "ACCESS_TOKEN_EXPIRE_SECONDS": 31536000000,\n "REFRESH_TOKEN_EXPIRE_SECONDS": 2628000,\n "AUTHORIZATION_CODE_EXPIRE_SECONDS": 600\n}'
|
||||
);
|
||||
assertDetail(wrapper, 'Login redirect override URL', 'https://foohost');
|
||||
assertVariableDetail(
|
||||
wrapper,
|
||||
'Authentication Backends',
|
||||
'[\n "awx.sso.backends.TACACSPlusBackend",\n "awx.main.backends.AWXModelBackend"\n]'
|
||||
);
|
||||
assertVariableDetail(wrapper, 'Social Auth Organization Map', '{}');
|
||||
assertVariableDetail(wrapper, 'Social Auth Team Map', '{}');
|
||||
assertVariableDetail(wrapper, 'Social Auth User Fields', '[]');
|
||||
assertDetail(
|
||||
wrapper,
|
||||
'Allow External Users to Create OAuth2 Tokens',
|
||||
'Off'
|
||||
);
|
||||
assertDetail(wrapper, 'Enable HTTP Basic Auth', 'On');
|
||||
assertDetail(wrapper, 'Idle Time Force Log Out', '1800 seconds');
|
||||
assertDetail(
|
||||
wrapper,
|
||||
'Maximum number of simultaneous logged in sessions',
|
||||
'-1'
|
||||
);
|
||||
});
|
||||
|
||||
test('should hide edit button from non-superusers', async () => {
|
||||
const config = {
|
||||
me: {
|
||||
is_superuser: false,
|
||||
},
|
||||
};
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<MiscAuthenticationDetail />
|
||||
</SettingsProvider>,
|
||||
{
|
||||
context: { config },
|
||||
}
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('Button[aria-label="Edit"]').exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should display content error when api throws error on initial render', async () => {
|
||||
SettingsAPI.readCategory.mockRejectedValue(new Error());
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<MiscAuthenticationDetail />
|
||||
</SettingsProvider>
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
expect(wrapper.find('ContentError').length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './MiscAuthenticationDetail';
|
||||
@@ -0,0 +1,270 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Formik } from 'formik';
|
||||
import { Form } from '@patternfly/react-core';
|
||||
import { CardBody } from '../../../../components/Card';
|
||||
import ContentError from '../../../../components/ContentError';
|
||||
import ContentLoading from '../../../../components/ContentLoading';
|
||||
import { FormSubmitError } from '../../../../components/FormField';
|
||||
import { FormColumnLayout } from '../../../../components/FormLayout';
|
||||
import { useSettings } from '../../../../contexts/Settings';
|
||||
import { RevertAllAlert, RevertFormActionGroup } from '../../shared';
|
||||
import {
|
||||
BooleanField,
|
||||
InputField,
|
||||
ObjectField,
|
||||
} from '../../shared/SharedFields';
|
||||
import useModal from '../../../../util/useModal';
|
||||
import useRequest from '../../../../util/useRequest';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
import { formatJson, pluck } from '../../shared/settingUtils';
|
||||
|
||||
function MiscAuthenticationEdit() {
|
||||
const history = useHistory();
|
||||
const { isModalOpen, toggleModal, closeModal } = useModal();
|
||||
const { PUT: options } = useSettings();
|
||||
|
||||
const {
|
||||
isLoading,
|
||||
error,
|
||||
request: fetchAuthentication,
|
||||
result: authentication,
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
const { data } = await SettingsAPI.readCategory('authentication');
|
||||
|
||||
const {
|
||||
OAUTH2_PROVIDER: {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
},
|
||||
...pluckedAuthenticationData
|
||||
} = pluck(
|
||||
data,
|
||||
'ALLOW_OAUTH2_FOR_EXTERNAL_USERS',
|
||||
'AUTH_BASIC_ENABLED',
|
||||
'LOGIN_REDIRECT_OVERRIDE',
|
||||
'DISABLE_LOCAL_AUTH',
|
||||
'OAUTH2_PROVIDER',
|
||||
'SESSIONS_PER_USER',
|
||||
'SESSION_COOKIE_AGE',
|
||||
'SOCIAL_AUTH_ORGANIZATION_MAP',
|
||||
'SOCIAL_AUTH_TEAM_MAP',
|
||||
'SOCIAL_AUTH_USER_FIELDS'
|
||||
);
|
||||
|
||||
const authenticationData = {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
...pluckedAuthenticationData,
|
||||
};
|
||||
|
||||
const {
|
||||
OAUTH2_PROVIDER: OAUTH2_PROVIDER_OPTIONS,
|
||||
...restOptions
|
||||
} = options;
|
||||
|
||||
const authenticationOptions = {
|
||||
...restOptions,
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS: {
|
||||
...OAUTH2_PROVIDER_OPTIONS,
|
||||
default: OAUTH2_PROVIDER_OPTIONS.default.ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
type: OAUTH2_PROVIDER_OPTIONS.child.type,
|
||||
label: t`Access Token Expiration`,
|
||||
},
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS: {
|
||||
...OAUTH2_PROVIDER_OPTIONS,
|
||||
default: OAUTH2_PROVIDER_OPTIONS.default.REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
type: OAUTH2_PROVIDER_OPTIONS.child.type,
|
||||
label: t`Refresh Token Expiration`,
|
||||
},
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS: {
|
||||
...OAUTH2_PROVIDER_OPTIONS,
|
||||
default:
|
||||
OAUTH2_PROVIDER_OPTIONS.default.AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
type: OAUTH2_PROVIDER_OPTIONS.child.type,
|
||||
label: t`Authorization Code Expiration`,
|
||||
},
|
||||
};
|
||||
|
||||
const mergedData = {};
|
||||
|
||||
Object.keys(authenticationData).forEach(key => {
|
||||
if (!authenticationOptions[key]) {
|
||||
return;
|
||||
}
|
||||
mergedData[key] = authenticationOptions[key];
|
||||
mergedData[key].value = authenticationData[key];
|
||||
});
|
||||
|
||||
return mergedData;
|
||||
}, [options]),
|
||||
null
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAuthentication();
|
||||
}, [fetchAuthentication]);
|
||||
|
||||
const { error: submitError, request: submitForm } = useRequest(
|
||||
useCallback(
|
||||
async values => {
|
||||
await SettingsAPI.updateAll(values);
|
||||
history.push('/settings/miscellaneous_authentication/details');
|
||||
},
|
||||
[history]
|
||||
),
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('authentication');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
const {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
...formData
|
||||
} = form;
|
||||
|
||||
await submitForm({
|
||||
...formData,
|
||||
OAUTH2_PROVIDER: {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
},
|
||||
SOCIAL_AUTH_ORGANIZATION_MAP: formatJson(
|
||||
formData.SOCIAL_AUTH_ORGANIZATION_MAP
|
||||
),
|
||||
SOCIAL_AUTH_TEAM_MAP: formatJson(formData.SOCIAL_AUTH_TEAM_MAP),
|
||||
SOCIAL_AUTH_USER_FIELDS: formatJson(formData.SOCIAL_AUTH_USER_FIELDS),
|
||||
});
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/miscellaneous_authentication/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.push('/settings/miscellaneous_authentication/details');
|
||||
};
|
||||
|
||||
const initialValues = fields =>
|
||||
Object.keys(fields).reduce((acc, key) => {
|
||||
if (fields[key].type === 'list' || fields[key].type === 'nested object') {
|
||||
const emptyDefault = fields[key].type === 'list' ? '[]' : '{}';
|
||||
acc[key] = fields[key].value
|
||||
? JSON.stringify(fields[key].value, null, 2)
|
||||
: emptyDefault;
|
||||
} else {
|
||||
acc[key] = fields[key].value ?? '';
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return (
|
||||
<CardBody>
|
||||
{isLoading && <ContentLoading />}
|
||||
{!isLoading && error && <ContentError error={error} />}
|
||||
{!isLoading && authentication && (
|
||||
<Formik
|
||||
initialValues={initialValues(authentication)}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<BooleanField
|
||||
name="DISABLE_LOCAL_AUTH"
|
||||
needsConfirmationModal
|
||||
modalTitle={t`Confirm Disable Local Authorization`}
|
||||
config={authentication.DISABLE_LOCAL_AUTH}
|
||||
/>
|
||||
<InputField
|
||||
name="SESSION_COOKIE_AGE"
|
||||
config={authentication.SESSION_COOKIE_AGE}
|
||||
type="number"
|
||||
isRequired
|
||||
/>
|
||||
<InputField
|
||||
name="SESSIONS_PER_USER"
|
||||
config={authentication.SESSIONS_PER_USER}
|
||||
type="number"
|
||||
isRequired
|
||||
/>
|
||||
<BooleanField
|
||||
name="AUTH_BASIC_ENABLED"
|
||||
config={authentication.AUTH_BASIC_ENABLED}
|
||||
/>
|
||||
<BooleanField
|
||||
name="ALLOW_OAUTH2_FOR_EXTERNAL_USERS"
|
||||
config={authentication.ALLOW_OAUTH2_FOR_EXTERNAL_USERS}
|
||||
/>
|
||||
<InputField
|
||||
name="LOGIN_REDIRECT_OVERRIDE"
|
||||
config={authentication.LOGIN_REDIRECT_OVERRIDE}
|
||||
type="url"
|
||||
/>
|
||||
<InputField
|
||||
name="ACCESS_TOKEN_EXPIRE_SECONDS"
|
||||
config={authentication.ACCESS_TOKEN_EXPIRE_SECONDS}
|
||||
type="number"
|
||||
/>
|
||||
<InputField
|
||||
name="REFRESH_TOKEN_EXPIRE_SECONDS"
|
||||
config={authentication.REFRESH_TOKEN_EXPIRE_SECONDS}
|
||||
type="number"
|
||||
/>
|
||||
<InputField
|
||||
name="AUTHORIZATION_CODE_EXPIRE_SECONDS"
|
||||
config={authentication.AUTHORIZATION_CODE_EXPIRE_SECONDS}
|
||||
type="number"
|
||||
/>
|
||||
<ObjectField
|
||||
name="SOCIAL_AUTH_ORGANIZATION_MAP"
|
||||
config={authentication.SOCIAL_AUTH_ORGANIZATION_MAP}
|
||||
/>
|
||||
<ObjectField
|
||||
name="SOCIAL_AUTH_TEAM_MAP"
|
||||
config={authentication.SOCIAL_AUTH_TEAM_MAP}
|
||||
/>
|
||||
<ObjectField
|
||||
name="SOCIAL_AUTH_USER_FIELDS"
|
||||
config={authentication.SOCIAL_AUTH_USER_FIELDS}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
onRevert={toggleModal}
|
||||
/>
|
||||
{isModalOpen && (
|
||||
<RevertAllAlert
|
||||
onClose={closeModal}
|
||||
onRevertAll={handleRevertAll}
|
||||
/>
|
||||
)}
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
)}
|
||||
</CardBody>
|
||||
);
|
||||
}
|
||||
|
||||
export default MiscAuthenticationEdit;
|
||||
@@ -6,36 +6,55 @@ import {
|
||||
waitForElement,
|
||||
} from '../../../../../testUtils/enzymeHelpers';
|
||||
import mockAllOptions from '../../shared/data.allSettingOptions.json';
|
||||
import mockAllSettings from '../../shared/data.allSettings.json';
|
||||
import { SettingsProvider } from '../../../../contexts/Settings';
|
||||
import { SettingsAPI } from '../../../../api';
|
||||
import ActivityStreamEdit from './ActivityStreamEdit';
|
||||
import MiscAuthenticationEdit from './MiscAuthenticationEdit';
|
||||
|
||||
jest.mock('../../../../api');
|
||||
|
||||
describe('<ActivityStreamEdit />', () => {
|
||||
const authenticationData = {
|
||||
SESSION_COOKIE_AGE: 1800,
|
||||
SESSIONS_PER_USER: -1,
|
||||
DISABLE_LOCAL_AUTH: false,
|
||||
AUTH_BASIC_ENABLED: true,
|
||||
OAUTH2_PROVIDER: {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS: 31536000000,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS: 2628000,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS: 600,
|
||||
},
|
||||
ALLOW_OAUTH2_FOR_EXTERNAL_USERS: false,
|
||||
LOGIN_REDIRECT_OVERRIDE: '',
|
||||
AUTHENTICATION_BACKENDS: [
|
||||
'awx.sso.backends.TACACSPlusBackend',
|
||||
'awx.main.backends.AWXModelBackend',
|
||||
],
|
||||
SOCIAL_AUTH_ORGANIZATION_MAP: {},
|
||||
SOCIAL_AUTH_TEAM_MAP: {},
|
||||
SOCIAL_AUTH_USER_FIELDS: [],
|
||||
};
|
||||
|
||||
describe('<MiscAuthenticationEdit />', () => {
|
||||
let wrapper;
|
||||
let history;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.unmount();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
history = createMemoryHistory({
|
||||
initialEntries: ['/settings/activity_stream/edit'],
|
||||
});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
ACTIVITY_STREAM_ENABLED: false,
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: true,
|
||||
},
|
||||
});
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: mockAllSettings,
|
||||
});
|
||||
history = createMemoryHistory({
|
||||
initialEntries: ['/settings/miscellaneous_authentication/edit'],
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<ActivityStreamEdit />
|
||||
<MiscAuthenticationEdit />
|
||||
</SettingsProvider>,
|
||||
{
|
||||
context: { router: { history } },
|
||||
@@ -45,54 +64,23 @@ describe('<ActivityStreamEdit />', () => {
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
});
|
||||
|
||||
test('initially renders without crashing', () => {
|
||||
expect(wrapper.find('ActivityStreamEdit').length).toBe(1);
|
||||
test('initially renders without crashing', async () => {
|
||||
expect(wrapper.find('MiscAuthenticationEdit').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should navigate to activity stream detail when cancel is clicked', async () => {
|
||||
test('save button should call updateAll', async () => {
|
||||
expect(wrapper.find('MiscAuthenticationEdit').length).toBe(1);
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual(
|
||||
'/settings/activity_stream/details'
|
||||
);
|
||||
});
|
||||
|
||||
test('should navigate to activity stream detail on successful submission', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual(
|
||||
'/settings/activity_stream/details'
|
||||
);
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(
|
||||
wrapper.find('Switch#ACTIVITY_STREAM_ENABLED').prop('isChecked')
|
||||
).toEqual(false);
|
||||
|
||||
await act(async () => {
|
||||
wrapper.find('Switch#ACTIVITY_STREAM_ENABLED').invoke('onChange')(true);
|
||||
wrapper.find('button[aria-label="Save"]').simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper.find('Switch#ACTIVITY_STREAM_ENABLED').prop('isChecked')
|
||||
).toEqual(true);
|
||||
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
ACTIVITY_STREAM_ENABLED: true,
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: true,
|
||||
});
|
||||
const { AUTHENTICATION_BACKENDS, ...rest } = authenticationData;
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith(rest);
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -107,11 +95,33 @@ describe('<ActivityStreamEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
ACTIVITY_STREAM_ENABLED: true,
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: false,
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('authentication');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should navigate to miscellaneous detail on successful submission', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('Form').invoke('onSubmit')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual(
|
||||
'/settings/miscellaneous_authentication/details'
|
||||
);
|
||||
});
|
||||
|
||||
test('should navigate to miscellaneous detail when cancel is clicked', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual(
|
||||
'/settings/miscellaneous_authentication/details'
|
||||
);
|
||||
});
|
||||
|
||||
test('should display error message on unsuccessful submission', async () => {
|
||||
@@ -138,7 +148,7 @@ describe('<ActivityStreamEdit />', () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<SettingsProvider value={mockAllOptions.actions}>
|
||||
<ActivityStreamEdit />
|
||||
<MiscAuthenticationEdit />
|
||||
</SettingsProvider>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './MiscAuthenticationEdit';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './MiscAuthentication';
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Link, Redirect, Route, Switch } from 'react-router-dom';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
import { PageSection, Card } from '@patternfly/react-core';
|
||||
import ContentError from '../../../components/ContentError';
|
||||
|
||||
@@ -18,84 +18,49 @@ import { sortNestedDetails, pluck } from '../../shared/settingUtils';
|
||||
|
||||
function MiscSystemDetail() {
|
||||
const { me } = useConfig();
|
||||
const { GET: allOptions } = useSettings();
|
||||
const { GET: options } = useSettings();
|
||||
|
||||
const { isLoading, error, request, result: system } = useRequest(
|
||||
useCallback(async () => {
|
||||
const { data } = await SettingsAPI.readCategory('all');
|
||||
let DEFAULT_EXECUTION_ENVIRONMENT = '';
|
||||
const { data } = await SettingsAPI.readCategory('system');
|
||||
if (data.DEFAULT_EXECUTION_ENVIRONMENT) {
|
||||
const {
|
||||
data: { name },
|
||||
} = await ExecutionEnvironmentsAPI.readDetail(
|
||||
data.DEFAULT_EXECUTION_ENVIRONMENT
|
||||
);
|
||||
DEFAULT_EXECUTION_ENVIRONMENT = name;
|
||||
data.DEFAULT_EXECUTION_ENVIRONMENT = name;
|
||||
}
|
||||
const {
|
||||
OAUTH2_PROVIDER: {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
},
|
||||
...pluckedSystemData
|
||||
} = pluck(
|
||||
|
||||
const systemData = pluck(
|
||||
data,
|
||||
'ALLOW_OAUTH2_FOR_EXTERNAL_USERS',
|
||||
'AUTH_BASIC_ENABLED',
|
||||
'ACTIVITY_STREAM_ENABLED',
|
||||
'ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC',
|
||||
'AUTOMATION_ANALYTICS_GATHER_INTERVAL',
|
||||
'AUTOMATION_ANALYTICS_URL',
|
||||
'INSIGHTS_TRACKING_STATE',
|
||||
'LOGIN_REDIRECT_OVERRIDE',
|
||||
'MANAGE_ORGANIZATION_AUTH',
|
||||
'DISABLE_LOCAL_AUTH',
|
||||
'OAUTH2_PROVIDER',
|
||||
'ORG_ADMINS_CAN_SEE_ALL_USERS',
|
||||
'REDHAT_PASSWORD',
|
||||
'REDHAT_USERNAME',
|
||||
'REMOTE_HOST_HEADERS',
|
||||
'SESSIONS_PER_USER',
|
||||
'SESSION_COOKIE_AGE',
|
||||
'REDHAT_PASSWORD',
|
||||
'SUBSCRIPTIONS_USERNAME',
|
||||
'SUBSCRIPTIONS_PASSWORD',
|
||||
'TOWER_URL_BASE'
|
||||
'INSTALL_UUID',
|
||||
'REMOTE_HOST_HEADERS',
|
||||
'TOWER_URL_BASE',
|
||||
'DEFAULT_EXECUTION_ENVIRONMENT',
|
||||
'PROXY_IP_ALLOWED_LIST',
|
||||
'AUTOMATION_ANALYTICS_LAST_GATHER',
|
||||
'AUTOMATION_ANALYTICS_LAST_ENTRIES'
|
||||
);
|
||||
const systemData = {
|
||||
...pluckedSystemData,
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
DEFAULT_EXECUTION_ENVIRONMENT,
|
||||
};
|
||||
const {
|
||||
OAUTH2_PROVIDER: OAUTH2_PROVIDER_OPTIONS,
|
||||
...options
|
||||
} = allOptions;
|
||||
const systemOptions = {
|
||||
...options,
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS: {
|
||||
...OAUTH2_PROVIDER_OPTIONS,
|
||||
type: OAUTH2_PROVIDER_OPTIONS.child.type,
|
||||
label: t`Access Token Expiration`,
|
||||
},
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS: {
|
||||
...OAUTH2_PROVIDER_OPTIONS,
|
||||
type: OAUTH2_PROVIDER_OPTIONS.child.type,
|
||||
label: t`Refresh Token Expiration`,
|
||||
},
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS: {
|
||||
...OAUTH2_PROVIDER_OPTIONS,
|
||||
type: OAUTH2_PROVIDER_OPTIONS.child.type,
|
||||
label: t`Authorization Code Expiration`,
|
||||
},
|
||||
};
|
||||
|
||||
const mergedData = {};
|
||||
Object.keys(systemData).forEach(key => {
|
||||
mergedData[key] = systemOptions[key];
|
||||
mergedData[key] = options[key];
|
||||
mergedData[key].value = systemData[key];
|
||||
});
|
||||
return sortNestedDetails(mergedData);
|
||||
}, [allOptions]),
|
||||
}, [options]),
|
||||
null
|
||||
);
|
||||
|
||||
|
||||
@@ -22,28 +22,26 @@ describe('<MiscSystemDetail />', () => {
|
||||
SettingsAPI.readCategory = jest.fn();
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
ALLOW_OAUTH2_FOR_EXTERNAL_USERS: false,
|
||||
AUTH_BASIC_ENABLED: true,
|
||||
AUTOMATION_ANALYTICS_GATHER_INTERVAL: 14400,
|
||||
ACTIVITY_STREAM_ENABLED: true,
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: false,
|
||||
ORG_ADMINS_CAN_SEE_ALL_USERS: true,
|
||||
MANAGE_ORGANIZATION_AUTH: true,
|
||||
TOWER_URL_BASE: 'https://towerhost',
|
||||
REMOTE_HOST_HEADERS: [],
|
||||
PROXY_IP_ALLOWED_LIST: [],
|
||||
LICENSE: null,
|
||||
REDHAT_USERNAME: 'name1',
|
||||
REDHAT_PASSWORD: '$encrypted$',
|
||||
SUBSCRIPTIONS_USERNAME: 'name2',
|
||||
SUBSCRIPTIONS_PASSWORD: '$encrypted$',
|
||||
AUTOMATION_ANALYTICS_URL: 'https://example.com',
|
||||
INSTALL_UUID: 'db39b9ec-0c6e-4554-987d-42aw9c732ed8',
|
||||
DEFAULT_EXECUTION_ENVIRONMENT: 1,
|
||||
CUSTOM_VENV_PATHS: [],
|
||||
INSIGHTS_TRACKING_STATE: false,
|
||||
LOGIN_REDIRECT_OVERRIDE: 'https://redirect.com',
|
||||
MANAGE_ORGANIZATION_AUTH: true,
|
||||
DISABLE_LOCAL_AUTH: false,
|
||||
OAUTH2_PROVIDER: {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS: 1,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS: 2,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS: 3,
|
||||
},
|
||||
ORG_ADMINS_CAN_SEE_ALL_USERS: true,
|
||||
REDHAT_PASSWORD: '$encrypted$',
|
||||
REDHAT_USERNAME: 'mock name',
|
||||
REMOTE_HOST_HEADERS: [],
|
||||
SESSIONS_PER_USER: -1,
|
||||
SESSION_COOKIE_AGE: 30000000000,
|
||||
TOWER_URL_BASE: 'https://towerhost',
|
||||
DEFAULT_EXECUTION_ENVIRONMENT: 1,
|
||||
AUTOMATION_ANALYTICS_LAST_GATHER: null,
|
||||
AUTOMATION_ANALYTICS_LAST_ENTRIES: 'foo',
|
||||
AUTOMATION_ANALYTICS_GATHER_INTERVAL: 14400,
|
||||
},
|
||||
});
|
||||
ExecutionEnvironmentsAPI.readDetail = jest.fn();
|
||||
@@ -77,14 +75,17 @@ describe('<MiscSystemDetail />', () => {
|
||||
});
|
||||
|
||||
test('should render expected details', () => {
|
||||
assertDetail(wrapper, 'Access Token Expiration', '1 seconds');
|
||||
assertDetail(wrapper, 'All Users Visible to Organization Admins', 'On');
|
||||
assertDetail(
|
||||
wrapper,
|
||||
'Allow External Users to Create OAuth2 Tokens',
|
||||
'Off'
|
||||
'Unique identifier for an installation',
|
||||
'db39b9ec-0c6e-4554-987d-42aw9c732ed8'
|
||||
);
|
||||
assertDetail(wrapper, 'Authorization Code Expiration', '2 seconds');
|
||||
assertDetail(
|
||||
wrapper,
|
||||
'Last gathered entries for expensive collectors for Insights for Ansible Automation Platform.',
|
||||
'foo'
|
||||
);
|
||||
assertDetail(wrapper, 'All Users Visible to Organization Admins', 'On');
|
||||
assertDetail(
|
||||
wrapper,
|
||||
'Insights for Ansible Automation Platform Gather Interval',
|
||||
@@ -96,32 +97,24 @@ describe('<MiscSystemDetail />', () => {
|
||||
'https://example.com'
|
||||
);
|
||||
assertDetail(wrapper, 'Base URL of the service', 'https://towerhost');
|
||||
assertDetail(wrapper, 'Enable HTTP Basic Auth', 'On');
|
||||
assertDetail(
|
||||
wrapper,
|
||||
'Gather data for Insights for Ansible Automation Platform',
|
||||
'Off'
|
||||
);
|
||||
assertDetail(wrapper, 'Idle Time Force Log Out', '30000000000 seconds');
|
||||
assertDetail(
|
||||
wrapper,
|
||||
'Login redirect override URL',
|
||||
'https://redirect.com'
|
||||
);
|
||||
assertDetail(
|
||||
wrapper,
|
||||
'Maximum number of simultaneous logged in sessions',
|
||||
'-1'
|
||||
);
|
||||
assertDetail(
|
||||
wrapper,
|
||||
'Organization Admins Can Manage Users and Teams',
|
||||
'On'
|
||||
);
|
||||
assertDetail(wrapper, 'Enable Activity Stream', 'On');
|
||||
assertDetail(wrapper, 'Enable Activity Stream for Inventory Sync', 'Off');
|
||||
assertDetail(wrapper, 'Red Hat customer password', 'Encrypted');
|
||||
assertDetail(wrapper, 'Red Hat customer username', 'mock name');
|
||||
assertDetail(wrapper, 'Refresh Token Expiration', '3 seconds');
|
||||
assertDetail(wrapper, 'Red Hat customer username', 'name1');
|
||||
assertDetail(wrapper, 'Red Hat or Satellite password', 'Encrypted');
|
||||
assertDetail(wrapper, 'Red Hat or Satellite username', 'name2');
|
||||
assertVariableDetail(wrapper, 'Remote Host Headers', '[]');
|
||||
assertVariableDetail(wrapper, 'Proxy IP Allowed List', '[]');
|
||||
assertDetail(wrapper, 'Global default execution environment', 'Foo');
|
||||
});
|
||||
|
||||
|
||||
@@ -30,76 +30,33 @@ function MiscSystemEdit() {
|
||||
|
||||
const { isLoading, error, request: fetchSystem, result: system } = useRequest(
|
||||
useCallback(async () => {
|
||||
const { data } = await SettingsAPI.readCategory('all');
|
||||
const {
|
||||
OAUTH2_PROVIDER: {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
},
|
||||
...pluckedSystemData
|
||||
} = pluck(
|
||||
const { data } = await SettingsAPI.readCategory('system');
|
||||
const systemData = pluck(
|
||||
data,
|
||||
'ALLOW_OAUTH2_FOR_EXTERNAL_USERS',
|
||||
'AUTH_BASIC_ENABLED',
|
||||
'ACTIVITY_STREAM_ENABLED',
|
||||
'ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC',
|
||||
'AUTOMATION_ANALYTICS_GATHER_INTERVAL',
|
||||
'AUTOMATION_ANALYTICS_URL',
|
||||
'AUTOMATION_ANALYTICS_LAST_ENTRIES',
|
||||
'INSIGHTS_TRACKING_STATE',
|
||||
'LOGIN_REDIRECT_OVERRIDE',
|
||||
'MANAGE_ORGANIZATION_AUTH',
|
||||
'DISABLE_LOCAL_AUTH',
|
||||
'OAUTH2_PROVIDER',
|
||||
'ORG_ADMINS_CAN_SEE_ALL_USERS',
|
||||
'REDHAT_PASSWORD',
|
||||
'REDHAT_USERNAME',
|
||||
'REDHAT_PASSWORD',
|
||||
'SUBSCRIPTIONS_USERNAME',
|
||||
'SUBSCRIPTIONS_PASSWORD',
|
||||
'REMOTE_HOST_HEADERS',
|
||||
'SESSIONS_PER_USER',
|
||||
'SESSION_COOKIE_AGE',
|
||||
'TOWER_URL_BASE',
|
||||
'DEFAULT_EXECUTION_ENVIRONMENT'
|
||||
'DEFAULT_EXECUTION_ENVIRONMENT',
|
||||
'PROXY_IP_ALLOWED_LIST'
|
||||
);
|
||||
|
||||
const systemData = {
|
||||
...pluckedSystemData,
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
};
|
||||
|
||||
const {
|
||||
OAUTH2_PROVIDER: OAUTH2_PROVIDER_OPTIONS,
|
||||
...restOptions
|
||||
} = options;
|
||||
|
||||
const systemOptions = {
|
||||
...restOptions,
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS: {
|
||||
...OAUTH2_PROVIDER_OPTIONS,
|
||||
default: OAUTH2_PROVIDER_OPTIONS.default.ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
type: OAUTH2_PROVIDER_OPTIONS.child.type,
|
||||
label: t`Access Token Expiration`,
|
||||
},
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS: {
|
||||
...OAUTH2_PROVIDER_OPTIONS,
|
||||
default: OAUTH2_PROVIDER_OPTIONS.default.REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
type: OAUTH2_PROVIDER_OPTIONS.child.type,
|
||||
label: t`Refresh Token Expiration`,
|
||||
},
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS: {
|
||||
...OAUTH2_PROVIDER_OPTIONS,
|
||||
default:
|
||||
OAUTH2_PROVIDER_OPTIONS.default.AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
type: OAUTH2_PROVIDER_OPTIONS.child.type,
|
||||
label: t`Authorization Code Expiration`,
|
||||
},
|
||||
};
|
||||
|
||||
const mergedData = {};
|
||||
Object.keys(systemData).forEach(key => {
|
||||
if (!systemOptions[key]) {
|
||||
if (!options[key]) {
|
||||
return;
|
||||
}
|
||||
mergedData[key] = systemOptions[key];
|
||||
mergedData[key] = options[key];
|
||||
mergedData[key].value = systemData[key];
|
||||
});
|
||||
return mergedData;
|
||||
@@ -122,50 +79,29 @@ function MiscSystemEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
const {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
...formData
|
||||
} = form;
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('system');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...formData,
|
||||
REMOTE_HOST_HEADERS: formatJson(formData.REMOTE_HOST_HEADERS),
|
||||
OAUTH2_PROVIDER: {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
},
|
||||
...form,
|
||||
PROXY_IP_ALLOWED_LIST: formatJson(form.PROXY_IP_ALLOWED_LIST),
|
||||
REMOTE_HOST_HEADERS: formatJson(form.REMOTE_HOST_HEADERS),
|
||||
DEFAULT_EXECUTION_ENVIRONMENT:
|
||||
formData.DEFAULT_EXECUTION_ENVIRONMENT?.id || null,
|
||||
form.DEFAULT_EXECUTION_ENVIRONMENT?.id || null,
|
||||
});
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS,
|
||||
...systemData
|
||||
} = system;
|
||||
await revertAll();
|
||||
|
||||
const defaultValues = {};
|
||||
Object.entries(systemData).forEach(([key, value]) => {
|
||||
defaultValues[key] = value.default;
|
||||
});
|
||||
|
||||
await submitForm({
|
||||
...defaultValues,
|
||||
OAUTH2_PROVIDER: {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS: ACCESS_TOKEN_EXPIRE_SECONDS.default,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS: REFRESH_TOKEN_EXPIRE_SECONDS.default,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS:
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS.default,
|
||||
},
|
||||
});
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/miscellaneous_system/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -226,6 +162,14 @@ function MiscSystemEdit() {
|
||||
return (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<BooleanField
|
||||
name="ACTIVITY_STREAM_ENABLED"
|
||||
config={system.ACTIVITY_STREAM_ENABLED}
|
||||
/>
|
||||
<BooleanField
|
||||
name="ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC"
|
||||
config={system.ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC}
|
||||
/>
|
||||
<ExecutionEnvironmentLookup
|
||||
helperTextInvalid={
|
||||
formik.errors.DEFAULT_EXECUTION_ENVIRONMENT
|
||||
@@ -267,52 +211,6 @@ function MiscSystemEdit() {
|
||||
name="MANAGE_ORGANIZATION_AUTH"
|
||||
config={system.MANAGE_ORGANIZATION_AUTH}
|
||||
/>
|
||||
<BooleanField
|
||||
name="DISABLE_LOCAL_AUTH"
|
||||
needsConfirmationModal
|
||||
modalTitle={t`Confirm Disable Local Authorization`}
|
||||
config={system.DISABLE_LOCAL_AUTH}
|
||||
/>
|
||||
<InputField
|
||||
name="SESSION_COOKIE_AGE"
|
||||
config={system.SESSION_COOKIE_AGE}
|
||||
type="number"
|
||||
isRequired
|
||||
/>
|
||||
<InputField
|
||||
name="SESSIONS_PER_USER"
|
||||
config={system.SESSIONS_PER_USER}
|
||||
type="number"
|
||||
isRequired
|
||||
/>
|
||||
<BooleanField
|
||||
name="AUTH_BASIC_ENABLED"
|
||||
config={system.AUTH_BASIC_ENABLED}
|
||||
/>
|
||||
<BooleanField
|
||||
name="ALLOW_OAUTH2_FOR_EXTERNAL_USERS"
|
||||
config={system.ALLOW_OAUTH2_FOR_EXTERNAL_USERS}
|
||||
/>
|
||||
<InputField
|
||||
name="LOGIN_REDIRECT_OVERRIDE"
|
||||
config={system.LOGIN_REDIRECT_OVERRIDE}
|
||||
type="url"
|
||||
/>
|
||||
<InputField
|
||||
name="ACCESS_TOKEN_EXPIRE_SECONDS"
|
||||
config={system.ACCESS_TOKEN_EXPIRE_SECONDS}
|
||||
type="number"
|
||||
/>
|
||||
<InputField
|
||||
name="REFRESH_TOKEN_EXPIRE_SECONDS"
|
||||
config={system.REFRESH_TOKEN_EXPIRE_SECONDS}
|
||||
type="number"
|
||||
/>
|
||||
<InputField
|
||||
name="AUTHORIZATION_CODE_EXPIRE_SECONDS"
|
||||
config={system.AUTHORIZATION_CODE_EXPIRE_SECONDS}
|
||||
type="number"
|
||||
/>
|
||||
<BooleanField
|
||||
name="INSIGHTS_TRACKING_STATE"
|
||||
config={system.INSIGHTS_TRACKING_STATE}
|
||||
@@ -325,6 +223,14 @@ function MiscSystemEdit() {
|
||||
name="REDHAT_PASSWORD"
|
||||
config={system.REDHAT_PASSWORD}
|
||||
/>
|
||||
<InputField
|
||||
name="SUBSCRIPTIONS_USERNAME"
|
||||
config={system.SUBSCRIPTIONS_USERNAME}
|
||||
/>
|
||||
<EncryptedField
|
||||
name="SUBSCRIPTIONS_PASSWORD"
|
||||
config={system.SUBSCRIPTIONS_PASSWORD}
|
||||
/>
|
||||
<InputField
|
||||
name="AUTOMATION_ANALYTICS_URL"
|
||||
config={system.AUTOMATION_ANALYTICS_URL}
|
||||
@@ -336,12 +242,22 @@ function MiscSystemEdit() {
|
||||
type="number"
|
||||
isRequired
|
||||
/>
|
||||
<InputField
|
||||
name="AUTOMATION_ANALYTICS_LAST_ENTRIES"
|
||||
config={system.AUTOMATION_ANALYTICS_LAST_ENTRIES}
|
||||
/>
|
||||
<ObjectField
|
||||
name="REMOTE_HOST_HEADERS"
|
||||
config={system.REMOTE_HOST_HEADERS}
|
||||
isRequired
|
||||
/>
|
||||
<ObjectField
|
||||
name="PROXY_IP_ALLOWED_LIST"
|
||||
config={system.PROXY_IP_ALLOWED_LIST}
|
||||
isRequired
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -23,27 +23,22 @@ const mockExecutionEnvironment = [
|
||||
];
|
||||
|
||||
const systemData = {
|
||||
ALLOW_OAUTH2_FOR_EXTERNAL_USERS: false,
|
||||
AUTH_BASIC_ENABLED: true,
|
||||
ACTIVITY_STREAM_ENABLED: true,
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: false,
|
||||
AUTOMATION_ANALYTICS_GATHER_INTERVAL: 14400,
|
||||
AUTOMATION_ANALYTICS_LAST_ENTRIES: '',
|
||||
AUTOMATION_ANALYTICS_URL: 'https://example.com',
|
||||
DEFAULT_EXECUTION_ENVIRONMENT: 1,
|
||||
INSIGHTS_TRACKING_STATE: false,
|
||||
LOGIN_REDIRECT_OVERRIDE: '',
|
||||
MANAGE_ORGANIZATION_AUTH: true,
|
||||
DISABLE_LOCAL_AUTH: false,
|
||||
OAUTH2_PROVIDER: {
|
||||
ACCESS_TOKEN_EXPIRE_SECONDS: 31536000000,
|
||||
AUTHORIZATION_CODE_EXPIRE_SECONDS: 600,
|
||||
REFRESH_TOKEN_EXPIRE_SECONDS: 2628000,
|
||||
},
|
||||
ORG_ADMINS_CAN_SEE_ALL_USERS: true,
|
||||
REDHAT_PASSWORD: '',
|
||||
REDHAT_USERNAME: '',
|
||||
REDHAT_PASSWORD: '',
|
||||
SUBSCRIPTIONS_USERNAME: '',
|
||||
SUBSCRIPTIONS_PASSWORD: '',
|
||||
REMOTE_HOST_HEADERS: ['REMOTE_ADDR', 'REMOTE_HOST'],
|
||||
SESSIONS_PER_USER: -1,
|
||||
SESSION_COOKIE_AGE: 1800,
|
||||
TOWER_URL_BASE: 'https://localhost:3000',
|
||||
PROXY_IP_ALLOWED_LIST: [],
|
||||
};
|
||||
|
||||
describe('<MiscSystemEdit />', () => {
|
||||
@@ -55,6 +50,7 @@ describe('<MiscSystemEdit />', () => {
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: mockAllSettings,
|
||||
@@ -116,7 +112,7 @@ describe('<MiscSystemEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -131,7 +127,8 @@ describe('<MiscSystemEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('system');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -47,18 +47,23 @@ function RADIUSEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('radius');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm(form);
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(radius).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/radius/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -90,6 +95,7 @@ function RADIUSEdit() {
|
||||
config={radius.RADIUS_SECRET}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('<RADIUSEdit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
@@ -60,7 +61,7 @@ describe('<RADIUSEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -75,12 +76,8 @@ describe('<RADIUSEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
RADIUS_SERVER: '',
|
||||
RADIUS_PORT: 1812,
|
||||
RADIUS_SECRET: '',
|
||||
});
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('radius');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -56,6 +56,13 @@ function SAMLEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('saml');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm({
|
||||
...form,
|
||||
@@ -86,13 +93,11 @@ function SAMLEdit() {
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(saml).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/saml/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -185,6 +190,7 @@ function SAMLEdit() {
|
||||
config={saml.SOCIAL_AUTH_SAML_EXTRA_DATA}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('<SAMLEdit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
@@ -131,7 +132,7 @@ describe('<SAMLEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -146,26 +147,8 @@ describe('<SAMLEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
SAML_AUTO_CREATE_OBJECTS: true,
|
||||
SOCIAL_AUTH_SAML_ENABLED_IDPS: {},
|
||||
SOCIAL_AUTH_SAML_EXTRA_DATA: null,
|
||||
SOCIAL_AUTH_SAML_ORGANIZATION_ATTR: {},
|
||||
SOCIAL_AUTH_SAML_ORGANIZATION_MAP: null,
|
||||
SOCIAL_AUTH_SAML_ORG_INFO: {},
|
||||
SOCIAL_AUTH_SAML_SP_ENTITY_ID: '',
|
||||
SOCIAL_AUTH_SAML_SP_EXTRA: null,
|
||||
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY: '',
|
||||
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT: '',
|
||||
SOCIAL_AUTH_SAML_SUPPORT_CONTACT: {},
|
||||
SOCIAL_AUTH_SAML_TEAM_ATTR: {},
|
||||
SOCIAL_AUTH_SAML_TEAM_MAP: null,
|
||||
SOCIAL_AUTH_SAML_TECHNICAL_CONTACT: {},
|
||||
SOCIAL_AUTH_SAML_SECURITY_CONFIG: {
|
||||
requestedAuthnContext: false,
|
||||
},
|
||||
});
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('saml');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -107,8 +107,8 @@ function SettingList() {
|
||||
path: '/settings/miscellaneous_system',
|
||||
},
|
||||
{
|
||||
title: t`Activity Stream settings`,
|
||||
path: '/settings/activity_stream',
|
||||
title: t`Miscellaneous Authentication settings`,
|
||||
path: '/settings/miscellaneous_authentication',
|
||||
},
|
||||
{
|
||||
title: t`Logging settings`,
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { Link, Route, Switch, Redirect } from 'react-router-dom';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
import { PageSection, Card } from '@patternfly/react-core';
|
||||
import ContentError from '../../components/ContentError';
|
||||
import ContentLoading from '../../components/ContentLoading';
|
||||
import ScreenHeader from '../../components/ScreenHeader';
|
||||
import ActivityStream from './ActivityStream';
|
||||
import AzureAD from './AzureAD';
|
||||
import GitHub from './GitHub';
|
||||
import GoogleOAuth2 from './GoogleOAuth2';
|
||||
@@ -14,6 +12,7 @@ import Jobs from './Jobs';
|
||||
import LDAP from './LDAP';
|
||||
import Subscription from './Subscription';
|
||||
import Logging from './Logging';
|
||||
import MiscAuthentication from './MiscAuthentication';
|
||||
import MiscSystem from './MiscSystem';
|
||||
import RADIUS from './RADIUS';
|
||||
import SAML from './SAML';
|
||||
@@ -94,6 +93,9 @@ function Settings() {
|
||||
'/settings/logging': t`Logging`,
|
||||
'/settings/logging/details': t`Details`,
|
||||
'/settings/logging/edit': t`Edit Details`,
|
||||
'/settings/miscellaneous_authentication': t`Miscellaneous Authentication`,
|
||||
'/settings/miscellaneous_authentication/details': t`Details`,
|
||||
'/settings/miscellaneous_authentication/edit': t`Edit Details`,
|
||||
'/settings/miscellaneous_system': t`Miscellaneous System`,
|
||||
'/settings/miscellaneous_system/details': t`Details`,
|
||||
'/settings/miscellaneous_system/edit': t`Edit Details`,
|
||||
@@ -142,9 +144,6 @@ function Settings() {
|
||||
<SettingsProvider value={result}>
|
||||
<ScreenHeader streamType="setting" breadcrumbConfig={breadcrumbConfig} />
|
||||
<Switch>
|
||||
<Route path="/settings/activity_stream">
|
||||
<ActivityStream />
|
||||
</Route>
|
||||
<Route path="/settings/azure">
|
||||
<AzureAD />
|
||||
</Route>
|
||||
@@ -170,6 +169,9 @@ function Settings() {
|
||||
<Route path="/settings/logging">
|
||||
<Logging />
|
||||
</Route>
|
||||
<Route path="/settings/miscellaneous_authentication">
|
||||
<MiscAuthentication />
|
||||
</Route>
|
||||
<Route path="/settings/miscellaneous_system">
|
||||
<MiscSystem />
|
||||
</Route>
|
||||
|
||||
@@ -51,18 +51,23 @@ function TACACSEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('tacacsplus');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm(form);
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(tacacs).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push('/settings/tacacs/details');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -107,6 +112,7 @@ function TACACSEdit() {
|
||||
config={tacacs.TACACSPLUS_AUTH_PROTOCOL}
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('<TACACSEdit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
@@ -68,7 +69,7 @@ describe('<TACACSEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -83,14 +84,8 @@ describe('<TACACSEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
TACACSPLUS_HOST: '',
|
||||
TACACSPLUS_PORT: 49,
|
||||
TACACSPLUS_SECRET: '',
|
||||
TACACSPLUS_SESSION_TIMEOUT: 5,
|
||||
TACACSPLUS_AUTH_PROTOCOL: 'ascii',
|
||||
});
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('tacacsplus');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
@@ -65,18 +65,26 @@ function UIEdit() {
|
||||
null
|
||||
);
|
||||
|
||||
const { error: revertError, request: revertAll } = useRequest(
|
||||
useCallback(async () => {
|
||||
await SettingsAPI.revertCategory('ui');
|
||||
}, []),
|
||||
null
|
||||
);
|
||||
|
||||
const handleSubmit = async form => {
|
||||
await submitForm(form);
|
||||
};
|
||||
|
||||
const handleRevertAll = async () => {
|
||||
const defaultValues = Object.assign(
|
||||
...Object.entries(uiData).map(([key, value]) => ({
|
||||
[key]: value.default,
|
||||
}))
|
||||
);
|
||||
await submitForm(defaultValues);
|
||||
await revertAll();
|
||||
|
||||
closeModal();
|
||||
|
||||
history.push({
|
||||
pathname: '/settings/ui/details',
|
||||
hardReload: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -115,6 +123,7 @@ function UIEdit() {
|
||||
type="dataURL"
|
||||
/>
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
{revertError && <FormSubmitError error={revertError} />}
|
||||
</FormColumnLayout>
|
||||
<RevertFormActionGroup
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('<UIEdit />', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsAPI.revertCategory.mockResolvedValue({});
|
||||
SettingsAPI.updateAll.mockResolvedValue({});
|
||||
SettingsAPI.readCategory.mockResolvedValue({
|
||||
data: {
|
||||
@@ -62,7 +63,7 @@ describe('<UIEdit />', () => {
|
||||
});
|
||||
|
||||
test('should successfully send default values to api on form revert all', async () => {
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@@ -77,12 +78,8 @@ describe('<UIEdit />', () => {
|
||||
.invoke('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||
CUSTOM_LOGIN_INFO: '',
|
||||
CUSTOM_LOGO: '',
|
||||
PENDO_TRACKING_STATE: 'off',
|
||||
});
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('ui');
|
||||
});
|
||||
|
||||
test('should successfully send request to api on form submission', async () => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user