mirror of
https://github.com/ansible/awx.git
synced 2026-03-22 11:25:08 -02:30
Fixes erroneous disabling of name input field on container and instance group forms
This commit is contained in:
@@ -30,13 +30,16 @@ function ContainerGroup({ setBreadcrumb }) {
|
|||||||
isLoading,
|
isLoading,
|
||||||
error: contentError,
|
error: contentError,
|
||||||
request: fetchInstanceGroups,
|
request: fetchInstanceGroups,
|
||||||
result: { instanceGroup, defaultExecution },
|
result: { instanceGroup, defaultControlPlane, defaultExecution },
|
||||||
} = useRequest(
|
} = useRequest(
|
||||||
useCallback(async () => {
|
useCallback(async () => {
|
||||||
const [
|
const [
|
||||||
{ data },
|
{ data },
|
||||||
{
|
{
|
||||||
data: { DEFAULT_EXECUTION_QUEUE_NAME },
|
data: {
|
||||||
|
DEFAULT_EXECUTION_QUEUE_NAME,
|
||||||
|
DEFAULT_CONTROL_PLANE_QUEUE_NAME,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
InstanceGroupsAPI.readDetail(id),
|
InstanceGroupsAPI.readDetail(id),
|
||||||
@@ -44,6 +47,7 @@ function ContainerGroup({ setBreadcrumb }) {
|
|||||||
]);
|
]);
|
||||||
return {
|
return {
|
||||||
instanceGroup: data,
|
instanceGroup: data,
|
||||||
|
defaultControlPlane: DEFAULT_CONTROL_PLANE_QUEUE_NAME,
|
||||||
defaultExecution: DEFAULT_EXECUTION_QUEUE_NAME,
|
defaultExecution: DEFAULT_EXECUTION_QUEUE_NAME,
|
||||||
};
|
};
|
||||||
}, [id]),
|
}, [id]),
|
||||||
@@ -123,6 +127,7 @@ function ContainerGroup({ setBreadcrumb }) {
|
|||||||
<Route path="/instance_groups/container_group/:id/edit">
|
<Route path="/instance_groups/container_group/:id/edit">
|
||||||
<ContainerGroupEdit
|
<ContainerGroupEdit
|
||||||
instanceGroup={instanceGroup}
|
instanceGroup={instanceGroup}
|
||||||
|
defaultControlPlane={defaultControlPlane}
|
||||||
defaultExecution={defaultExecution}
|
defaultExecution={defaultExecution}
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { jsonToYaml, isJsonString } from 'util/yaml';
|
|||||||
|
|
||||||
import ContainerGroupForm from '../shared/ContainerGroupForm';
|
import ContainerGroupForm from '../shared/ContainerGroupForm';
|
||||||
|
|
||||||
function ContainerGroupAdd() {
|
function ContainerGroupAdd({ defaultExecution, defaultControlPlane }) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [submitError, setSubmitError] = useState(null);
|
const [submitError, setSubmitError] = useState(null);
|
||||||
|
|
||||||
@@ -93,6 +93,8 @@ function ContainerGroupAdd() {
|
|||||||
<Card>
|
<Card>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<ContainerGroupForm
|
<ContainerGroupForm
|
||||||
|
defaultControlPlane={defaultControlPlane}
|
||||||
|
defaultExecution={defaultExecution}
|
||||||
initialPodSpec={initialPodSpec}
|
initialPodSpec={initialPodSpec}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
submitError={submitError}
|
submitError={submitError}
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ import ContentError from 'components/ContentError';
|
|||||||
import ContentLoading from 'components/ContentLoading';
|
import ContentLoading from 'components/ContentLoading';
|
||||||
import ContainerGroupForm from '../shared/ContainerGroupForm';
|
import ContainerGroupForm from '../shared/ContainerGroupForm';
|
||||||
|
|
||||||
function ContainerGroupEdit({ instanceGroup }) {
|
function ContainerGroupEdit({
|
||||||
|
instanceGroup,
|
||||||
|
defaultControlPlane,
|
||||||
|
defaultExecution,
|
||||||
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [submitError, setSubmitError] = useState(null);
|
const [submitError, setSubmitError] = useState(null);
|
||||||
const detailsIUrl = `/instance_groups/container_group/${instanceGroup.id}/details`;
|
const detailsIUrl = `/instance_groups/container_group/${instanceGroup.id}/details`;
|
||||||
@@ -77,6 +81,8 @@ function ContainerGroupEdit({ instanceGroup }) {
|
|||||||
return (
|
return (
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<ContainerGroupForm
|
<ContainerGroupForm
|
||||||
|
defaultControlPlane={defaultControlPlane}
|
||||||
|
defaultExecution={defaultExecution}
|
||||||
instanceGroup={instanceGroup}
|
instanceGroup={instanceGroup}
|
||||||
initialPodSpec={initialPodSpec}
|
initialPodSpec={initialPodSpec}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { CardBody } from 'components/Card';
|
|||||||
import { InstanceGroupsAPI } from 'api';
|
import { InstanceGroupsAPI } from 'api';
|
||||||
import InstanceGroupForm from '../shared/InstanceGroupForm';
|
import InstanceGroupForm from '../shared/InstanceGroupForm';
|
||||||
|
|
||||||
function InstanceGroupAdd() {
|
function InstanceGroupAdd({ defaultExecution, defaultControlPlane }) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [submitError, setSubmitError] = useState(null);
|
const [submitError, setSubmitError] = useState(null);
|
||||||
|
|
||||||
@@ -28,6 +28,8 @@ function InstanceGroupAdd() {
|
|||||||
<Card>
|
<Card>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<InstanceGroupForm
|
<InstanceGroupForm
|
||||||
|
defaultControlPlane={defaultControlPlane}
|
||||||
|
defaultExecution={defaultExecution}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
submitError={submitError}
|
submitError={submitError}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
|
|||||||
@@ -19,13 +19,21 @@ function InstanceGroups() {
|
|||||||
request: settingsRequest,
|
request: settingsRequest,
|
||||||
isLoading: isSettingsRequestLoading,
|
isLoading: isSettingsRequestLoading,
|
||||||
error: settingsRequestError,
|
error: settingsRequestError,
|
||||||
result: isKubernetes,
|
result: { isKubernetes, defaultControlPlane, defaultExecution },
|
||||||
} = useRequest(
|
} = useRequest(
|
||||||
useCallback(async () => {
|
useCallback(async () => {
|
||||||
const {
|
const {
|
||||||
data: { IS_K8S },
|
data: {
|
||||||
|
IS_K8S,
|
||||||
|
DEFAULT_CONTROL_PLANE_QUEUE_NAME,
|
||||||
|
DEFAULT_EXECUTION_QUEUE_NAME,
|
||||||
|
},
|
||||||
} = await SettingsAPI.readCategory('all');
|
} = await SettingsAPI.readCategory('all');
|
||||||
return IS_K8S;
|
return {
|
||||||
|
isKubernetes: IS_K8S,
|
||||||
|
defaultControlPlane: DEFAULT_CONTROL_PLANE_QUEUE_NAME,
|
||||||
|
defaultExecution: DEFAULT_EXECUTION_QUEUE_NAME,
|
||||||
|
};
|
||||||
}, []),
|
}, []),
|
||||||
{ isLoading: true }
|
{ isLoading: true }
|
||||||
);
|
);
|
||||||
@@ -75,16 +83,22 @@ function InstanceGroups() {
|
|||||||
/>
|
/>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/instance_groups/container_group/add">
|
<Route path="/instance_groups/container_group/add">
|
||||||
<ContainerGroupAdd />
|
<ContainerGroupAdd
|
||||||
|
defaultControlPlane={defaultControlPlane}
|
||||||
|
defaultExecution={defaultExecution}
|
||||||
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/instance_groups/container_group/:id">
|
<Route path="/instance_groups/container_group/:id">
|
||||||
<ContainerGroup setBreadcrumb={buildBreadcrumbConfig} />
|
<ContainerGroup setBreadcrumb={buildBreadcrumbConfig} />
|
||||||
</Route>
|
</Route>
|
||||||
{!isSettingsRequestLoading && !isKubernetes ? (
|
{!isSettingsRequestLoading && !isKubernetes && (
|
||||||
<Route path="/instance_groups/add">
|
<Route path="/instance_groups/add">
|
||||||
<InstanceGroupAdd />
|
<InstanceGroupAdd
|
||||||
|
defaultControlPlane={defaultControlPlane}
|
||||||
|
defaultExecution={defaultExecution}
|
||||||
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
) : null}
|
)}
|
||||||
<Route path="/instance_groups/:id">
|
<Route path="/instance_groups/:id">
|
||||||
<InstanceGroup setBreadcrumb={buildBreadcrumbConfig} />
|
<InstanceGroup setBreadcrumb={buildBreadcrumbConfig} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import FormField, {
|
|||||||
CheckboxField,
|
CheckboxField,
|
||||||
} from 'components/FormField';
|
} from 'components/FormField';
|
||||||
import FormActionGroup from 'components/FormActionGroup';
|
import FormActionGroup from 'components/FormActionGroup';
|
||||||
import { required } from 'util/validators';
|
import { combine, required, protectedResourceName } from 'util/validators';
|
||||||
import {
|
import {
|
||||||
FormColumnLayout,
|
FormColumnLayout,
|
||||||
FormFullWidthLayout,
|
FormFullWidthLayout,
|
||||||
@@ -21,12 +21,20 @@ import {
|
|||||||
import CredentialLookup from 'components/Lookup/CredentialLookup';
|
import CredentialLookup from 'components/Lookup/CredentialLookup';
|
||||||
import { VariablesField } from 'components/CodeEditor';
|
import { VariablesField } from 'components/CodeEditor';
|
||||||
|
|
||||||
function ContainerGroupFormFields({ instanceGroup }) {
|
function ContainerGroupFormFields({
|
||||||
|
instanceGroup,
|
||||||
|
defaultControlPlane,
|
||||||
|
defaultExecution,
|
||||||
|
}) {
|
||||||
const { setFieldValue, setFieldTouched } = useFormikContext();
|
const { setFieldValue, setFieldTouched } = useFormikContext();
|
||||||
const [credentialField, credentialMeta, credentialHelpers] =
|
const [credentialField, credentialMeta, credentialHelpers] =
|
||||||
useField('credential');
|
useField('credential');
|
||||||
|
|
||||||
const [nameField] = useField('name');
|
const [, { initialValue }] = useField('name');
|
||||||
|
|
||||||
|
const isProtected =
|
||||||
|
initialValue === `${defaultControlPlane}` ||
|
||||||
|
initialValue === `${defaultExecution}`;
|
||||||
|
|
||||||
const [overrideField] = useField('override');
|
const [overrideField] = useField('override');
|
||||||
|
|
||||||
@@ -42,11 +50,21 @@ function ContainerGroupFormFields({ instanceGroup }) {
|
|||||||
<>
|
<>
|
||||||
<FormField
|
<FormField
|
||||||
name="name"
|
name="name"
|
||||||
|
helperText={
|
||||||
|
isProtected
|
||||||
|
? t`This is a protected Instance Group. The name cannot be changed.`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
id="container-group-name"
|
id="container-group-name"
|
||||||
label={t`Name`}
|
label={t`Name`}
|
||||||
type="text"
|
type="text"
|
||||||
validate={required(null)}
|
validate={combine([
|
||||||
isDisabled={nameField.value === 'default'}
|
required(null),
|
||||||
|
protectedResourceName(
|
||||||
|
t`This is a protected name for Container Groups. Please use a different name.`,
|
||||||
|
[defaultControlPlane, defaultExecution]
|
||||||
|
),
|
||||||
|
])}
|
||||||
isRequired
|
isRequired
|
||||||
/>
|
/>
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
|
|||||||
@@ -3,42 +3,48 @@ import { func, shape } from 'prop-types';
|
|||||||
import { Formik, useField } from 'formik';
|
import { Formik, useField } from 'formik';
|
||||||
|
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Form, Tooltip } from '@patternfly/react-core';
|
import { Form } from '@patternfly/react-core';
|
||||||
|
|
||||||
import FormField, { FormSubmitError } from 'components/FormField';
|
import FormField, { FormSubmitError } from 'components/FormField';
|
||||||
import FormActionGroup from 'components/FormActionGroup';
|
import FormActionGroup from 'components/FormActionGroup';
|
||||||
import { required, minMaxValue } from 'util/validators';
|
import {
|
||||||
|
combine,
|
||||||
|
required,
|
||||||
|
protectedResourceName,
|
||||||
|
minMaxValue,
|
||||||
|
} from 'util/validators';
|
||||||
import { FormColumnLayout } from 'components/FormLayout';
|
import { FormColumnLayout } from 'components/FormLayout';
|
||||||
|
|
||||||
function InstanceGroupFormFields({ defaultControlPlane, defaultExecution }) {
|
function InstanceGroupFormFields({ defaultControlPlane, defaultExecution }) {
|
||||||
const [{ value }, ,] = useField('name');
|
const [, { initialValue }] = useField('name');
|
||||||
const isDisabled =
|
const isProtected =
|
||||||
value === defaultExecution || value === defaultControlPlane;
|
initialValue === `${defaultControlPlane}` ||
|
||||||
|
initialValue === `${defaultExecution}`;
|
||||||
|
|
||||||
|
const validators = combine([
|
||||||
|
required(null),
|
||||||
|
protectedResourceName(
|
||||||
|
[defaultControlPlane, defaultExecution],
|
||||||
|
t`This is a protected name for Instance Groups. Please use a different name.`
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isDisabled ? (
|
<FormField
|
||||||
<Tooltip content={t`Name cannot be changed on this Instance Group`}>
|
name="name"
|
||||||
<FormField
|
helperText={
|
||||||
name="name"
|
isProtected
|
||||||
id="instance-group-name"
|
? t`This is a protected Instance Group. The name cannot be changed.`
|
||||||
label={t`Name`}
|
: ''
|
||||||
type="text"
|
}
|
||||||
validate={required(null)}
|
id="instance-group-name"
|
||||||
isRequired
|
label={t`Name`}
|
||||||
isDisabled={isDisabled}
|
type="text"
|
||||||
/>
|
validate={validators}
|
||||||
</Tooltip>
|
isRequired
|
||||||
) : (
|
isDisabled={isProtected}
|
||||||
<FormField
|
/>
|
||||||
name="name"
|
|
||||||
id="instance-group-name"
|
|
||||||
label={t`Name`}
|
|
||||||
type="text"
|
|
||||||
validate={required(null)}
|
|
||||||
isRequired
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<FormField
|
<FormField
|
||||||
id="instance-group-policy-instance-minimum"
|
id="instance-group-policy-instance-minimum"
|
||||||
label={t`Policy instance minimum`}
|
label={t`Policy instance minimum`}
|
||||||
@@ -66,7 +72,6 @@ function InstanceGroupFormFields({ defaultControlPlane, defaultExecution }) {
|
|||||||
|
|
||||||
function InstanceGroupForm({
|
function InstanceGroupForm({
|
||||||
instanceGroup = {},
|
instanceGroup = {},
|
||||||
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onCancel,
|
onCancel,
|
||||||
submitError,
|
submitError,
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ describe('<InstanceGroupForm/>', () => {
|
|||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Initially renders successfully', () => {
|
test('Initially renders successfully', () => {
|
||||||
|
|||||||
@@ -186,3 +186,8 @@ export function regExp() {
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function protectedResourceName(message, names = []) {
|
||||||
|
return (value) =>
|
||||||
|
names.some((name) => value.trim() === `${name}`) ? message : undefined;
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
regExp,
|
regExp,
|
||||||
requiredEmail,
|
requiredEmail,
|
||||||
validateTime,
|
validateTime,
|
||||||
|
protectedResourceName,
|
||||||
} from './validators';
|
} from './validators';
|
||||||
|
|
||||||
describe('validators', () => {
|
describe('validators', () => {
|
||||||
@@ -187,4 +188,21 @@ describe('validators', () => {
|
|||||||
expect(validateTime()('12.15 PM')).toEqual('Invalid time format');
|
expect(validateTime()('12.15 PM')).toEqual('Invalid time format');
|
||||||
expect(validateTime()('12;15 PM')).toEqual('Invalid time format');
|
expect(validateTime()('12;15 PM')).toEqual('Invalid time format');
|
||||||
});
|
});
|
||||||
|
test('protectedResourceName should validate properly', () => {
|
||||||
|
expect(
|
||||||
|
protectedResourceName('failed validation', ['Alex'])('Apollo')
|
||||||
|
).toBeUndefined();
|
||||||
|
expect(
|
||||||
|
protectedResourceName('failed validation', ['Alex', 'Athena'])('alex')
|
||||||
|
).toBeUndefined();
|
||||||
|
expect(
|
||||||
|
protectedResourceName('failed validation', ['Alex', 'Athena'])('Alex')
|
||||||
|
).toEqual('failed validation');
|
||||||
|
expect(
|
||||||
|
protectedResourceName('failed validation', ['Alex'])('Alex')
|
||||||
|
).toEqual('failed validation');
|
||||||
|
expect(
|
||||||
|
protectedResourceName('failed validation', ['Alex'])('Alex ')
|
||||||
|
).toEqual('failed validation');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user