mirror of
https://github.com/ansible/awx.git
synced 2026-01-20 06:01:25 -03:30
adds advanced search functionality and lists correct EEs
This commit is contained in:
parent
e6bde23aea
commit
1d442452b0
@ -35,22 +35,29 @@ function AdHocCommands({ adHocItems, i18n, hasListItems, onLaunchLoading }) {
|
||||
}, [isKebabified, isWizardOpen, onKebabModalChange]);
|
||||
|
||||
const {
|
||||
result: { moduleOptions, credentialTypeId, isAdHocDisabled },
|
||||
result: {
|
||||
moduleOptions,
|
||||
credentialTypeId,
|
||||
isAdHocDisabled,
|
||||
organizationId,
|
||||
},
|
||||
request: fetchData,
|
||||
error: fetchError,
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
const [options, cred] = await Promise.all([
|
||||
const [options, { data }, cred] = await Promise.all([
|
||||
InventoriesAPI.readAdHocOptions(id),
|
||||
InventoriesAPI.readDetail(id),
|
||||
CredentialTypesAPI.read({ namespace: 'ssh' }),
|
||||
]);
|
||||
return {
|
||||
moduleOptions: options.data.actions.GET.module_name.choices,
|
||||
credentialTypeId: cred.data.results[0].id,
|
||||
isAdHocDisabled: !options.data.actions.POST,
|
||||
organizationId: data.organization,
|
||||
};
|
||||
}, [id]),
|
||||
{ moduleOptions: [], isAdHocDisabled: true }
|
||||
{ moduleOptions: [], isAdHocDisabled: true, organizationId: null }
|
||||
);
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
@ -141,6 +148,7 @@ function AdHocCommands({ adHocItems, i18n, hasListItems, onLaunchLoading }) {
|
||||
{isWizardOpen && (
|
||||
<AdHocCommandsWizard
|
||||
adHocItems={adHocItems}
|
||||
organizationId={organizationId}
|
||||
moduleOptions={moduleOptions}
|
||||
verbosityOptions={verbosityOptions}
|
||||
credentialTypeId={credentialTypeId}
|
||||
|
||||
@ -103,6 +103,7 @@ describe('<AdHocCommands />', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
InventoriesAPI.readDetail.mockResolvedValue({ data: { organization: 1 } });
|
||||
CredentialTypesAPI.read.mockResolvedValue({
|
||||
data: { results: [{ id: 1 }] },
|
||||
});
|
||||
@ -135,6 +136,10 @@ describe('<AdHocCommands />', () => {
|
||||
|
||||
test('should submit properly', async () => {
|
||||
InventoriesAPI.launchAdHocCommands.mockResolvedValue({ data: { id: 1 } });
|
||||
InventoriesAPI.readDetail.mockResolvedValue({
|
||||
data: { organization: 1 },
|
||||
});
|
||||
|
||||
CredentialsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
results: credentials,
|
||||
@ -150,6 +155,9 @@ describe('<AdHocCommands />', () => {
|
||||
count: 2,
|
||||
},
|
||||
});
|
||||
ExecutionEnvironmentsAPI.readOptions.mockResolvedValue({
|
||||
data: { actions: { GET: {} } },
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<AdHocCommands
|
||||
@ -275,6 +283,9 @@ describe('<AdHocCommands />', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
InventoriesAPI.readDetail.mockResolvedValue({
|
||||
data: { organization: 1 },
|
||||
});
|
||||
CredentialTypesAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
results: [
|
||||
@ -307,6 +318,9 @@ describe('<AdHocCommands />', () => {
|
||||
count: 2,
|
||||
},
|
||||
});
|
||||
ExecutionEnvironmentsAPI.readOptions.mockResolvedValue({
|
||||
data: { actions: { GET: {} } },
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<AdHocCommands
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { withI18n } from '@lingui/react';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
import { ExclamationCircleIcon as PFExclamationCircleIcon } from '@patternfly/react-icons';
|
||||
import { Tooltip } from '@patternfly/react-core';
|
||||
@ -24,11 +24,11 @@ const ExclamationCircleIcon = styled(PFExclamationCircleIcon)`
|
||||
|
||||
function AdHocCommandsWizard({
|
||||
onLaunch,
|
||||
i18n,
|
||||
moduleOptions,
|
||||
verbosityOptions,
|
||||
onCloseWizard,
|
||||
credentialTypeId,
|
||||
organizationId,
|
||||
}) {
|
||||
const [currentStepId, setCurrentStepId] = useState(1);
|
||||
const [enableLaunch, setEnableLaunch] = useState(false);
|
||||
@ -58,17 +58,17 @@ function AdHocCommandsWizard({
|
||||
key: 1,
|
||||
name: hasDetailsStepError ? (
|
||||
<AlertText>
|
||||
{i18n._(t`Details`)}
|
||||
{t`Details`}
|
||||
<Tooltip
|
||||
position="right"
|
||||
content={i18n._(t`This step contains errors`)}
|
||||
content={t`This step contains errors`}
|
||||
trigger="click mouseenter focus"
|
||||
>
|
||||
<ExclamationCircleIcon />
|
||||
</Tooltip>
|
||||
</AlertText>
|
||||
) : (
|
||||
i18n._(t`Details`)
|
||||
t`Details`
|
||||
),
|
||||
component: (
|
||||
<AdHocDetailsStep
|
||||
@ -77,20 +77,25 @@ function AdHocCommandsWizard({
|
||||
/>
|
||||
),
|
||||
enableNext: enabledNextOnDetailsStep(),
|
||||
nextButtonText: i18n._(t`Next`),
|
||||
nextButtonText: t`Next`,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
key: 2,
|
||||
name: t`Execution Environment`,
|
||||
component: <AdHocExecutionEnvironmentStep />,
|
||||
component: (
|
||||
<AdHocExecutionEnvironmentStep organizationId={organizationId} />
|
||||
),
|
||||
// Removed this line when https://github.com/patternfly/patternfly-react/issues/5729 is fixed
|
||||
stepNavItemProps: { style: { 'white-space': 'nowrap' } },
|
||||
enableNext: true,
|
||||
nextButtonText: t`Next`,
|
||||
canJumpTo: currentStepId >= 2,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
key: 3,
|
||||
name: i18n._(t`Machine credential`),
|
||||
name: t`Machine credential`,
|
||||
component: (
|
||||
<AdHocCredentialStep
|
||||
credentialTypeId={credentialTypeId}
|
||||
@ -98,7 +103,7 @@ function AdHocCommandsWizard({
|
||||
/>
|
||||
),
|
||||
enableNext: enableLaunch && Object.values(errors).length === 0,
|
||||
nextButtonText: i18n._(t`Launch`),
|
||||
nextButtonText: t`Launch`,
|
||||
canJumpTo: currentStepId >= 2,
|
||||
},
|
||||
];
|
||||
@ -115,10 +120,10 @@ function AdHocCommandsWizard({
|
||||
onLaunch(values);
|
||||
}}
|
||||
steps={steps}
|
||||
title={i18n._(t`Run command`)}
|
||||
title={t`Run command`}
|
||||
nextButtonText={currentStep.nextButtonText || undefined}
|
||||
backButtonText={i18n._(t`Back`)}
|
||||
cancelButtonText={i18n._(t`Cancel`)}
|
||||
backButtonText={t`Back`}
|
||||
cancelButtonText={t`Cancel`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -149,4 +154,4 @@ FormikApp.propTypes = {
|
||||
onCloseWizard: PropTypes.func.isRequired,
|
||||
credentialTypeId: PropTypes.number.isRequired,
|
||||
};
|
||||
export default withI18n()(FormikApp);
|
||||
export default FormikApp;
|
||||
|
||||
@ -41,6 +41,7 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
verbosityOptions={verbosityOptions}
|
||||
onCloseWizard={() => {}}
|
||||
credentialTypeId={1}
|
||||
organizationId={1}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@ -108,6 +109,9 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
count: 2,
|
||||
},
|
||||
});
|
||||
ExecutionEnvironmentsAPI.readOptions.mockResolvedValue({
|
||||
data: { actions: { GET: {} } },
|
||||
});
|
||||
CredentialsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
results: [
|
||||
|
||||
@ -22,10 +22,13 @@ describe('<AdHocExecutionEnvironmentStep />', () => {
|
||||
count: 2,
|
||||
},
|
||||
});
|
||||
ExecutionEnvironmentsAPI.readOptions.mockResolvedValue({
|
||||
data: { actions: { GET: {} } },
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<Formik>
|
||||
<AdHocExecutionEnvironmentStep />
|
||||
<AdHocExecutionEnvironmentStep organizationId={1} />
|
||||
</Formik>
|
||||
);
|
||||
});
|
||||
@ -6,18 +6,18 @@ import { Form, FormGroup } from '@patternfly/react-core';
|
||||
import { ExecutionEnvironmentsAPI } from '../../api';
|
||||
import Popover from '../Popover';
|
||||
|
||||
import { parseQueryString, getQSConfig } from '../../util/qs';
|
||||
import { parseQueryString, getQSConfig, mergeParams } from '../../util/qs';
|
||||
import useRequest from '../../util/useRequest';
|
||||
import ContentError from '../ContentError';
|
||||
import ContentLoading from '../ContentLoading';
|
||||
import OptionsList from '../OptionsList';
|
||||
|
||||
const QS_CONFIG = getQSConfig('execution_environemts', {
|
||||
const QS_CONFIG = getQSConfig('execution_environments', {
|
||||
page: 1,
|
||||
page_size: 5,
|
||||
order_by: 'name',
|
||||
});
|
||||
function AdHocExecutionEnvironmentStep() {
|
||||
function AdHocExecutionEnvironmentStep({ organizationId }) {
|
||||
const history = useHistory();
|
||||
const [executionEnvironmentField, , executionEnvironmentHelpers] = useField(
|
||||
'execution_environment'
|
||||
@ -26,21 +26,51 @@ function AdHocExecutionEnvironmentStep() {
|
||||
error,
|
||||
isLoading,
|
||||
request: fetchExecutionEnvironments,
|
||||
result: { executionEnvironments, executionEnvironmentsCount },
|
||||
result: {
|
||||
executionEnvironments,
|
||||
executionEnvironmentsCount,
|
||||
relatedSearchableKeys,
|
||||
searchableKeys,
|
||||
},
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
const params = parseQueryString(QS_CONFIG, history.location.search);
|
||||
const globallyAvailableParams = { or__organization__isnull: 'True' };
|
||||
const organizationIdParams = organizationId
|
||||
? { or__organization__id: organizationId }
|
||||
: {};
|
||||
|
||||
const {
|
||||
data: { results, count },
|
||||
} = await ExecutionEnvironmentsAPI.read(params);
|
||||
|
||||
const [
|
||||
{
|
||||
data: { results, count },
|
||||
},
|
||||
actionsResponse,
|
||||
] = await Promise.all([
|
||||
ExecutionEnvironmentsAPI.read(
|
||||
mergeParams(params, {
|
||||
...globallyAvailableParams,
|
||||
...organizationIdParams,
|
||||
})
|
||||
),
|
||||
ExecutionEnvironmentsAPI.readOptions(),
|
||||
]);
|
||||
return {
|
||||
executionEnvironments: results,
|
||||
executionEnvironmentsCount: count,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
};
|
||||
}, [history.location.search]),
|
||||
{ executionEnvironments: [], executionEnvironmentsCount: 0 }
|
||||
}, [history.location.search, organizationId]),
|
||||
{
|
||||
executionEnvironments: [],
|
||||
executionEnvironmentsCount: 0,
|
||||
relatedSearchableKeys: [],
|
||||
searchableKeys: [],
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -62,11 +92,12 @@ function AdHocExecutionEnvironmentStep() {
|
||||
aria-label={t`Execution Environments`}
|
||||
labelIcon={
|
||||
<Popover
|
||||
content={t`Select the Execution Environment you want this command to run inside`}
|
||||
content={t`Select the Execution Environment you want this command to run inside.`}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<OptionsList
|
||||
isLoading={isLoading}
|
||||
value={executionEnvironmentField.value || []}
|
||||
options={executionEnvironments}
|
||||
optionCount={executionEnvironmentsCount}
|
||||
@ -75,7 +106,7 @@ function AdHocExecutionEnvironmentStep() {
|
||||
searchColumns={[
|
||||
{
|
||||
name: t`Name`,
|
||||
key: 'name',
|
||||
key: 'name__icontains',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
@ -94,6 +125,8 @@ function AdHocExecutionEnvironmentStep() {
|
||||
},
|
||||
]}
|
||||
name="execution_environment"
|
||||
searchableKeys={searchableKeys}
|
||||
relatedSearchableKeys={relatedSearchableKeys}
|
||||
selectItem={value => {
|
||||
executionEnvironmentHelpers.setValue([value]);
|
||||
}}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -92,11 +92,11 @@ describe('<InventoryDetail />', () => {
|
||||
expectDetailToMatch(wrapper, 'Type', 'Inventory');
|
||||
const org = wrapper.find('Detail[label="Organization"]');
|
||||
expect(org.prop('value')).toMatchInlineSnapshot(`
|
||||
<ForwardRef
|
||||
<Link
|
||||
to="/organizations/1/details"
|
||||
>
|
||||
The Organization
|
||||
</ForwardRef>
|
||||
</Link>
|
||||
`);
|
||||
const vars = wrapper.find('VariablesDetail');
|
||||
expect(vars).toHaveLength(1);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user