Merge pull request #13873 from marshmalien/10799-bug-prompt-launch-credential-type-dropdown-complete

Fix screen crash when changing credential type in launch prompt dropdown
This commit is contained in:
Sarah Akus
2023-04-28 13:25:40 -04:00
committed by GitHub
7 changed files with 85 additions and 14 deletions

View File

@@ -43,6 +43,7 @@ function LaunchButton({ resource, children }) {
const [surveyConfig, setSurveyConfig] = useState(null); const [surveyConfig, setSurveyConfig] = useState(null);
const [labels, setLabels] = useState([]); const [labels, setLabels] = useState([]);
const [isLaunching, setIsLaunching] = useState(false); const [isLaunching, setIsLaunching] = useState(false);
const [resourceCredentials, setResourceCredentials] = useState([]);
const [error, setError] = useState(null); const [error, setError] = useState(null);
const handleLaunch = async () => { const handleLaunch = async () => {
@@ -83,6 +84,13 @@ function LaunchButton({ resource, children }) {
setLabels(allLabels); setLabels(allLabels);
} }
if (launch.ask_credential_on_launch) {
const {
data: { results: templateCredentials },
} = await JobTemplatesAPI.readCredentials(resource.id);
setResourceCredentials(templateCredentials);
}
if (canLaunchWithoutPrompt(launch)) { if (canLaunchWithoutPrompt(launch)) {
await launchWithParams({}); await launchWithParams({});
} else { } else {
@@ -208,6 +216,7 @@ function LaunchButton({ resource, children }) {
labels={labels} labels={labels}
onLaunch={launchWithParams} onLaunch={launchWithParams}
onCancel={() => setShowLaunchPrompt(false)} onCancel={() => setShowLaunchPrompt(false)}
resourceDefaultCredentials={resourceCredentials}
/> />
)} )}
</> </>

View File

@@ -47,6 +47,12 @@ describe('LaunchButton', () => {
variables_needed_to_start: [], variables_needed_to_start: [],
}, },
}); });
JobTemplatesAPI.readCredentials.mockResolvedValue({
data: {
count: 0,
results: [],
},
});
}); });
afterEach(() => jest.clearAllMocks()); afterEach(() => jest.clearAllMocks());

View File

