mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 10:00:01 -03:30
Merge pull request #9188 from nixocio/ui_issue_8707
Allow user to remove organization from credentials Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
commit
efbbbc0bdc
@ -29,6 +29,7 @@ function OrganizationLookup({
|
||||
value,
|
||||
history,
|
||||
autoPopulate,
|
||||
isDisabled,
|
||||
}) {
|
||||
const autoPopulateLookup = useAutoPopulateLookup(onChange);
|
||||
|
||||
@ -80,6 +81,7 @@ function OrganizationLookup({
|
||||
label={i18n._(t`Organization`)}
|
||||
>
|
||||
<Lookup
|
||||
isDisabled={isDisabled}
|
||||
id="organization"
|
||||
header={i18n._(t`Organization`)}
|
||||
value={value}
|
||||
@ -139,6 +141,7 @@ OrganizationLookup.propTypes = {
|
||||
required: bool,
|
||||
value: Organization,
|
||||
autoPopulate: bool,
|
||||
isDisabled: bool,
|
||||
};
|
||||
|
||||
OrganizationLookup.defaultProps = {
|
||||
@ -148,6 +151,7 @@ OrganizationLookup.defaultProps = {
|
||||
required: false,
|
||||
value: null,
|
||||
autoPopulate: false,
|
||||
isDisabled: false,
|
||||
};
|
||||
|
||||
export { OrganizationLookup as _OrganizationLookup };
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { object } from 'prop-types';
|
||||
import { CardBody } from '../../../components/Card';
|
||||
@ -6,15 +6,20 @@ import {
|
||||
CredentialsAPI,
|
||||
CredentialInputSourcesAPI,
|
||||
CredentialTypesAPI,
|
||||
OrganizationsAPI,
|
||||
UsersAPI,
|
||||
} from '../../../api';
|
||||
import ContentError from '../../../components/ContentError';
|
||||
import ContentLoading from '../../../components/ContentLoading';
|
||||
import CredentialForm from '../shared/CredentialForm';
|
||||
import useRequest from '../../../util/useRequest';
|
||||
import { useConfig } from '../../../contexts/Config';
|
||||
|
||||
function CredentialEdit({ credential, me }) {
|
||||
function CredentialEdit({ credential }) {
|
||||
const history = useHistory();
|
||||
const { id: credId } = useParams();
|
||||
const { me = {} } = useConfig();
|
||||
const [isOrgLookupDisabled, setIsOrgLookupDisabled] = useState(false);
|
||||
|
||||
const { error: submitError, request: submitRequest, result } = useRequest(
|
||||
useCallback(
|
||||
@ -81,8 +86,11 @@ function CredentialEdit({ credential, me }) {
|
||||
// can send only one of org, user, team
|
||||
if (organization?.id) {
|
||||
modifiedData.organization = organization.id;
|
||||
} else if (me?.id) {
|
||||
modifiedData.user = me.id;
|
||||
} else {
|
||||
modifiedData.organization = null;
|
||||
if (me?.id) {
|
||||
modifiedData.user = me.id;
|
||||
}
|
||||
}
|
||||
const [{ data }] = await Promise.all([
|
||||
CredentialsAPI.update(credId, modifiedData),
|
||||
@ -114,10 +122,22 @@ function CredentialEdit({ credential, me }) {
|
||||
{
|
||||
data: { results },
|
||||
},
|
||||
{
|
||||
data: { count: adminOrgCount },
|
||||
},
|
||||
{
|
||||
data: { count: credentialAdminCount },
|
||||
},
|
||||
] = await Promise.all([
|
||||
CredentialTypesAPI.read({ page_size: 200 }),
|
||||
CredentialsAPI.readInputSources(credId),
|
||||
UsersAPI.readAdminOfOrganizations(me.id),
|
||||
OrganizationsAPI.read({
|
||||
page_size: 1,
|
||||
role_level: 'credential_admin_role',
|
||||
}),
|
||||
]);
|
||||
setIsOrgLookupDisabled(!(adminOrgCount || credentialAdminCount));
|
||||
const credTypes = data.results;
|
||||
if (data.next && data.next.includes('page=2')) {
|
||||
const {
|
||||
@ -137,7 +157,7 @@ function CredentialEdit({ credential, me }) {
|
||||
return inputSourcesMap;
|
||||
}, {});
|
||||
return { credentialTypes: creds, loadedInputSources: inputSources };
|
||||
}, [credId]),
|
||||
}, [credId, me.id]),
|
||||
{ credentialTypes: {}, loadedInputSources: {} }
|
||||
);
|
||||
|
||||
@ -171,6 +191,7 @@ function CredentialEdit({ credential, me }) {
|
||||
credentialTypes={credentialTypes}
|
||||
inputSources={loadedInputSources}
|
||||
submitError={submitError}
|
||||
isOrgLookupDisabled={isOrgLookupDisabled}
|
||||
/>
|
||||
</CardBody>
|
||||
);
|
||||
|
||||
@ -10,6 +10,8 @@ import {
|
||||
CredentialsAPI,
|
||||
CredentialInputSourcesAPI,
|
||||
CredentialTypesAPI,
|
||||
OrganizationsAPI,
|
||||
UsersAPI,
|
||||
} from '../../../api';
|
||||
import CredentialEdit from './CredentialEdit';
|
||||
|
||||
@ -114,6 +116,25 @@ const mockCredential = {
|
||||
kubernetes: false,
|
||||
};
|
||||
|
||||
UsersAPI.readAdminOfOrganizations.mockResolvedValue({
|
||||
data: {
|
||||
count: 1,
|
||||
results: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'org',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
OrganizationsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
results: [{ id: 1 }],
|
||||
count: 1,
|
||||
},
|
||||
});
|
||||
|
||||
CredentialTypesAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
results: [
|
||||
@ -310,7 +331,12 @@ describe('<CredentialEdit />', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<CredentialEdit credential={mockCredential} />,
|
||||
{
|
||||
context: { router: { history } },
|
||||
context: {
|
||||
router: { history },
|
||||
me: {
|
||||
id: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
@ -377,6 +403,7 @@ describe('<CredentialEdit />', () => {
|
||||
expect(CredentialsAPI.update).toHaveBeenCalledWith(3, {
|
||||
user: 1,
|
||||
name: 'foo',
|
||||
organization: null,
|
||||
description: 'bar',
|
||||
credential_type: '1',
|
||||
inputs: {
|
||||
@ -439,6 +466,7 @@ describe('<CredentialEdit />', () => {
|
||||
).toBe('$encrypted$');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Initial GET request fails', () => {
|
||||
test('shows error when initial GET request fails', async () => {
|
||||
CredentialTypesAPI.read.mockRejectedValue(new Error());
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { func, shape } from 'prop-types';
|
||||
import { Formik, useField, useFormikContext } from 'formik';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { arrayOf, func, object, shape } from 'prop-types';
|
||||
import {
|
||||
ActionGroup,
|
||||
Button,
|
||||
@ -136,6 +136,7 @@ function CredentialFormFields({ i18n, credentialTypes }) {
|
||||
touched={orgMeta.touched}
|
||||
error={orgMeta.error}
|
||||
required={isGalaxyCredential}
|
||||
isDisabled={initialValues.isOrgLookupDisabled}
|
||||
/>
|
||||
<FormGroup
|
||||
fieldId="credential-Type"
|
||||
@ -189,6 +190,7 @@ function CredentialForm({
|
||||
onSubmit,
|
||||
onCancel,
|
||||
submitError,
|
||||
isOrgLookupDisabled,
|
||||
...rest
|
||||
}) {
|
||||
const [showExternalTestModal, setShowExternalTestModal] = useState(false);
|
||||
@ -199,6 +201,7 @@ function CredentialForm({
|
||||
credential_type: credential?.credential_type || '',
|
||||
inputs: credential?.inputs || {},
|
||||
passwordPrompts: {},
|
||||
isOrgLookupDisabled: isOrgLookupDisabled || false,
|
||||
};
|
||||
|
||||
Object.values(credentialTypes).forEach(credentialType => {
|
||||
@ -311,18 +314,18 @@ function CredentialForm({
|
||||
);
|
||||
}
|
||||
|
||||
CredentialForm.proptype = {
|
||||
CredentialForm.propTypes = {
|
||||
handleSubmit: func.isRequired,
|
||||
handleCancel: func.isRequired,
|
||||
credentialTypes: shape({}).isRequired,
|
||||
credential: shape({}),
|
||||
inputSources: arrayOf(object),
|
||||
inputSources: shape({}),
|
||||
submitError: shape({}),
|
||||
};
|
||||
|
||||
CredentialForm.defaultProps = {
|
||||
credential: {},
|
||||
inputSources: [],
|
||||
inputSources: {},
|
||||
submitError: null,
|
||||
};
|
||||
|
||||
|
||||
@ -327,6 +327,27 @@ describe('<CredentialForm />', () => {
|
||||
machineFieldExpects();
|
||||
});
|
||||
|
||||
test('organization lookup should be disabled', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<CredentialForm
|
||||
onCancel={onCancel}
|
||||
onSubmit={onSubmit}
|
||||
credential={machineCredential}
|
||||
credentialTypes={credentialTypes}
|
||||
isOrgLookupDisabled
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find('CredentialFormFields')
|
||||
.find('OrganizationLookup')
|
||||
.prop('isDisabled')
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('should display form fields for source control credential properly', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
|
||||
@ -168,7 +168,7 @@ function ExternalTestModal({
|
||||
);
|
||||
}
|
||||
|
||||
ExternalTestModal.proptype = {
|
||||
ExternalTestModal.propType = {
|
||||
credential: shape({}),
|
||||
credentialType: shape({}).isRequired,
|
||||
credentialFormValues: shape({}).isRequired,
|
||||
|
||||
@ -118,7 +118,7 @@ function InventoryEdit({ inventory }) {
|
||||
);
|
||||
}
|
||||
|
||||
InventoryEdit.proptype = {
|
||||
InventoryEdit.propType = {
|
||||
inventory: object.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@ -138,7 +138,7 @@ function InventoryForm({
|
||||
);
|
||||
}
|
||||
|
||||
InventoryForm.proptype = {
|
||||
InventoryForm.propType = {
|
||||
handleSubmit: func.isRequired,
|
||||
handleCancel: func.isRequired,
|
||||
instanceGroups: shape(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user