Delete inventory source fields

This commit is contained in:
Jake McDermott 2020-07-29 18:33:46 -04:00 committed by Ryan Petrello
parent dce946e93f
commit 42e70bc852
No known key found for this signature in database
GPG Key ID: F2AA5F2122351777
20 changed files with 16 additions and 455 deletions

View File

@ -3,10 +3,9 @@ import { Link, useHistory } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Button, Chip, List, ListItem } from '@patternfly/react-core';
import { Button, List, ListItem } from '@patternfly/react-core';
import AlertModal from '../../../components/AlertModal';
import { CardBody, CardActionsRow } from '../../../components/Card';
import ChipGroup from '../../../components/ChipGroup';
import { VariablesDetail } from '../../../components/CodeMirrorInput';
import ContentError from '../../../components/ContentError';
import ContentLoading from '../../../components/ContentLoading';
@ -28,16 +27,13 @@ function InventorySourceDetail({ inventorySource, i18n }) {
created,
custom_virtualenv,
description,
group_by,
id,
instance_filters,
modified,
name,
overwrite,
overwrite_vars,
source,
source_path,
source_regions,
source_vars,
update_cache_timeout,
update_on_launch,
@ -233,57 +229,6 @@ function InventorySourceDetail({ inventorySource, i18n }) {
))}
/>
)}
{source_regions && (
<Detail
fullWidth
label={i18n._(t`Regions`)}
value={
<ChipGroup
numChips={5}
totalChips={source_regions.split(',').length}
>
{source_regions.split(',').map(region => (
<Chip key={region} isReadOnly>
{region}
</Chip>
))}
</ChipGroup>
}
/>
)}
{instance_filters && (
<Detail
fullWidth
label={i18n._(t`Instance filters`)}
value={
<ChipGroup
numChips={5}
totalChips={instance_filters.split(',').length}
>
{instance_filters.split(',').map(filter => (
<Chip key={filter} isReadOnly>
{filter}
</Chip>
))}
</ChipGroup>
}
/>
)}
{group_by && (
<Detail
fullWidth
label={i18n._(t`Only group by`)}
value={
<ChipGroup numChips={5} totalChips={group_by.split(',').length}>
{group_by.split(',').map(group => (
<Chip key={group} isReadOnly>
{group}
</Chip>
))}
</ChipGroup>
}
/>
)}
{optionsList && (
<Detail fullWidth label={i18n._(t`Options`)} value={optionsList} />
)}

View File