@@ -19,6 +19,7 @@ function PromptModalForm({
labels, labels,
surveyConfig, surveyConfig,
instanceGroups, instanceGroups,
resourceDefaultCredentials,
}) { }) {
const { setFieldTouched, values } = useFormikContext(); const { setFieldTouched, values } = useFormikContext();
const [showDescription, setShowDescription] = useState(false); const [showDescription, setShowDescription] = useState(false);
@@ -35,9 +36,9 @@ function PromptModalForm({
surveyConfig, surveyConfig,
resource, resource,
labels, labels,
instanceGroups instanceGroups,
resourceDefaultCredentials
); );
const handleSubmit = async () => { const handleSubmit = async () => {
const postValues = {}; const postValues = {};
const setValue = (key, value) => { const setValue = (key, value) => {

View File

@@ -69,6 +69,20 @@ describe('LaunchPrompt', () => {
spec: [{ type: 'text', variable: 'foo' }], spec: [{ type: 'text', variable: 'foo' }],
}, },
}); });
JobTemplatesAPI.readCredentials.mockResolvedValue({
data: {
results: [
{
id: 5,
name: 'cred that prompts',
credential_type: 1,
inputs: {
password: 'ASK',
},
},
],
},
});
InstanceGroupsAPI.read.mockResolvedValue({ InstanceGroupsAPI.read.mockResolvedValue({
data: { data: {
results: [ results: [
@@ -212,6 +226,16 @@ describe('LaunchPrompt', () => {
], ],
}, },
}} }}
resourceDefaultCredentials={[
{
id: 5,
name: 'cred that prompts',
credential_type: 1,
inputs: {
password: 'ASK',
},
},
]}
onLaunch={noop} onLaunch={noop}
onCancel={noop} onCancel={noop}
surveyConfig={{ surveyConfig={{
@@ -289,6 +313,16 @@ describe('LaunchPrompt', () => {
resource={resource} resource={resource}
onLaunch={noop} onLaunch={noop}
onCancel={noop} onCancel={noop}
resourceDefaultCredentials={[
{
id: 5,
name: 'cred that prompts',
credential_type: 1,
inputs: {
password: 'ASK',
},
},
]}
/> />
); );
}); });

View File

@@ -1,6 +1,6 @@
import 'styled-components/macro'; import 'styled-components/macro';
import React, { useState, useCallback, useEffect } from 'react'; import React, { useState, useCallback, useEffect } from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory, useLocation } from 'react-router-dom';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { useField } from 'formik'; import { useField } from 'formik';
@@ -8,7 +8,7 @@ import styled from 'styled-components';
import { Alert, ToolbarItem } from '@patternfly/react-core'; import { Alert, ToolbarItem } from '@patternfly/react-core';
import { CredentialsAPI, CredentialTypesAPI } from 'api'; import { CredentialsAPI, CredentialTypesAPI } from 'api';
import { getSearchableKeys } from 'components/PaginatedTable'; import { getSearchableKeys } from 'components/PaginatedTable';
import { getQSConfig, parseQueryString } from 'util/qs'; import { getQSConfig, parseQueryString, updateQueryString } from 'util/qs';
import useRequest from 'hooks/useRequest'; import useRequest from 'hooks/useRequest';
import AnsibleSelect from '../../AnsibleSelect'; import AnsibleSelect from '../../AnsibleSelect';
import OptionsList from '../../OptionsList'; import OptionsList from '../../OptionsList';
@@ -31,18 +31,18 @@ function CredentialsStep({
allowCredentialsWithPasswords, allowCredentialsWithPasswords,
defaultCredentials = [], defaultCredentials = [],
}) { }) {
const history = useHistory();
const location = useLocation();
const [field, meta, helpers] = useField({ const [field, meta, helpers] = useField({
name: 'credentials', name: 'credentials',
validate: (val) => validate: (val) =>
credentialsValidator( credentialsValidator(
allowCredentialsWithPasswords, allowCredentialsWithPasswords,
val, val,
defaultCredentials defaultCredentials ?? []
), ),
}); });
const [selectedType, setSelectedType] = useState(null); const [selectedType, setSelectedType] = useState(null);
const history = useHistory();
const { const {
result: types, result: types,
error: typesError, error: typesError,
@@ -104,12 +104,32 @@ function CredentialsStep({
credentialsValidator( credentialsValidator(
allowCredentialsWithPasswords, allowCredentialsWithPasswords,
field.value, field.value,
defaultCredentials defaultCredentials ?? []
) )
); );
/* eslint-disable-next-line react-hooks/exhaustive-deps */ /* eslint-disable-next-line react-hooks/exhaustive-deps */
}, []); }, []);
const removeAllSearchTerms = (qsConfig) => {
const oldParams = parseQueryString(qsConfig, location.search);
Object.keys(oldParams).forEach((key) => {
oldParams[key] = null;
});
const defaultParams = {
...oldParams,
page: 1,
page_size: 5,
order_by: 'name',
};
const qs = updateQueryString(qsConfig, location.search, defaultParams);
pushHistoryState(qs);
};
const pushHistoryState = (qs) => {
const { pathname } = history.location;
history.push(qs ? `${pathname}?${qs}` : pathname);
};
if (isTypesLoading) { if (isTypesLoading) {
return <ContentLoading />; return <ContentLoading />;
} }
@@ -154,9 +174,7 @@ function CredentialsStep({
value={selectedType && selectedType.id} value={selectedType && selectedType.id}
onChange={(e, id) => { onChange={(e, id) => {
// Reset query params when the category of credentials is changed // Reset query params when the category of credentials is changed
history.replace({ removeAllSearchTerms(QS_CONFIG);
search: '',
});
setSelectedType(types.find((o) => o.id === parseInt(id, 10))); setSelectedType(types.find((o) => o.id === parseInt(id, 10)));
}} }}
/> />

View File

@@ -168,7 +168,9 @@ describe('CredentialsStep', () => {
test('should reset query params (credential.page) when selected credential type is changed', async () => { test('should reset query params (credential.page) when selected credential type is changed', async () => {
let wrapper; let wrapper;
const history = createMemoryHistory({ const history = createMemoryHistory({
initialEntries: ['?credential.page=2'], initialEntries: [
'?credential.page=2&credential.page_size=5&credential.order_by=name',
],
}); });
await act(async () => { await act(async () => {
wrapper = mountWithContexts( wrapper = mountWithContexts(

View File

@@ -46,7 +46,8 @@ export default function useLaunchSteps(
surveyConfig, surveyConfig,
resource, resource,
labels, labels,
instanceGroups instanceGroups,
resourceDefaultCredentials
) { ) {
const [visited, setVisited] = useState({}); const [visited, setVisited] = useState({});
const [isReady, setIsReady] = useState(false); const [isReady, setIsReady] = useState(false);
@@ -56,7 +57,7 @@ export default function useLaunchSteps(
useCredentialsStep( useCredentialsStep(
launchConfig, launchConfig,
resource, resource,
resource.summary_fields.credentials || [], resourceDefaultCredentials,
true true
), ),
useCredentialPasswordsStep( useCredentialPasswordsStep(