From 558dfb685e2156b81807246c29b8959bdb82780f Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Fri, 25 Sep 2020 14:00:27 -0400 Subject: [PATCH] Add a new key "unit" to api setting fields * Add detail popover * Fix broken redirects * Add additional id and data-cy attributes to Detail components * Remove galaxy fields from job settings --- awx/api/conf.py | 2 + awx/api/metadata.py | 2 +- awx/conf/registry.py | 2 + awx/main/conf.py | 15 +- awx/sso/conf.py | 1 + .../CodeMirrorInput/VariablesDetail.jsx | 12 +- .../src/components/DetailList/Detail.jsx | 18 +- .../DetailPopover/DetailPopover.jsx | 51 +++++ .../src/components/DetailPopover/index.js | 1 + .../ResourceAccessListItem.test.jsx.snap | 56 +++--- .../ActivityStreamDetail.jsx | 3 + .../AzureAD/AzureADDetail/AzureADDetail.jsx | 3 + .../src/screens/Setting/GitHub/GitHub.jsx | 16 +- .../screens/Setting/GitHub/GitHub.test.jsx | 2 +- .../GitHub/GitHubDetail/GitHubDetail.jsx | 3 + .../GitHub/GitHubDetail/GitHubDetail.test.jsx | 4 +- .../GoogleOAuth2Detail/GoogleOAuth2Detail.jsx | 3 + .../GoogleOAuth2Detail.test.jsx | 2 +- .../Setting/Jobs/JobsDetail/JobsDetail.jsx | 8 +- .../Jobs/JobsDetail/JobsDetail.test.jsx | 33 +--- awx/ui_next/src/screens/Setting/LDAP/LDAP.jsx | 16 +- .../src/screens/Setting/LDAP/LDAP.test.jsx | 2 +- .../Setting/LDAP/LDAPDetail/LDAPDetail.jsx | 7 +- .../LDAP/LDAPDetail/LDAPDetail.test.jsx | 2 +- .../Logging/LoggingDetail/LoggingDetail.jsx | 3 + .../LoggingDetail/LoggingDetail.test.jsx | 2 +- .../MiscSystemDetail/MiscSystemDetail.jsx | 3 + .../MiscSystemDetail.test.jsx | 16 +- .../RADIUS/RADIUSDetail/RADIUSDetail.jsx | 3 + .../Setting/SAML/SAMLDetail/SAMLDetail.jsx | 3 + .../SAML/SAMLDetail/SAMLDetail.test.jsx | 6 +- .../TACACS/TACACSDetail/TACACSDetail.jsx | 3 + .../TACACS/TACACSDetail/TACACSDetail.test.jsx | 2 +- .../screens/Setting/UI/UIDetail/UIDetail.jsx | 3 + .../screens/Setting/shared/SettingDetail.jsx | 187 ++++++++++-------- .../shared/data.allSettingOptions.json | 182 ++++++----------- .../Setting/shared/data.jobSettings.json | 7 +- .../screens/Setting/shared/settingUtils.js | 9 +- 38 files changed, 403 insertions(+), 290 deletions(-) create mode 100644 awx/ui_next/src/components/DetailPopover/DetailPopover.jsx create mode 100644 awx/ui_next/src/components/DetailPopover/index.js diff --git a/awx/api/conf.py b/awx/api/conf.py index 493eed6981..f7da952004 100644 --- a/awx/api/conf.py +++ b/awx/api/conf.py @@ -16,6 +16,7 @@ register( help_text=_('Number of seconds that a user is inactive before they will need to login again.'), category=_('Authentication'), category_slug='authentication', + unit=_('seconds'), ) register( 'SESSIONS_PER_USER', @@ -49,6 +50,7 @@ register( 'in the number of seconds.'), category=_('Authentication'), category_slug='authentication', + unit=_('seconds'), ) register( 'ALLOW_OAUTH2_FOR_EXTERNAL_USERS', diff --git a/awx/api/metadata.py b/awx/api/metadata.py index 847e353890..0b60f9a1ef 100644 --- a/awx/api/metadata.py +++ b/awx/api/metadata.py @@ -39,7 +39,7 @@ class Metadata(metadata.SimpleMetadata): 'min_length', 'max_length', 'min_value', 'max_value', 'category', 'category_slug', - 'defined_in_file' + 'defined_in_file', 'unit', ] for attr in text_attrs: diff --git a/awx/conf/registry.py b/awx/conf/registry.py index 63336fc55e..e8e52fe695 100644 --- a/awx/conf/registry.py +++ b/awx/conf/registry.py @@ -129,12 +129,14 @@ class SettingsRegistry(object): placeholder = field_kwargs.pop('placeholder', empty) encrypted = bool(field_kwargs.pop('encrypted', False)) defined_in_file = bool(field_kwargs.pop('defined_in_file', False)) + unit = field_kwargs.pop('unit', None) if getattr(field_kwargs.get('child', None), 'source', None) is not None: field_kwargs['child'].source = None field_instance = field_class(**field_kwargs) field_instance.category_slug = category_slug field_instance.category = category field_instance.depends_on = depends_on + field_instance.unit = unit if placeholder is not empty: field_instance.placeholder = placeholder field_instance.defined_in_file = defined_in_file diff --git a/awx/main/conf.py b/awx/main/conf.py index 7e6d3485fa..3b41c3a19b 100644 --- a/awx/main/conf.py +++ b/awx/main/conf.py @@ -148,7 +148,7 @@ register( default='https://example.com', schemes=('http', 'https'), allow_plain_hostname=True, # Allow hostname only without TLD. - label=_('Automation Analytics upload URL.'), + label=_('Automation Analytics upload URL'), help_text=_('This setting is used to to configure data collection for the Automation Analytics dashboard'), category=_('System'), category_slug='system', @@ -253,6 +253,7 @@ register( help_text=_('The number of seconds to sleep between status checks for jobs running on isolated instances.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -264,6 +265,7 @@ register( 'This includes the time needed to copy source control files (playbooks) to the isolated instance.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -276,6 +278,7 @@ register( 'Value should be substantially greater than expected network latency.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -497,6 +500,7 @@ register( 'timeout should be imposed. A timeout set on an individual job template will override this.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -509,6 +513,7 @@ register( 'timeout should be imposed. A timeout set on an individual inventory source will override this.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -521,6 +526,7 @@ register( 'timeout should be imposed. A timeout set on an individual project will override this.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -535,6 +541,7 @@ register( 'Use a value of 0 to indicate that no timeout should be imposed.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -542,7 +549,7 @@ register( field_class=fields.IntegerField, allow_null=False, default=200, - label=_('Maximum number of forks per job.'), + label=_('Maximum number of forks per job'), help_text=_('Saving a Job Template with more than this number of forks will result in an error. ' 'When set to 0, no limit is applied.'), category=_('Jobs'), @@ -672,6 +679,7 @@ register( 'aggregator protocols.'), category=_('Logging'), category_slug='logging', + unit=_('seconds'), ) register( 'LOG_AGGREGATOR_VERIFY_CERT', @@ -752,7 +760,8 @@ register( default=14400, # every 4 hours min_value=1800, # every 30 minutes category=_('System'), - category_slug='system' + category_slug='system', + unit=_('seconds'), ) diff --git a/awx/sso/conf.py b/awx/sso/conf.py index 7be4f23de1..5f595517cc 100644 --- a/awx/sso/conf.py +++ b/awx/sso/conf.py @@ -515,6 +515,7 @@ register( help_text=_('TACACS+ session timeout value in seconds, 0 disables timeout.'), category=_('TACACS+'), category_slug='tacacsplus', + unit=_('seconds'), ) register( diff --git a/awx/ui_next/src/components/CodeMirrorInput/VariablesDetail.jsx b/awx/ui_next/src/components/CodeMirrorInput/VariablesDetail.jsx index a39b217575..3e83ed8f5e 100644 --- a/awx/ui_next/src/components/CodeMirrorInput/VariablesDetail.jsx +++ b/awx/ui_next/src/components/CodeMirrorInput/VariablesDetail.jsx @@ -4,6 +4,7 @@ import { node, number, oneOfType, shape, string, arrayOf } from 'prop-types'; import { Split, SplitItem, TextListItemVariants } from '@patternfly/react-core'; import { DetailName, DetailValue } from '../DetailList'; import MultiButtonToggle from '../MultiButtonToggle'; +import DetailPopover from '../DetailPopover'; import { yamlToJson, jsonToYaml, @@ -27,7 +28,7 @@ function getValueAsMode(value, mode) { return mode === YAML_MODE ? jsonToYaml(value) : yamlToJson(value); } -function VariablesDetail({ value, label, rows, fullHeight }) { +function VariablesDetail({ dataCy, helpText, value, label, rows, fullHeight }) { const [mode, setMode] = useState( isJsonObject(value) || isJsonString(value) ? JSON_MODE : YAML_MODE ); @@ -46,9 +47,14 @@ function VariablesDetail({ value, label, rows, fullHeight }) { /* eslint-disable-next-line react-hooks/exhaustive-deps */ }, [value]); + const labelCy = dataCy ? `${dataCy}-label` : null; + const valueCy = dataCy ? `${dataCy}-value` : null; + return ( <> {label} + {helpText && ( + + )} @@ -84,6 +93,7 @@ function VariablesDetail({ value, label, rows, fullHeight }) { ( @@ -15,7 +16,7 @@ const DetailName = styled(({ fullWidth, ...props }) => ( `; const DetailValue = styled( - ({ fullWidth, isEncrypted, isUnconfigured, ...props }) => ( + ({ fullWidth, isEncrypted, isNotConfigured, ...props }) => ( ) )` @@ -26,7 +27,7 @@ const DetailValue = styled( grid-column: 2 / -1; `} ${props => - (props.isEncrypted || props.isUnconfigured) && + (props.isEncrypted || props.isNotConfigured) && ` color: var(--pf-global--Color--400); `} @@ -39,8 +40,9 @@ const Detail = ({ className, dataCy, alwaysVisible, + helpText, isEncrypted, - isUnconfigured, + isNotConfigured, }) => { if (!value && typeof value !== 'number' && !alwaysVisible) { return null; @@ -56,8 +58,12 @@ const Detail = ({ component={TextListItemVariants.dt} fullWidth={fullWidth} data-cy={labelCy} + id={dataCy} > {label} + {helpText && ( + + )} {value} @@ -77,11 +83,13 @@ Detail.propTypes = { value: node, fullWidth: bool, alwaysVisible: bool, + helpText: string, }; Detail.defaultProps = { value: null, fullWidth: false, alwaysVisible: false, + helpText: null, }; export default Detail; diff --git a/awx/ui_next/src/components/DetailPopover/DetailPopover.jsx b/awx/ui_next/src/components/DetailPopover/DetailPopover.jsx new file mode 100644 index 0000000000..fa4aec9ee8 --- /dev/null +++ b/awx/ui_next/src/components/DetailPopover/DetailPopover.jsx @@ -0,0 +1,51 @@ +import React, { useState } from 'react'; +import { node, string } from 'prop-types'; +import { Button as _Button, Popover } from '@patternfly/react-core'; +import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons'; +import styled from 'styled-components'; + +const Button = styled(_Button)` + --pf-c-button--PaddingTop: 0; + --pf-c-button--PaddingBottom: 0; +`; + +function DetailPopover({ header, content, id }) { + const [showPopover, setShowPopover] = useState(false); + if (!content) { + return null; + } + return ( + setShowPopover(false)} + > + + + ); +} + +DetailPopover.propTypes = { + content: node, + header: node, + id: string, +}; +DetailPopover.defaultProps = { + content: null, + header: null, + id: 'detail-popover', +}; + +export default DetailPopover; diff --git a/awx/ui_next/src/components/DetailPopover/index.js b/awx/ui_next/src/components/DetailPopover/index.js new file mode 100644 index 0000000000..02b42518c5 --- /dev/null +++ b/awx/ui_next/src/components/DetailPopover/index.js @@ -0,0 +1 @@ +export { default } from './DetailPopover'; diff --git a/awx/ui_next/src/components/ResourceAccessList/__snapshots__/ResourceAccessListItem.test.jsx.snap b/awx/ui_next/src/components/ResourceAccessList/__snapshots__/ResourceAccessListItem.test.jsx.snap index d9bb8bed4e..818f2cf945 100644 --- a/awx/ui_next/src/components/ResourceAccessList/__snapshots__/ResourceAccessListItem.test.jsx.snap +++ b/awx/ui_next/src/components/ResourceAccessList/__snapshots__/ResourceAccessListItem.test.jsx.snap @@ -74,6 +74,7 @@ exports[` initially renders succesfully 1`] = ` @@ -86,6 +87,7 @@ exports[` initially renders succesfully 1`] = ` initially renders succesfully 1`] = ` @@ -150,6 +153,7 @@ exports[` initially renders succesfully 1`] = ` initially renders succesfully 1`] = ` @@ -237,6 +242,7 @@ exports[` initially renders succesfully 1`] = ` initially renders succesfully 1`] = ` @@ -463,9 +470,9 @@ exports[` initially renders succesfully 1`] = ` "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "componentId": "sc-htpNat", + "componentId": "sc-bxivhb", "isStatic": false, - "lastClassName": "iYJcPm", + "lastClassName": "gQwVdc", "rules": Array [ " font-weight: var(--pf-global--FontWeight--bold); @@ -478,7 +485,7 @@ exports[` initially renders succesfully 1`] = ` "displayName": "Styled(Component)", "foldedComponentIds": Array [], "render": [Function], - "styledComponentId": "sc-htpNat", + "styledComponentId": "sc-bxivhb", "target": [Function], "toString": [Function], "warnTooManyClasses": [Function], @@ -489,18 +496,18 @@ exports[` initially renders succesfully 1`] = ` fullWidth={false} >
@@ -523,9 +530,9 @@ exports[` initially renders succesfully 1`] = ` "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "componentId": "sc-bxivhb", + "componentId": "sc-ifAKCX", "isStatic": false, - "lastClassName": "gxmPlV", + "lastClassName": "boHWLt", "rules": Array [ " word-break: break-all; @@ -541,7 +548,7 @@ exports[` initially renders succesfully 1`] = ` "displayName": "Styled(Component)", "foldedComponentIds": Array [], "render": [Function], - "styledComponentId": "sc-bxivhb", + "styledComponentId": "sc-ifAKCX", "target": [Function], "toString": [Function], "warnTooManyClasses": [Function], @@ -552,18 +559,18 @@ exports[` initially renders succesfully 1`] = ` fullWidth={false} >
@@ -670,6 +677,7 @@ exports[` initially renders succesfully 1`] = ` initially renders succesfully 1`] = ` "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "componentId": "sc-htpNat", + "componentId": "sc-bxivhb", "isStatic": false, - "lastClassName": "iYJcPm", + "lastClassName": "gQwVdc", "rules": Array [ " font-weight: var(--pf-global--FontWeight--bold); @@ -718,7 +726,7 @@ exports[` initially renders succesfully 1`] = ` "displayName": "Styled(Component)", "foldedComponentIds": Array [], "render": [Function], - "styledComponentId": "sc-htpNat", + "styledComponentId": "sc-bxivhb", "target": [Function], "toString": [Function], "warnTooManyClasses": [Function], @@ -729,18 +737,18 @@ exports[` initially renders succesfully 1`] = ` fullWidth={false} >
@@ -763,9 +771,9 @@ exports[` initially renders succesfully 1`] = ` "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "componentId": "sc-bxivhb", + "componentId": "sc-ifAKCX", "isStatic": false, - "lastClassName": "gxmPlV", + "lastClassName": "boHWLt", "rules": Array [ " word-break: break-all; @@ -781,7 +789,7 @@ exports[` initially renders succesfully 1`] = ` "displayName": "Styled(Component)", "foldedComponentIds": Array [], "render": [Function], - "styledComponentId": "sc-bxivhb", + "styledComponentId": "sc-ifAKCX", "target": [Function], "toString": [Function], "warnTooManyClasses": [Function], @@ -792,18 +800,18 @@ exports[` initially renders succesfully 1`] = ` fullWidth={false} >
diff --git a/awx/ui_next/src/screens/Setting/ActivityStream/ActivityStreamDetail/ActivityStreamDetail.jsx b/awx/ui_next/src/screens/Setting/ActivityStream/ActivityStreamDetail/ActivityStreamDetail.jsx index 9b51b1caa6..1412421ed5 100644 --- a/awx/ui_next/src/screens/Setting/ActivityStream/ActivityStreamDetail/ActivityStreamDetail.jsx +++ b/awx/ui_next/src/screens/Setting/ActivityStream/ActivityStreamDetail/ActivityStreamDetail.jsx @@ -70,8 +70,11 @@ function ActivityStreamDetail({ i18n }) { return ( ); diff --git a/awx/ui_next/src/screens/Setting/AzureAD/AzureADDetail/AzureADDetail.jsx b/awx/ui_next/src/screens/Setting/AzureAD/AzureADDetail/AzureADDetail.jsx index f73e4d6c44..cb97d182a1 100644 --- a/awx/ui_next/src/screens/Setting/AzureAD/AzureADDetail/AzureADDetail.jsx +++ b/awx/ui_next/src/screens/Setting/AzureAD/AzureADDetail/AzureADDetail.jsx @@ -62,8 +62,11 @@ function AzureADDetail({ i18n }) { return ( ); diff --git a/awx/ui_next/src/screens/Setting/GitHub/GitHub.jsx b/awx/ui_next/src/screens/Setting/GitHub/GitHub.jsx index 01ba2f6ada..c134adca9c 100644 --- a/awx/ui_next/src/screens/Setting/GitHub/GitHub.jsx +++ b/awx/ui_next/src/screens/Setting/GitHub/GitHub.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Link, Redirect, Route, Switch } from 'react-router-dom'; +import { Link, Redirect, Route, Switch, useRouteMatch } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { PageSection, Card } from '@patternfly/react-core'; @@ -9,11 +9,23 @@ import GitHubEdit from './GitHubEdit'; function GitHub({ i18n }) { const baseURL = '/settings/github'; + const baseRoute = useRouteMatch({ path: '/settings/github', exact: true }); + const categoryRoute = useRouteMatch({ + path: '/settings/github/:category', + exact: true, + }); + return ( - + {baseRoute && } + {categoryRoute && ( + + )} diff --git a/awx/ui_next/src/screens/Setting/GitHub/GitHub.test.jsx b/awx/ui_next/src/screens/Setting/GitHub/GitHub.test.jsx index e0c2d57e7e..37a1c62a67 100644 --- a/awx/ui_next/src/screens/Setting/GitHub/GitHub.test.jsx +++ b/awx/ui_next/src/screens/Setting/GitHub/GitHub.test.jsx @@ -49,7 +49,7 @@ describe('', () => { test('should show content error when user navigates to erroneous route', async () => { const history = createMemoryHistory({ - initialEntries: ['/settings/github/foo'], + initialEntries: ['/settings/github/foo/bar'], }); await act(async () => { wrapper = mountWithContexts(, { diff --git a/awx/ui_next/src/screens/Setting/GitHub/GitHubDetail/GitHubDetail.jsx b/awx/ui_next/src/screens/Setting/GitHub/GitHubDetail/GitHubDetail.jsx index 691d4db325..0b70e09d70 100644 --- a/awx/ui_next/src/screens/Setting/GitHub/GitHubDetail/GitHubDetail.jsx +++ b/awx/ui_next/src/screens/Setting/GitHub/GitHubDetail/GitHubDetail.jsx @@ -98,8 +98,11 @@ function GitHubDetail({ i18n }) { return ( ); diff --git a/awx/ui_next/src/screens/Setting/GitHub/GitHubDetail/GitHubDetail.test.jsx b/awx/ui_next/src/screens/Setting/GitHub/GitHubDetail/GitHubDetail.test.jsx index 517621aa89..11db4b57f0 100644 --- a/awx/ui_next/src/screens/Setting/GitHub/GitHubDetail/GitHubDetail.test.jsx +++ b/awx/ui_next/src/screens/Setting/GitHub/GitHubDetail/GitHubDetail.test.jsx @@ -175,9 +175,9 @@ describe('', () => { 'GitHub Organization OAuth2 Callback URL', 'https://towerhost/sso/complete/github-org/' ); - assertDetail(wrapper, 'GitHub Organization OAuth2 Key', 'Unconfigured'); + assertDetail(wrapper, 'GitHub Organization OAuth2 Key', 'Not configured'); assertDetail(wrapper, 'GitHub Organization OAuth2 Secret', 'Encrypted'); - assertDetail(wrapper, 'GitHub Organization Name', 'Unconfigured'); + assertDetail(wrapper, 'GitHub Organization Name', 'Not configured'); assertVariableDetail( wrapper, 'GitHub Organization OAuth2 Organization Map', diff --git a/awx/ui_next/src/screens/Setting/GoogleOAuth2/GoogleOAuth2Detail/GoogleOAuth2Detail.jsx b/awx/ui_next/src/screens/Setting/GoogleOAuth2/GoogleOAuth2Detail/GoogleOAuth2Detail.jsx index 21dae3a215..bbf512249c 100644 --- a/awx/ui_next/src/screens/Setting/GoogleOAuth2/GoogleOAuth2Detail/GoogleOAuth2Detail.jsx +++ b/awx/ui_next/src/screens/Setting/GoogleOAuth2/GoogleOAuth2Detail/GoogleOAuth2Detail.jsx @@ -62,8 +62,11 @@ function GoogleOAuth2Detail({ i18n }) { return ( ); diff --git a/awx/ui_next/src/screens/Setting/GoogleOAuth2/GoogleOAuth2Detail/GoogleOAuth2Detail.test.jsx b/awx/ui_next/src/screens/Setting/GoogleOAuth2/GoogleOAuth2Detail/GoogleOAuth2Detail.test.jsx index e4545dd943..ab4ce44ce3 100644 --- a/awx/ui_next/src/screens/Setting/GoogleOAuth2/GoogleOAuth2Detail/GoogleOAuth2Detail.test.jsx +++ b/awx/ui_next/src/screens/Setting/GoogleOAuth2/GoogleOAuth2Detail/GoogleOAuth2Detail.test.jsx @@ -72,7 +72,7 @@ describe('', () => { assertDetail(wrapper, 'Google OAuth2 Secret', 'Encrypted'); assertVariableDetail( wrapper, - 'Google OAuth2 Whitelisted Domains', + 'Google OAuth2 Allowed Domains', '[\n "example.com",\n "example_2.com"\n]' ); assertVariableDetail(wrapper, 'Google OAuth2 Extra Arguments', '{}'); diff --git a/awx/ui_next/src/screens/Setting/Jobs/JobsDetail/JobsDetail.jsx b/awx/ui_next/src/screens/Setting/Jobs/JobsDetail/JobsDetail.jsx index 11f32069ab..5b099a802a 100644 --- a/awx/ui_next/src/screens/Setting/Jobs/JobsDetail/JobsDetail.jsx +++ b/awx/ui_next/src/screens/Setting/Jobs/JobsDetail/JobsDetail.jsx @@ -29,7 +29,6 @@ function JobsDetail({ i18n }) { AWX_ISOLATED_KEY_GENERATION, AWX_ISOLATED_PRIVATE_KEY, AWX_ISOLATED_PUBLIC_KEY, - GALAXY_IGNORE_CERTS, STDOUT_MAX_BYTES_DISPLAY, EVENT_STDOUT_MAX_BYTES_DISPLAY, ...jobsData @@ -76,12 +75,15 @@ function JobsDetail({ i18n }) { {!isLoading && error && } {!isLoading && jobs && ( - {Array.from(jobs).map(([, detail]) => { + {jobs.map(([key, detail]) => { return ( ); diff --git a/awx/ui_next/src/screens/Setting/Jobs/JobsDetail/JobsDetail.test.jsx b/awx/ui_next/src/screens/Setting/Jobs/JobsDetail/JobsDetail.test.jsx index bcb6908249..02ec0b8118 100644 --- a/awx/ui_next/src/screens/Setting/Jobs/JobsDetail/JobsDetail.test.jsx +++ b/awx/ui_next/src/screens/Setting/Jobs/JobsDetail/JobsDetail.test.jsx @@ -52,9 +52,9 @@ describe('', () => { test('should render expected details', () => { assertDetail(wrapper, 'Enable job isolation', 'On'); assertDetail(wrapper, 'Job execution path', '/tmp'); - assertDetail(wrapper, 'Isolated status check interval', '1'); - assertDetail(wrapper, 'Isolated launch timeout', '600'); - assertDetail(wrapper, 'Isolated connection timeout', '10'); + assertDetail(wrapper, 'Isolated status check interval', '1 seconds'); + assertDetail(wrapper, 'Isolated launch timeout', '600 seconds'); + assertDetail(wrapper, 'Isolated connection timeout', '10 seconds'); assertDetail(wrapper, 'Isolated host key checking', 'Off'); assertDetail( wrapper, @@ -67,28 +67,15 @@ describe('', () => { assertDetail(wrapper, 'Follow symlinks', 'Off'); assertDetail( wrapper, - 'Primary Galaxy Server URL', - 'https://galaxy.server.com' + 'Ignore Ansible Galaxy SSL Certificate Verification', + 'Off' ); - assertDetail(wrapper, 'Primary Galaxy Server Username', 'Unconfigured'); - assertDetail(wrapper, 'Primary Galaxy Server Password', 'Unconfigured'); - assertDetail(wrapper, 'Primary Galaxy Server Token', 'Encrypted'); - assertDetail( - wrapper, - 'Primary Galaxy Authentication URL', - 'https://galaxy.auth.com' - ); - assertDetail(wrapper, 'Allow Access to Public Galaxy', 'On'); assertDetail(wrapper, 'Maximum Scheduled Jobs', '10'); - assertDetail(wrapper, 'Default Job Timeout', 'Unconfigured'); - assertDetail(wrapper, 'Default Inventory Update Timeout', 'Unconfigured'); - assertDetail(wrapper, 'Default Project Update Timeout', 'Unconfigured'); - assertDetail( - wrapper, - 'Per-Host Ansible Fact Cache Timeout', - 'Unconfigured' - ); - assertDetail(wrapper, 'Maximum number of forks per job.', '200'); + assertDetail(wrapper, 'Default Job Timeout', '0 seconds'); + assertDetail(wrapper, 'Default Inventory Update Timeout', '0 seconds'); + assertDetail(wrapper, 'Default Project Update Timeout', '0 seconds'); + assertDetail(wrapper, 'Per-Host Ansible Fact Cache Timeout', '0 seconds'); + assertDetail(wrapper, 'Maximum number of forks per job', '200'); assertVariableDetail( wrapper, 'Ansible Modules Allowed for Ad Hoc Jobs', diff --git a/awx/ui_next/src/screens/Setting/LDAP/LDAP.jsx b/awx/ui_next/src/screens/Setting/LDAP/LDAP.jsx index e675132912..722127079c 100644 --- a/awx/ui_next/src/screens/Setting/LDAP/LDAP.jsx +++ b/awx/ui_next/src/screens/Setting/LDAP/LDAP.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Link, Redirect, Route, Switch } from 'react-router-dom'; +import { Link, Redirect, Route, Switch, useRouteMatch } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { PageSection, Card } from '@patternfly/react-core'; @@ -9,11 +9,23 @@ import LDAPEdit from './LDAPEdit'; function LDAP({ i18n }) { const baseURL = '/settings/ldap'; + const baseRoute = useRouteMatch({ path: '/settings/ldap', exact: true }); + const categoryRoute = useRouteMatch({ + path: '/settings/ldap/:category', + exact: true, + }); + return ( - + {baseRoute && } + {categoryRoute && ( + + )} diff --git a/awx/ui_next/src/screens/Setting/LDAP/LDAP.test.jsx b/awx/ui_next/src/screens/Setting/LDAP/LDAP.test.jsx index 2b3d185257..bcc6c60be0 100644 --- a/awx/ui_next/src/screens/Setting/LDAP/LDAP.test.jsx +++ b/awx/ui_next/src/screens/Setting/LDAP/LDAP.test.jsx @@ -49,7 +49,7 @@ describe('', () => { test('should show content error when user navigates to erroneous route', async () => { const history = createMemoryHistory({ - initialEntries: ['/settings/ldap/foo'], + initialEntries: ['/settings/ldap/foo/bar'], }); await act(async () => { wrapper = mountWithContexts(, { diff --git a/awx/ui_next/src/screens/Setting/LDAP/LDAPDetail/LDAPDetail.jsx b/awx/ui_next/src/screens/Setting/LDAP/LDAPDetail/LDAPDetail.jsx index bb5196c93f..dd881572e2 100644 --- a/awx/ui_next/src/screens/Setting/LDAP/LDAPDetail/LDAPDetail.jsx +++ b/awx/ui_next/src/screens/Setting/LDAP/LDAPDetail/LDAPDetail.jsx @@ -138,12 +138,15 @@ function LDAPDetail({ i18n }) { {!isLoading && error && } {!isLoading && !Object.values(LDAPDetails)?.includes(null) && ( - {Array.from(LDAPDetails[category]).map(([, detail]) => { + {LDAPDetails[category].map(([key, detail]) => { return ( ); diff --git a/awx/ui_next/src/screens/Setting/LDAP/LDAPDetail/LDAPDetail.test.jsx b/awx/ui_next/src/screens/Setting/LDAP/LDAPDetail/LDAPDetail.test.jsx index 7720dc9b3b..e688882a20 100644 --- a/awx/ui_next/src/screens/Setting/LDAP/LDAPDetail/LDAPDetail.test.jsx +++ b/awx/ui_next/src/screens/Setting/LDAP/LDAPDetail/LDAPDetail.test.jsx @@ -82,7 +82,7 @@ describe('', () => { 'LDAP Require Group', 'CN=Tower Users,OU=Users,DC=example,DC=com' ); - assertDetail(wrapper, 'LDAP Deny Group', 'Unconfigured'); + assertDetail(wrapper, 'LDAP Deny Group', 'Not configured'); assertVariableDetail(wrapper, 'LDAP User Search', '[]'); assertVariableDetail(wrapper, 'LDAP User Attribute Map', '{}'); assertVariableDetail(wrapper, 'LDAP Group Search', '[]'); diff --git a/awx/ui_next/src/screens/Setting/Logging/LoggingDetail/LoggingDetail.jsx b/awx/ui_next/src/screens/Setting/Logging/LoggingDetail/LoggingDetail.jsx index 8e9fabc4bd..11fe55ddfc 100644 --- a/awx/ui_next/src/screens/Setting/Logging/LoggingDetail/LoggingDetail.jsx +++ b/awx/ui_next/src/screens/Setting/Logging/LoggingDetail/LoggingDetail.jsx @@ -84,8 +84,11 @@ function LoggingDetail({ i18n }) { {logging.map(([key, detail]) => ( ))} diff --git a/awx/ui_next/src/screens/Setting/Logging/LoggingDetail/LoggingDetail.test.jsx b/awx/ui_next/src/screens/Setting/Logging/LoggingDetail/LoggingDetail.test.jsx index aa292af616..dc669252d7 100644 --- a/awx/ui_next/src/screens/Setting/Logging/LoggingDetail/LoggingDetail.test.jsx +++ b/awx/ui_next/src/screens/Setting/Logging/LoggingDetail/LoggingDetail.test.jsx @@ -58,7 +58,7 @@ describe('', () => { assertDetail(wrapper, 'Logging Aggregator Password/Token', 'Encrypted'); assertDetail(wrapper, 'Log System Tracking Facts Individually', 'Off'); assertDetail(wrapper, 'Logging Aggregator Protocol', 'https'); - assertDetail(wrapper, 'TCP Connection Timeout', '5'); + assertDetail(wrapper, 'TCP Connection Timeout', '5 seconds'); assertDetail(wrapper, 'Logging Aggregator Level Threshold', 'INFO'); assertDetail( wrapper, diff --git a/awx/ui_next/src/screens/Setting/MiscSystem/MiscSystemDetail/MiscSystemDetail.jsx b/awx/ui_next/src/screens/Setting/MiscSystem/MiscSystemDetail/MiscSystemDetail.jsx index 8328ba0755..a8fcdf411d 100644 --- a/awx/ui_next/src/screens/Setting/MiscSystem/MiscSystemDetail/MiscSystemDetail.jsx +++ b/awx/ui_next/src/screens/Setting/MiscSystem/MiscSystemDetail/MiscSystemDetail.jsx @@ -125,8 +125,11 @@ function MiscSystemDetail({ i18n }) { {system.map(([key, detail]) => ( ))} diff --git a/awx/ui_next/src/screens/Setting/MiscSystem/MiscSystemDetail/MiscSystemDetail.test.jsx b/awx/ui_next/src/screens/Setting/MiscSystem/MiscSystemDetail/MiscSystemDetail.test.jsx index 20c1166306..8437930be2 100644 --- a/awx/ui_next/src/screens/Setting/MiscSystem/MiscSystemDetail/MiscSystemDetail.test.jsx +++ b/awx/ui_next/src/screens/Setting/MiscSystem/MiscSystemDetail/MiscSystemDetail.test.jsx @@ -69,24 +69,28 @@ describe('', () => { }); test('should render expected details', () => { - assertDetail(wrapper, 'Access Token Expiration', '1'); + 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' ); - assertDetail(wrapper, 'Authorization Code Expiration', '2'); - assertDetail(wrapper, 'Automation Analytics Gather Interval', '14400'); + assertDetail(wrapper, 'Authorization Code Expiration', '2 seconds'); assertDetail( wrapper, - 'Automation Analytics upload URL.', + 'Automation Analytics Gather Interval', + '14400 seconds' + ); + assertDetail( + wrapper, + 'Automation Analytics upload URL', 'https://example.com' ); assertDetail(wrapper, 'Base URL of the Tower host', 'https://towerhost'); assertDetail(wrapper, 'Enable HTTP Basic Auth', 'On'); assertDetail(wrapper, 'Gather data for Automation Analytics', 'Off'); - assertDetail(wrapper, 'Idle Time Force Log Out', '30000000000'); + assertDetail(wrapper, 'Idle Time Force Log Out', '30000000000 seconds'); assertDetail( wrapper, 'Login redirect override URL', @@ -104,7 +108,7 @@ describe('', () => { ); assertDetail(wrapper, 'Red Hat customer password', 'Encrypted'); assertDetail(wrapper, 'Red Hat customer username', 'mock name'); - assertDetail(wrapper, 'Refresh Token Expiration', '3'); + assertDetail(wrapper, 'Refresh Token Expiration', '3 seconds'); assertVariableDetail(wrapper, 'Remote Host Headers', '[]'); assertVariableDetail(wrapper, 'Custom virtual environment paths', '[]'); }); diff --git a/awx/ui_next/src/screens/Setting/RADIUS/RADIUSDetail/RADIUSDetail.jsx b/awx/ui_next/src/screens/Setting/RADIUS/RADIUSDetail/RADIUSDetail.jsx index 37a8c74bdf..d1d8a4e6f6 100644 --- a/awx/ui_next/src/screens/Setting/RADIUS/RADIUSDetail/RADIUSDetail.jsx +++ b/awx/ui_next/src/screens/Setting/RADIUS/RADIUSDetail/RADIUSDetail.jsx @@ -62,8 +62,11 @@ function RADIUSDetail({ i18n }) { return ( ); diff --git a/awx/ui_next/src/screens/Setting/SAML/SAMLDetail/SAMLDetail.jsx b/awx/ui_next/src/screens/Setting/SAML/SAMLDetail/SAMLDetail.jsx index b5228ea311..e6694d8591 100644 --- a/awx/ui_next/src/screens/Setting/SAML/SAMLDetail/SAMLDetail.jsx +++ b/awx/ui_next/src/screens/Setting/SAML/SAMLDetail/SAMLDetail.jsx @@ -62,8 +62,11 @@ function SAMLDetail({ i18n }) { return ( ); diff --git a/awx/ui_next/src/screens/Setting/SAML/SAMLDetail/SAMLDetail.test.jsx b/awx/ui_next/src/screens/Setting/SAML/SAMLDetail/SAMLDetail.test.jsx index b5a003c1d2..1afaee4e24 100644 --- a/awx/ui_next/src/screens/Setting/SAML/SAMLDetail/SAMLDetail.test.jsx +++ b/awx/ui_next/src/screens/Setting/SAML/SAMLDetail/SAMLDetail.test.jsx @@ -75,7 +75,11 @@ describe('', () => { 'SAML Service Provider Public Certificate', 'mock_cert' ); - assertDetail(wrapper, 'SAML Service Provider Private Key', 'Unconfigured'); + assertDetail( + wrapper, + 'SAML Service Provider Private Key', + 'Not configured' + ); assertVariableDetail( wrapper, 'SAML Service Provider Organization Info', diff --git a/awx/ui_next/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.jsx b/awx/ui_next/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.jsx index cd5c59595b..de85665808 100644 --- a/awx/ui_next/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.jsx +++ b/awx/ui_next/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.jsx @@ -62,8 +62,11 @@ function TACACSDetail({ i18n }) { return ( ); diff --git a/awx/ui_next/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.test.jsx b/awx/ui_next/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.test.jsx index d79bd132fd..66e673b2e0 100644 --- a/awx/ui_next/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.test.jsx +++ b/awx/ui_next/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.test.jsx @@ -55,7 +55,7 @@ describe('', () => { assertDetail(wrapper, 'TACACS+ Server', 'mockhost'); assertDetail(wrapper, 'TACACS+ Port', '49'); assertDetail(wrapper, 'TACACS+ Secret', 'Encrypted'); - assertDetail(wrapper, 'TACACS+ Auth Session Timeout', '5'); + assertDetail(wrapper, 'TACACS+ Auth Session Timeout', '5 seconds'); assertDetail(wrapper, 'TACACS+ Authentication Protocol', 'ascii'); }); diff --git a/awx/ui_next/src/screens/Setting/UI/UIDetail/UIDetail.jsx b/awx/ui_next/src/screens/Setting/UI/UIDetail/UIDetail.jsx index 18776447df..2d4e475006 100644 --- a/awx/ui_next/src/screens/Setting/UI/UIDetail/UIDetail.jsx +++ b/awx/ui_next/src/screens/Setting/UI/UIDetail/UIDetail.jsx @@ -77,8 +77,11 @@ function UIDetail({ i18n }) { return ( ); diff --git a/awx/ui_next/src/screens/Setting/shared/SettingDetail.jsx b/awx/ui_next/src/screens/Setting/shared/SettingDetail.jsx index 6d56aa2ba1..56a4e42d2d 100644 --- a/awx/ui_next/src/screens/Setting/shared/SettingDetail.jsx +++ b/awx/ui_next/src/screens/Setting/shared/SettingDetail.jsx @@ -3,84 +3,115 @@ import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Detail } from '../../../components/DetailList'; import { VariablesDetail } from '../../../components/CodeMirrorInput'; +// import DetailPopover from '../../../components/DetailList/DetailPopover'; -export default withI18n()(({ i18n, label, type, value }) => { - const dataType = value === '$encrypted$' ? 'encrypted' : type; - let detail = null; +export default withI18n()( + ({ i18n, helpText, id, label, type, unit = '', value }) => { + const dataType = value === '$encrypted$' ? 'encrypted' : type; + let detail = null; - switch (dataType) { - case 'nested object': - detail = ( - - ); - break; - case 'list': - detail = ; - break; - case 'image': - detail = ( - } - /> - ); - break; - case 'encrypted': - detail = ( - - ); - break; - case 'boolean': - detail = ( - - ); - break; - case 'choice': - detail = ( - - ); - break; - case 'integer': - detail = ( - - ); - break; - case 'string': - detail = ( - - ); - break; - default: - detail = null; + switch (dataType) { + case 'nested object': + detail = ( + + ); + break; + case 'list': + detail = ( + + ); + break; + case 'image': + detail = ( + + ) + } + /> + ); + break; + case 'encrypted': + detail = ( + + ); + break; + case 'boolean': + detail = ( + + ); + break; + case 'choice': + detail = ( + + ); + break; + case 'integer': + detail = ( + + ); + break; + case 'string': + detail = ( + + ); + break; + default: + detail = null; + } + return detail; } - return detail; -}); +); diff --git a/awx/ui_next/src/screens/Setting/shared/data.allSettingOptions.json b/awx/ui_next/src/screens/Setting/shared/data.allSettingOptions.json index 35ca07e6c0..4587e845d8 100644 --- a/awx/ui_next/src/screens/Setting/shared/data.allSettingOptions.json +++ b/awx/ui_next/src/screens/Setting/shared/data.allSettingOptions.json @@ -1,4 +1,3 @@ - { "name": "Setting Detail", "actions": { @@ -94,7 +93,7 @@ }, "AUTOMATION_ANALYTICS_URL": { "type": "string", - "label": "Automation Analytics upload URL.", + "label": "Automation Analytics upload URL", "help_text": "This setting is used to to configure data collection for the Automation Analytics dashboard", "category": "System", "category_slug": "system", @@ -196,7 +195,8 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", - "defined_in_file": false + "defined_in_file": false, + "unit": "seconds" }, "AWX_ISOLATED_LAUNCH_TIMEOUT": { "type": "integer", @@ -205,7 +205,8 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", - "defined_in_file": false + "defined_in_file": false, + "unit": "seconds" }, "AWX_ISOLATED_CONNECTION_TIMEOUT": { "type": "integer", @@ -214,7 +215,8 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", - "defined_in_file": false + "defined_in_file": false, + "unit": "seconds" }, "AWX_ISOLATED_HOST_KEY_CHECKING": { "type": "boolean", @@ -331,58 +333,10 @@ "category_slug": "jobs", "defined_in_file": false }, - "PRIMARY_GALAXY_URL": { - "type": "string", - "label": "Primary Galaxy Server URL", - "help_text": "For organizations that run their own Galaxy service, this gives the option to specify a host as the primary galaxy server. Requirements will be downloaded from the primary if the specific role or collection is available there. If the content is not avilable in the primary, or if this field is left blank, it will default to galaxy.ansible.com.", - "category": "Jobs", - "category_slug": "jobs", - "defined_in_file": false - }, - "PRIMARY_GALAXY_USERNAME": { - "type": "string", - "label": "Primary Galaxy Server Username", - "help_text": "(This setting is deprecated and will be removed in a future release) For using a galaxy server at higher precedence than the public Ansible Galaxy. The username to use for basic authentication against the Galaxy instance, this is mutually exclusive with PRIMARY_GALAXY_TOKEN.", - "category": "Jobs", - "category_slug": "jobs", - "defined_in_file": false - }, - "PRIMARY_GALAXY_PASSWORD": { - "type": "string", - "label": "Primary Galaxy Server Password", - "help_text": "(This setting is deprecated and will be removed in a future release) For using a galaxy server at higher precedence than the public Ansible Galaxy. The password to use for basic authentication against the Galaxy instance, this is mutually exclusive with PRIMARY_GALAXY_TOKEN.", - "category": "Jobs", - "category_slug": "jobs", - "defined_in_file": false - }, - "PRIMARY_GALAXY_TOKEN": { - "type": "string", - "label": "Primary Galaxy Server Token", - "help_text": "For using a galaxy server at higher precedence than the public Ansible Galaxy. The token to use for connecting with the Galaxy instance, this is mutually exclusive with corresponding username and password settings.", - "category": "Jobs", - "category_slug": "jobs", - "defined_in_file": false - }, - "PRIMARY_GALAXY_AUTH_URL": { - "type": "string", - "label": "Primary Galaxy Authentication URL", - "help_text": "For using a galaxy server at higher precedence than the public Ansible Galaxy. The token_endpoint of a Keycloak server.", - "category": "Jobs", - "category_slug": "jobs", - "defined_in_file": false - }, - "PUBLIC_GALAXY_ENABLED": { - "type": "boolean", - "label": "Allow Access to Public Galaxy", - "help_text": "Allow or deny access to the public Ansible Galaxy during project updates.", - "category": "Jobs", - "category_slug": "jobs", - "defined_in_file": false - }, "GALAXY_IGNORE_CERTS": { "type": "boolean", "label": "Ignore Ansible Galaxy SSL Certificate Verification", - "help_text": "If set to true, certificate validation will not be done wheninstalling content from any Galaxy server.", + "help_text": "If set to true, certificate validation will not be done when installing content from any Galaxy server.", "category": "Jobs", "category_slug": "jobs", "defined_in_file": false @@ -432,7 +386,8 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", - "defined_in_file": false + "defined_in_file": false, + "unit": "seconds" }, "DEFAULT_INVENTORY_UPDATE_TIMEOUT": { "type": "integer", @@ -441,7 +396,8 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", - "defined_in_file": false + "defined_in_file": false, + "unit": "seconds" }, "DEFAULT_PROJECT_UPDATE_TIMEOUT": { "type": "integer", @@ -450,7 +406,8 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", - "defined_in_file": false + "defined_in_file": false, + "unit": "seconds" }, "ANSIBLE_FACT_CACHE_TIMEOUT": { "type": "integer", @@ -459,11 +416,12 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", - "defined_in_file": false + "defined_in_file": false, + "unit": "seconds" }, "MAX_FORKS": { "type": "integer", - "label": "Maximum number of forks per job.", + "label": "Maximum number of forks per job", "help_text": "Saving a Job Template with more than this number of forks will result in an error. When set to 0, no limit is applied.", "category": "Jobs", "category_slug": "jobs", @@ -598,7 +556,8 @@ "help_text": "Number of seconds for a TCP connection to external log aggregator to timeout. Applies to HTTPS and TCP log aggregator protocols.", "category": "Logging", "category_slug": "logging", - "defined_in_file": false + "defined_in_file": false, + "unit": "seconds" }, "LOG_AGGREGATOR_VERIFY_CERT": { "type": "boolean", @@ -677,7 +636,8 @@ "min_value": 1800, "category": "System", "category_slug": "system", - "defined_in_file": false + "defined_in_file": false, + "unit": "seconds" }, "SESSION_COOKIE_AGE": { "type": "integer", @@ -687,7 +647,8 @@ "max_value": 30000000000, "category": "Authentication", "category_slug": "authentication", - "defined_in_file": false + "defined_in_file": false, + "unit": "seconds" }, "SESSIONS_PER_USER": { "type": "integer", @@ -713,6 +674,7 @@ "category": "Authentication", "category_slug": "authentication", "defined_in_file": false, + "unit": "seconds", "child": { "type": "integer", "min_value": 1 @@ -2204,7 +2166,8 @@ "min_value": 0, "category": "TACACS+", "category_slug": "tacacsplus", - "defined_in_file": false + "defined_in_file": false, + "unit": "seconds" }, "TACACSPLUS_AUTH_PROTOCOL": { "type": "choice", @@ -2250,7 +2213,7 @@ }, "SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS": { "type": "list", - "label": "Google OAuth2 Whitelisted Domains", + "label": "Google OAuth2 Allowed Domains", "help_text": "Update this setting to restrict the domains who are allowed to login using Google OAuth2.", "category": "Google OAuth2", "category_slug": "google-oauth2", @@ -2542,6 +2505,14 @@ } } }, + "SAML_AUTO_CREATE_OBJECTS": { + "type": "boolean", + "label": "Automatically Create Organizations and Teams on SAML Login", + "help_text": "When enabled (the default), mapped Organizations and Teams will be created automatically on successful SAML login.", + "category": "SAML", + "category_slug": "saml", + "defined_in_file": false + }, "SOCIAL_AUTH_SAML_CALLBACK_URL": { "type": "string", "label": "SAML Assertion Consumer Service (ACS) URL", @@ -2844,7 +2815,7 @@ "AUTOMATION_ANALYTICS_URL": { "type": "string", "required": false, - "label": "Automation Analytics upload URL.", + "label": "Automation Analytics upload URL", "help_text": "This setting is used to to configure data collection for the Automation Analytics dashboard", "category": "System", "category_slug": "system", @@ -2975,6 +2946,7 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", + "unit": "seconds", "default": 1 }, "AWX_ISOLATED_LAUNCH_TIMEOUT": { @@ -2985,6 +2957,7 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", + "unit": "seconds", "default": 600 }, "AWX_ISOLATED_CONNECTION_TIMEOUT": { @@ -2995,6 +2968,7 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", + "unit": "seconds", "default": 10 }, "AWX_ISOLATED_HOST_KEY_CHECKING": { @@ -3104,65 +3078,11 @@ "category_slug": "jobs", "default": false }, - "PRIMARY_GALAXY_URL": { - "type": "string", - "required": false, - "label": "Primary Galaxy Server URL", - "help_text": "For organizations that run their own Galaxy service, this gives the option to specify a host as the primary galaxy server. Requirements will be downloaded from the primary if the specific role or collection is available there. If the content is not avilable in the primary, or if this field is left blank, it will default to galaxy.ansible.com.", - "category": "Jobs", - "category_slug": "jobs", - "default": "" - }, - "PRIMARY_GALAXY_USERNAME": { - "type": "string", - "required": false, - "label": "Primary Galaxy Server Username", - "help_text": "(This setting is deprecated and will be removed in a future release) For using a galaxy server at higher precedence than the public Ansible Galaxy. The username to use for basic authentication against the Galaxy instance, this is mutually exclusive with PRIMARY_GALAXY_TOKEN.", - "category": "Jobs", - "category_slug": "jobs", - "default": "" - }, - "PRIMARY_GALAXY_PASSWORD": { - "type": "string", - "required": false, - "label": "Primary Galaxy Server Password", - "help_text": "(This setting is deprecated and will be removed in a future release) For using a galaxy server at higher precedence than the public Ansible Galaxy. The password to use for basic authentication against the Galaxy instance, this is mutually exclusive with PRIMARY_GALAXY_TOKEN.", - "category": "Jobs", - "category_slug": "jobs", - "default": "" - }, - "PRIMARY_GALAXY_TOKEN": { - "type": "string", - "required": false, - "label": "Primary Galaxy Server Token", - "help_text": "For using a galaxy server at higher precedence than the public Ansible Galaxy. The token to use for connecting with the Galaxy instance, this is mutually exclusive with corresponding username and password settings.", - "category": "Jobs", - "category_slug": "jobs", - "default": "" - }, - "PRIMARY_GALAXY_AUTH_URL": { - "type": "string", - "required": false, - "label": "Primary Galaxy Authentication URL", - "help_text": "For using a galaxy server at higher precedence than the public Ansible Galaxy. The token_endpoint of a Keycloak server.", - "category": "Jobs", - "category_slug": "jobs", - "default": "" - }, - "PUBLIC_GALAXY_ENABLED": { - "type": "boolean", - "required": false, - "label": "Allow Access to Public Galaxy", - "help_text": "Allow or deny access to the public Ansible Galaxy during project updates.", - "category": "Jobs", - "category_slug": "jobs", - "default": true - }, "GALAXY_IGNORE_CERTS": { "type": "boolean", "required": false, "label": "Ignore Ansible Galaxy SSL Certificate Verification", - "help_text": "If set to true, certificate validation will not be done wheninstalling content from any Galaxy server.", + "help_text": "If set to true, certificate validation will not be done when installing content from any Galaxy server.", "category": "Jobs", "category_slug": "jobs", "default": false @@ -3219,6 +3139,7 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", + "unit": "seconds", "default": 0 }, "DEFAULT_INVENTORY_UPDATE_TIMEOUT": { @@ -3229,6 +3150,7 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", + "unit": "seconds", "default": 0 }, "DEFAULT_PROJECT_UPDATE_TIMEOUT": { @@ -3239,6 +3161,7 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", + "unit": "seconds", "default": 0 }, "ANSIBLE_FACT_CACHE_TIMEOUT": { @@ -3249,12 +3172,13 @@ "min_value": 0, "category": "Jobs", "category_slug": "jobs", + "unit": "seconds", "default": 0 }, "MAX_FORKS": { "type": "integer", "required": false, - "label": "Maximum number of forks per job.", + "label": "Maximum number of forks per job", "help_text": "Saving a Job Template with more than this number of forks will result in an error. When set to 0, no limit is applied.", "category": "Jobs", "category_slug": "jobs", @@ -3407,6 +3331,7 @@ "help_text": "Number of seconds for a TCP connection to external log aggregator to timeout. Applies to HTTPS and TCP log aggregator protocols.", "category": "Logging", "category_slug": "logging", + "unit": "seconds", "default": 5 }, "LOG_AGGREGATOR_VERIFY_CERT": { @@ -3493,6 +3418,7 @@ "min_value": 1800, "category": "System", "category_slug": "system", + "unit": "seconds", "default": 14400 }, "SESSION_COOKIE_AGE": { @@ -3504,6 +3430,7 @@ "max_value": 30000000000, "category": "Authentication", "category_slug": "authentication", + "unit": "seconds", "default": 1800 }, "SESSIONS_PER_USER": { @@ -3532,6 +3459,7 @@ "help_text": "Dictionary for customizing OAuth 2 timeouts, available items are `ACCESS_TOKEN_EXPIRE_SECONDS`, the duration of access tokens in the number of seconds, `AUTHORIZATION_CODE_EXPIRE_SECONDS`, the duration of authorization codes in the number of seconds, and `REFRESH_TOKEN_EXPIRE_SECONDS`, the duration of refresh tokens, after expired access tokens, in the number of seconds.", "category": "Authentication", "category_slug": "authentication", + "unit": "seconds", "default": { "ACCESS_TOKEN_EXPIRE_SECONDS": 31536000000, "AUTHORIZATION_CODE_EXPIRE_SECONDS": 600, @@ -5668,6 +5596,7 @@ "min_value": 0, "category": "TACACS+", "category_slug": "tacacsplus", + "unit": "seconds", "default": 5 }, "TACACSPLUS_AUTH_PROTOCOL": { @@ -5712,7 +5641,7 @@ "SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS": { "type": "list", "required": false, - "label": "Google OAuth2 Whitelisted Domains", + "label": "Google OAuth2 Allowed Domains", "help_text": "Update this setting to restrict the domains who are allowed to login using Google OAuth2.", "category": "Google OAuth2", "category_slug": "google-oauth2", @@ -6208,6 +6137,15 @@ } } }, + "SAML_AUTO_CREATE_OBJECTS": { + "type": "boolean", + "required": false, + "label": "Automatically Create Organizations and Teams on SAML Login", + "help_text": "When enabled (the default), mapped Organizations and Teams will be created automatically on successful SAML login.", + "category": "SAML", + "category_slug": "saml", + "default": true + }, "SOCIAL_AUTH_SAML_SP_ENTITY_ID": { "type": "string", "required": false, diff --git a/awx/ui_next/src/screens/Setting/shared/data.jobSettings.json b/awx/ui_next/src/screens/Setting/shared/data.jobSettings.json index 25f5126ef6..190a346560 100644 --- a/awx/ui_next/src/screens/Setting/shared/data.jobSettings.json +++ b/awx/ui_next/src/screens/Setting/shared/data.jobSettings.json @@ -1,3 +1,4 @@ + { "AD_HOC_COMMANDS": [ "command" @@ -23,12 +24,6 @@ "AWX_ROLES_ENABLED": true, "AWX_COLLECTIONS_ENABLED": true, "AWX_SHOW_PLAYBOOK_LINKS": false, - "PRIMARY_GALAXY_URL": "https://galaxy.server.com", - "PRIMARY_GALAXY_USERNAME": "", - "PRIMARY_GALAXY_PASSWORD": "", - "PRIMARY_GALAXY_TOKEN": "$encrypted$", - "PRIMARY_GALAXY_AUTH_URL": "https://galaxy.auth.com", - "PUBLIC_GALAXY_ENABLED": true, "GALAXY_IGNORE_CERTS": false, "STDOUT_MAX_BYTES_DISPLAY": 1048576, "EVENT_STDOUT_MAX_BYTES_DISPLAY": 1024, diff --git a/awx/ui_next/src/screens/Setting/shared/settingUtils.js b/awx/ui_next/src/screens/Setting/shared/settingUtils.js index 5e33e0e54f..78d27142aa 100644 --- a/awx/ui_next/src/screens/Setting/shared/settingUtils.js +++ b/awx/ui_next/src/screens/Setting/shared/settingUtils.js @@ -3,10 +3,13 @@ export function sortNestedDetails(obj = {}) { const notNested = Object.entries(obj).filter( ([, value]) => !nestedTypes.includes(value.type) ); - const nested = Object.entries(obj).filter(([, value]) => - nestedTypes.includes(value.type) + const nestedList = Object.entries(obj).filter( + ([, value]) => value.type === 'list' ); - return [...notNested, ...nested]; + const nestedObject = Object.entries(obj).filter( + ([, value]) => value.type === 'nested object' + ); + return [...notNested, ...nestedList, ...nestedObject]; } export function pluck(sourceObject, ...keys) {