@ -64,32 +64,6 @@ describe('InventorySourceDetail', () => {
assertDetail(wrapper, 'Inventory file', 'foo');
assertDetail(wrapper, 'Verbosity', '2 (Debug)');
assertDetail(wrapper, 'Cache timeout', '2 seconds');
expect(
wrapper
.find('Detail[label="Regions"]')
.containsAllMatchingElements([
<span>us-east-1</span>,
<span>us-east-2</span>,
])
).toEqual(true);
expect(
wrapper
.find('Detail[label="Instance filters"]')
.containsAllMatchingElements([
<span>filter1</span>,
<span>filter2</span>,
<span>filter3</span>,
])
).toEqual(true);
expect(
wrapper
.find('Detail[label="Only group by"]')
.containsAllMatchingElements([
<span>group1</span>,
<span>group2</span>,
<span>group3</span>,
])
).toEqual(true);
expect(wrapper.find('CredentialChip').text()).toBe('Cloud: mock cred');
expect(wrapper.find('VariablesDetail').prop('value')).toEqual(
'---\nfoo: bar'

View File

@ -75,14 +75,11 @@ const InventorySourceFormFields = ({ sourceOptions, i18n }) => {
} else {
const defaults = {
credential: null,
group_by: '',
instance_filters: '',
overwrite: false,
overwrite_vars: false,
source: sourceType,
source_path: '',
source_project: null,
source_regions: '',
source_script: null,
source_vars: '---\n',
update_cache_timeout: 0,
@ -200,15 +197,12 @@ const InventorySourceForm = ({
credential: source?.summary_fields?.credential || null,
custom_virtualenv: source?.custom_virtualenv || '',
description: source?.description || '',
group_by: source?.group_by || '',
instance_filters: source?.instance_filters || '',
name: source?.name || '',
overwrite: source?.overwrite || false,
overwrite_vars: source?.overwrite_vars || false,
source: source?.source || '',
source_path: source?.source_path === '' ? '/ (project root)' : '',
source_project: source?.summary_fields?.source_project || null,
source_regions: source?.source_regions || '',
source_script: source?.summary_fields?.source_script || null,
source_vars: source?.source_vars || '---\n',
update_cache_timeout: source?.update_cache_timeout || 0,

View File

@ -3,14 +3,9 @@ import { useField } from 'formik';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
import {
OptionsField,
RegionsField,
SourceVarsField,
VerbosityField,
} from './SharedFields';
import { OptionsField, SourceVarsField, VerbosityField } from './SharedFields';
const AzureSubForm = ({ i18n, sourceOptions }) => {
const AzureSubForm = ({ i18n }) => {
const [credentialField, credentialMeta, credentialHelpers] = useField(
'credential'
);
@ -29,11 +24,6 @@ const AzureSubForm = ({ i18n, sourceOptions }) => {
value={credentialField.value}
required
/>
<RegionsField
regionOptions={
sourceOptions?.actions?.POST?.source_regions?.azure_rm_region_choices
}
/>
<VerbosityField />
<OptionsField />
<SourceVarsField />

View File

@ -10,13 +10,10 @@ jest.mock('../../../../api/models/Credentials');
const initialValues = {
credential: null,
custom_virtualenv: '',
group_by: '',
instance_filters: '',
overwrite: false,
overwrite_vars: false,
source_path: '',
source_project: null,
source_regions: '',
source_script: null,
source_vars: '---\n',
update_cache_timeout: 0,
@ -27,11 +24,7 @@ const initialValues = {
const mockSourceOptions = {
actions: {
POST: {
source_regions: {
azure_rm_region_choices: [],
},
},
POST: {},
},
};
@ -58,7 +51,6 @@ describe('<AzureSubForm />', () => {
test('should render subform fields', () => {
expect(wrapper.find('FormGroup[label="Credential"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Regions"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Verbosity"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Update options"]')).toHaveLength(1);
expect(

View File

@ -10,13 +10,10 @@ jest.mock('../../../../api/models/Credentials');
const initialValues = {
credential: null,
custom_virtualenv: '',
group_by: '',
instance_filters: '',
overwrite: false,
overwrite_vars: false,
source_path: '',
source_project: null,
source_regions: '',
source_script: null,
source_vars: '---\n',
update_cache_timeout: 0,

View File

@ -3,24 +3,10 @@ import { useField } from 'formik';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
import {
GroupByField,
InstanceFiltersField,
OptionsField,
RegionsField,
SourceVarsField,
VerbosityField,
} from './SharedFields';
import { OptionsField, SourceVarsField, VerbosityField } from './SharedFields';
const EC2SubForm = ({ i18n, sourceOptions }) => {
const EC2SubForm = ({ i18n }) => {
const [credentialField, , credentialHelpers] = useField('credential');
const groupByOptionsObj = Object.assign(
{},
...sourceOptions?.actions?.POST?.group_by?.ec2_group_by_choices.map(
([key, val]) => ({ [key]: val })
)
);
return (
<>
<CredentialLookup
@ -31,13 +17,6 @@ const EC2SubForm = ({ i18n, sourceOptions }) => {
credentialHelpers.setValue(value);
}}
/>
<RegionsField
regionOptions={
sourceOptions?.actions?.POST?.source_regions?.ec2_region_choices
}
/>
<InstanceFiltersField />
<GroupByField fixedOptions={groupByOptionsObj} />
<VerbosityField />
<OptionsField />
<SourceVarsField />

View File

@ -10,13 +10,10 @@ jest.mock('../../../../api/models/Credentials');
const initialValues = {
credential: null,
custom_virtualenv: '',
group_by: '',
instance_filters: '',
overwrite: false,
overwrite_vars: false,
source_path: '',
source_project: null,
source_regions: '',
source_script: null,
source_vars: '---\n',
update_cache_timeout: 0,
@ -27,14 +24,7 @@ const initialValues = {
const mockSourceOptions = {
actions: {
POST: {
source_regions: {
ec2_region_choices: [],
},
group_by: {
ec2_group_by_choices: [],
},
},
POST: {},
},
};
@ -61,9 +51,6 @@ describe('<EC2SubForm />', () => {
test('should render subform fields', () => {
expect(wrapper.find('FormGroup[label="Credential"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Regions"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Instance filters"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Only group by"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Verbosity"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Update options"]')).toHaveLength(1);
expect(

View File

@ -3,9 +3,9 @@ import { useField } from 'formik';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
import { OptionsField, RegionsField, VerbosityField } from './SharedFields';
import { OptionsField, VerbosityField } from './SharedFields';
const GCESubForm = ({ i18n, sourceOptions }) => {
const GCESubForm = ({ i18n }) => {
const [credentialField, credentialMeta, credentialHelpers] = useField(
'credential'
);
@ -24,11 +24,6 @@ const GCESubForm = ({ i18n, sourceOptions }) => {
value={credentialField.value}
required
/>
<RegionsField
regionOptions={
sourceOptions?.actions?.POST?.source_regions?.gce_region_choices
}
/>
<VerbosityField />
<OptionsField />
</>

View File

@ -10,13 +10,10 @@ jest.mock('../../../../api/models/Credentials');
const initialValues = {
credential: null,
custom_virtualenv: '',
group_by: '',
instance_filters: '',
overwrite: false,
overwrite_vars: false,
source_path: '',
source_project: null,
source_regions: '',
source_script: null,
source_vars: '---\n',
update_cache_timeout: 0,
@ -27,11 +24,7 @@ const initialValues = {
const mockSourceOptions = {
actions: {
POST: {
source_regions: {
gce_region_choices: [],
},
},
POST: {},
},
};
@ -58,7 +51,6 @@ describe('<GCESubForm />', () => {
test('should render subform fields', () => {
expect(wrapper.find('FormGroup[label="Credential"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Regions"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Verbosity"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Update options"]')).toHaveLength(1);
expect(

View File

@ -10,13 +10,10 @@ jest.mock('../../../../api/models/Credentials');
const initialValues = {
credential: null,
custom_virtualenv: '',
group_by: '',
instance_filters: '',
overwrite: false,
overwrite_vars: false,
source_path: '',
source_project: null,
source_regions: '',
source_script: null,
source_vars: '---\n',
update_cache_timeout: 0,

View File

@ -11,13 +11,10 @@ jest.mock('../../../../api/models/Credentials');
const initialValues = {
credential: null,
custom_virtualenv: '',
group_by: '',
instance_filters: '',
overwrite: false,
overwrite_vars: false,
source_path: '',
source_project: null,
source_regions: '',
source_script: null,
source_vars: '---\n',
update_cache_timeout: 0,

View File

@ -10,13 +10,10 @@ jest.mock('../../../../api/models/Credentials');
const initialValues = {
credential: null,
custom_virtualenv: '',
group_by: '',
instance_filters: '',
overwrite: false,
overwrite_vars: false,
source_path: '',
source_project: null,
source_regions: '',
source_script: null,
source_vars: '---\n',
update_cache_timeout: 0,

View File

@ -1,16 +1,9 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect } from 'react';
import { withI18n } from '@lingui/react';
import { t, Trans } from '@lingui/macro';
import { t } from '@lingui/macro';
import { useField } from 'formik';
import {
FormGroup,
Select,
SelectOption,
SelectVariant,
} from '@patternfly/react-core';
import { arrayToString, stringToArray } from '../../../../util/strings';
import { FormGroup } from '@patternfly/react-core';
import { minMaxValue } from '../../../../util/validators';
import { BrandName } from '../../../../variables';
import AnsibleSelect from '../../../../components/AnsibleSelect';
import { VariablesField } from '../../../../components/CodeMirrorInput';
import FormField, {
@ -32,196 +25,6 @@ export const SourceVarsField = withI18n()(({ i18n }) => (
</FormFullWidthLayout>
));
export const RegionsField = withI18n()(({ i18n, regionOptions }) => {
const [field, meta, helpers] = useField('source_regions');
const [isOpen, setIsOpen] = useState(false);
const options = Object.assign(
{},
...regionOptions.map(([key, val]) => ({ [key]: val }))
);
const selected = stringToArray(field?.value)
.filter(i => options[i])
.map(val => options[val]);
return (
<FormGroup
fieldId="regions"
helperTextInvalid={meta.error}
validated="default"
label={i18n._(t`Regions`)}
labelIcon={
<FieldTooltip
content={
<Trans>
Click on the regions field to see a list of regions for your cloud
provider. You can select multiple regions, or choose
<em> All</em> to include all regions. Only Hosts associated with
the selected regions will be updated.
</Trans>
}
/>
}
>
<Select
variant={SelectVariant.typeaheadMulti}
id="regions"
onToggle={setIsOpen}
onClear={() => helpers.setValue('')}
onSelect={(event, option) => {
let selectedValues;
if (selected.includes(option)) {
selectedValues = selected.filter(o => o !== option);
} else {
selectedValues = selected.concat(option);
}
const selectedKeys = selectedValues.map(val =>
Object.keys(options).find(key => options[key] === val)
);
helpers.setValue(arrayToString(selectedKeys));
}}
isExpanded={isOpen}
placeholderText={i18n._(t`Select a region`)}
selections={selected}
>
{regionOptions.map(([key, val]) => (
<SelectOption key={key} value={val} />
))}
</Select>
</FormGroup>
);
});
export const GroupByField = withI18n()(
({ i18n, fixedOptions, isCreatable = false }) => {
const [field, meta, helpers] = useField('group_by');
const fixedOptionLabels = fixedOptions && Object.values(fixedOptions);
const selections = fixedOptions
? stringToArray(field.value).map(o => fixedOptions[o])
: stringToArray(field.value);
const [options, setOptions] = useState(selections);
const [isOpen, setIsOpen] = useState(false);
const renderOptions = opts => {
return opts.map(option => (
<SelectOption key={option} value={option}>
{option}
</SelectOption>
));
};
const handleFilter = event => {
const str = event.target.value.toLowerCase();
let matches;
if (fixedOptions) {
matches = fixedOptionLabels.filter(o => o.toLowerCase().includes(str));
} else {
matches = options.filter(o => o.toLowerCase().includes(str));
}
return renderOptions(matches);
};
const handleSelect = (e, option) => {
let selectedValues;
if (selections.includes(option)) {
selectedValues = selections.filter(o => o !== option);
} else {
selectedValues = selections.concat(option);
}
if (fixedOptions) {
selectedValues = selectedValues.map(val =>
Object.keys(fixedOptions).find(key => fixedOptions[key] === val)
);
}
helpers.setValue(arrayToString(selectedValues));
};
return (
<FormGroup
fieldId="group-by"
helperTextInvalid={meta.error}
validated="default"
label={i18n._(t`Only group by`)}
labelIcon={
<FieldTooltip
content={
<Trans>
Select which groups to create automatically. AWX will create
group names similar to the following examples based on the
options selected:
<br />
<br />
<ul>
<li>
Availability Zone: <strong>zones &raquo; us-east-1b</strong>
</li>
<li>
Image ID: <strong>images &raquo; ami-b007ab1e</strong>
</li>
<li>
Instance ID: <strong>instances &raquo; i-ca11ab1e </strong>
</li>
<li>
Instance Type: <strong>types &raquo; type_m1_medium</strong>
</li>
<li>
Key Name: <strong>keys &raquo; key_testing</strong>
</li>
<li>
Region: <strong>regions &raquo; us-east-1</strong>
</li>
<li>
Security Group:{' '}
<strong>
security_groups &raquo; security_group_default
</strong>
</li>
<li>
Tags: <strong>tags &raquo; tag_Name_host1</strong>
</li>
<li>
VPC ID: <strong>vpcs &raquo; vpc-5ca1ab1e</strong>
</li>
<li>
Tag None: <strong>tags &raquo; tag_none</strong>
</li>
</ul>
<br />
If blank, all groups above are created except{' '}
<em>Instance ID</em>.
</Trans>
}
/>
}
>
<Select
variant={SelectVariant.typeaheadMulti}
id="group-by"
onToggle={setIsOpen}
onClear={() => helpers.setValue('')}
isCreatable={isCreatable}
createText={i18n._(t`Create`)}
onCreateOption={name => {
name = name.trim();
if (!options.find(opt => opt === name)) {
setOptions(options.concat(name));
}
return name;
}}
onFilter={handleFilter}
onSelect={handleSelect}
isExpanded={isOpen}
placeholderText={i18n._(t`Select a group`)}
selections={selections}
>
{fixedOptions
? renderOptions(fixedOptionLabels)
: renderOptions(options)}
</Select>
</FormGroup>
);
}
);
export const VerbosityField = withI18n()(({ i18n }) => {
const [field, meta, helpers] = useField('verbosity');
const isValid = !(meta.touched && meta.error);
@ -351,49 +154,3 @@ export const OptionsField = withI18n()(
);
}
);
export const InstanceFiltersField = withI18n()(({ i18n }) => {
// Setting BrandName to a variable here is necessary to get the jest tests
// passing. Attempting to use BrandName in the template literal results
// in failing tests.
const brandName = BrandName;
return (
<FormField
id="instance-filters"
label={i18n._(t`Instance filters`)}
name="instance_filters"
type="text"
tooltip={
<Trans>
Provide a comma-separated list of filter expressions. Hosts are
imported to {brandName} when <em>ANY</em> of the filters match.
<br />
<br />
Limit to hosts having a tag:
<br />
tag-key=TowerManaged
<br />
<br />
Limit to hosts using either key pair:
<br />
key-name=staging, key-name=production
<br />
<br />
Limit to hosts where the Name tag begins with <em>test</em>:<br />
tag:Name=test*
<br />
<br />
View the
<a
href="http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeInstances.html\"
target="_blank\"
>
{' '}
Describe Instances documentation{' '}
</a>
for a complete list of supported filters.
</Trans>
}
/>
);
});

View File

@ -3,11 +3,7 @@ import { useField } from 'formik';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
import {
InstanceFiltersField,
OptionsField,
VerbosityField,
} from './SharedFields';
import { OptionsField, VerbosityField } from './SharedFields';
const TowerSubForm = ({ i18n }) => {
const [credentialField, credentialMeta, credentialHelpers] = useField(
@ -28,7 +24,6 @@ const TowerSubForm = ({ i18n }) => {
value={credentialField.value}
required
/>
<InstanceFiltersField />
<VerbosityField />
<OptionsField />
</>

View File

@ -10,13 +10,10 @@ jest.mock('../../../../api/models/Credentials');
const initialValues = {
credential: null,
custom_virtualenv: '',
group_by: '',
instance_filters: '',
overwrite: false,
overwrite_vars: false,
source_path: '',
source_project: null,
source_regions: '',
source_script: null,
source_vars: '---\n',
update_cache_timeout: 0,
@ -48,7 +45,6 @@ describe('<TowerSubForm />', () => {
test('should render subform fields', () => {
expect(wrapper.find('FormGroup[label="Credential"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Instance filters"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Verbosity"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Update options"]')).toHaveLength(1);
expect(

View File

@ -3,13 +3,7 @@ import { useField } from 'formik';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
import {
InstanceFiltersField,
GroupByField,
OptionsField,
SourceVarsField,
VerbosityField,
} from './SharedFields';
import { OptionsField, SourceVarsField, VerbosityField } from './SharedFields';
const VMwareSubForm = ({ i18n }) => {
const [credentialField, credentialMeta, credentialHelpers] = useField(
@ -30,8 +24,6 @@ const VMwareSubForm = ({ i18n }) => {
value={credentialField.value}
required
/>
<InstanceFiltersField />
<GroupByField isCreatable />
<VerbosityField />
<OptionsField />
<SourceVarsField />

View File

@ -10,13 +10,10 @@ jest.mock('../../../../api/models/Credentials');
const initialValues = {
credential: null,
custom_virtualenv: '',
group_by: '',
instance_filters: '',
overwrite: false,
overwrite_vars: false,
source_path: '',
source_project: null,
source_regions: '',
source_script: null,
source_vars: '---\n',
update_cache_timeout: 0,
@ -27,11 +24,7 @@ const initialValues = {
const mockSourceOptions = {
actions: {
POST: {
source_regions: {
gce_region_choices: [],
},
},
POST: {},
},
};
@ -58,8 +51,6 @@ describe('<VMwareSubForm />', () => {
test('should render subform fields', () => {
expect(wrapper.find('FormGroup[label="Credential"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Instance filters"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Only group by"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Verbosity"]')).toHaveLength(1);
expect(wrapper.find('FormGroup[label="Update options"]')).toHaveLength(1);
expect(

View File

@ -10,13 +10,10 @@ jest.mock('../../../../api/models/Credentials');
const initialValues = {
credential: null,
custom_virtualenv: '',
group_by: '',
instance_filters: '',
overwrite: false,
overwrite_vars: false,
source_path: '',
source_project: null,
source_regions: '',
source_script: null,
source_vars: '---\n',
update_cache_timeout: 0,

View File

@ -96,9 +96,6 @@
"source_script": "Mock Script",
"source_vars":"---\nfoo: bar",
"credential": 8,
"source_regions": "us-east-1,us-east-2",
"instance_filters": "filter1,filter2,filter3",
"group_by": "group1,group2,group3",
"overwrite":true,
"overwrite_vars":true,
"custom_virtualenv":"/venv/custom",