From 4a9d39c3faf6bc61e363958262db61fce55c02bc Mon Sep 17 00:00:00 2001 From: mabashian Date: Tue, 14 Apr 2020 09:55:30 -0400 Subject: [PATCH 1/4] Adds support for GCE credentials in credential form(s) --- .../src/components/FormField/FormField.jsx | 5 + .../CredentialAdd/CredentialAdd.jsx | 14 +- .../CredentialEdit/CredentialEdit.jsx | 4 +- .../Credential/shared/CredentialForm.jsx | 38 +- .../Credential/shared/CredentialForm.test.jsx | 489 +++++++++++++----- .../GoogleComputeEngineSubForm.jsx | 120 +++++ .../CredentialSubForms/SharedFields.jsx | 1 + .../SourceControlSubForm.jsx | 12 +- .../shared/CredentialSubForms/index.js | 3 + 9 files changed, 530 insertions(+), 156 deletions(-) create mode 100644 awx/ui_next/src/screens/Credential/shared/CredentialSubForms/GoogleComputeEngineSubForm.jsx diff --git a/awx/ui_next/src/components/FormField/FormField.jsx b/awx/ui_next/src/components/FormField/FormField.jsx index c94c370e47..096960ef39 100644 --- a/awx/ui_next/src/components/FormField/FormField.jsx +++ b/awx/ui_next/src/components/FormField/FormField.jsx @@ -7,6 +7,7 @@ import FieldTooltip from './FieldTooltip'; function FormField(props) { const { id, + helperText, name, label, tooltip, @@ -25,6 +26,7 @@ function FormField(props) { {(type === 'textarea' && ( {}, isRequired: false, diff --git a/awx/ui_next/src/screens/Credential/CredentialAdd/CredentialAdd.jsx b/awx/ui_next/src/screens/Credential/CredentialAdd/CredentialAdd.jsx index 092abd1e1c..e42b3faec7 100644 --- a/awx/ui_next/src/screens/Credential/CredentialAdd/CredentialAdd.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialAdd/CredentialAdd.jsx @@ -20,7 +20,9 @@ function CredentialAdd({ me }) { try { const { data: { results: loadedCredentialTypes }, - } = await CredentialTypesAPI.read({ or__kind: ['scm', 'ssh'] }); + } = await CredentialTypesAPI.read({ + or__namespace: ['gce', 'scm', 'ssh'], + }); setCredentialTypes(loadedCredentialTypes); } catch (err) { setError(err); @@ -65,7 +67,15 @@ function CredentialAdd({ me }) { ); } if (isLoading) { - return ; + return ( + + + + + + + + ); } return ( diff --git a/awx/ui_next/src/screens/Credential/CredentialEdit/CredentialEdit.jsx b/awx/ui_next/src/screens/Credential/CredentialEdit/CredentialEdit.jsx index 636431d3fd..71409638f6 100644 --- a/awx/ui_next/src/screens/Credential/CredentialEdit/CredentialEdit.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialEdit/CredentialEdit.jsx @@ -20,7 +20,9 @@ function CredentialEdit({ credential, me }) { try { const { data: { results: loadedCredentialTypes }, - } = await CredentialTypesAPI.read({ or__kind: ['scm', 'ssh'] }); + } = await CredentialTypesAPI.read({ + or__namespace: ['gce', 'scm', 'ssh'], + }); setCredentialTypes(loadedCredentialTypes); } catch (err) { setError(err); diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx index ef8dc686b5..466dd43bf3 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx +++ b/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx @@ -13,12 +13,17 @@ import { FormColumnLayout, SubFormLayout, } from '../../../components/FormLayout'; -import { ManualSubForm, SourceControlSubForm } from './CredentialSubForms'; +import { + GoogleComputeEngineSubForm, + ManualSubForm, + SourceControlSubForm, +} from './CredentialSubForms'; function CredentialFormFields({ i18n, credentialTypes, formik, + gceCredentialTypeId, initialValues, scmCredentialTypeId, sshCredentialTypeId, @@ -106,6 +111,7 @@ function CredentialFormFields({ {i18n._(t`Type Details`)} { { + [gceCredentialTypeId]: , [sshCredentialTypeId]: , [scmCredentialTypeId]: , }[formik.values.credential_type] @@ -130,22 +136,26 @@ function CredentialForm({ organization: credential?.summary_fields?.organization || null, credential_type: credential.credential_type || '', inputs: { - username: credential?.inputs?.username || '', - password: credential?.inputs?.password || '', - ssh_key_data: credential?.inputs?.ssh_key_data || '', - ssh_public_key_data: credential?.inputs?.ssh_public_key_data || '', - ssh_key_unlock: credential?.inputs?.ssh_key_unlock || '', become_method: credential?.inputs?.become_method || '', - become_username: credential?.inputs?.become_username || '', become_password: credential?.inputs?.become_password || '', + become_username: credential?.inputs?.become_username || '', + password: credential?.inputs?.password || '', + project: credential?.inputs?.project || '', + ssh_key_data: credential?.inputs?.ssh_key_data || '', + ssh_key_unlock: credential?.inputs?.ssh_key_unlock || '', + ssh_public_key_data: credential?.inputs?.ssh_public_key_data || '', + username: credential?.inputs?.username || '', }, }; const scmCredentialTypeId = Object.keys(credentialTypes) - .filter(key => credentialTypes[key].kind === 'scm') + .filter(key => credentialTypes[key].namespace === 'scm') .map(key => credentialTypes[key].id)[0]; const sshCredentialTypeId = Object.keys(credentialTypes) - .filter(key => credentialTypes[key].kind === 'ssh') + .filter(key => credentialTypes[key].namespace === 'ssh') + .map(key => credentialTypes[key].id)[0]; + const gceCredentialTypeId = Object.keys(credentialTypes) + .filter(key => credentialTypes[key].namespace === 'gce') .map(key => credentialTypes[key].id)[0]; return ( @@ -168,6 +178,7 @@ function CredentialForm({ 'become_username', 'become_password', ]; + const gceKeys = ['username', 'ssh_key_data', 'project']; if (parseInt(values.credential_type, 10) === scmCredentialTypeId) { Object.keys(values.inputs).forEach(key => { if (scmKeys.indexOf(key) < 0) { @@ -182,6 +193,14 @@ function CredentialForm({ delete values.inputs[key]; } }); + } else if ( + parseInt(values.credential_type, 10) === gceCredentialTypeId + ) { + Object.keys(values.inputs).forEach(key => { + if (gceKeys.indexOf(key) < 0) { + delete values.inputs[key]; + } + }); } onSubmit(values); }} @@ -193,6 +212,7 @@ function CredentialForm({ formik={formik} initialValues={initialValues} credentialTypes={credentialTypes} + gceCredentialTypeId={gceCredentialTypeId} scmCredentialTypeId={scmCredentialTypeId} sshCredentialTypeId={sshCredentialTypeId} {...rest} diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx index de441f954a..dd517529c2 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx +++ b/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx @@ -4,6 +4,8 @@ import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; import CredentialForm from './CredentialForm'; +jest.mock('@api'); + const machineCredential = { id: 3, type: 'credential', @@ -180,6 +182,104 @@ const sourceControlCredential = { kubernetes: false, }; +const gceCredential = { + id: 9, + type: 'credential', + url: '/api/v2/credentials/9/', + related: { + named_url: + '/api/v2/credentials/a gce cred++Google Compute Engine+cloud++Default/', + created_by: '/api/v2/users/1/', + modified_by: '/api/v2/users/1/', + organization: '/api/v2/organizations/4/', + activity_stream: '/api/v2/credentials/9/activity_stream/', + access_list: '/api/v2/credentials/9/access_list/', + object_roles: '/api/v2/credentials/9/object_roles/', + owner_users: '/api/v2/credentials/9/owner_users/', + owner_teams: '/api/v2/credentials/9/owner_teams/', + copy: '/api/v2/credentials/9/copy/', + input_sources: '/api/v2/credentials/9/input_sources/', + credential_type: '/api/v2/credential_types/10/', + }, + summary_fields: { + organization: { + id: 4, + name: 'Default', + description: '', + }, + credential_type: { + id: 10, + name: 'Google Compute Engine', + description: '', + }, + created_by: { + id: 1, + username: 'admin', + first_name: '', + last_name: '', + }, + modified_by: { + id: 1, + username: 'admin', + first_name: '', + last_name: '', + }, + object_roles: { + admin_role: { + description: 'Can manage all aspects of the credential', + name: 'Admin', + id: 287, + }, + use_role: { + description: 'Can use the credential in a job template', + name: 'Use', + id: 288, + }, + read_role: { + description: 'May view settings for the credential', + name: 'Read', + id: 289, + }, + }, + user_capabilities: { + edit: true, + delete: true, + copy: true, + use: true, + }, + owners: [ + { + id: 1, + type: 'user', + name: 'admin', + description: ' ', + url: '/api/v2/users/1/', + }, + { + id: 4, + type: 'organization', + name: 'Default', + description: '', + url: '/api/v2/organizations/4/', + }, + ], + }, + created: '2020-04-13T17:33:27.625773Z', + modified: '2020-04-13T17:33:27.625882Z', + name: 'a gce cred', + description: '', + organization: 4, + credential_type: 10, + inputs: { + project: 'test123', + username: 'test123.iam.gserviceaccount.com', + ssh_key_data: '$encrypted$', + }, + kind: 'gce', + cloud: true, + kubernetes: false, +}; + const credentialTypes = [ { id: 2, @@ -313,36 +413,71 @@ const credentialTypes = [ }, injectors: {}, }, + { + id: 10, + type: 'credential_type', + url: '/api/v2/credential_types/10/', + related: { + credentials: '/api/v2/credential_types/10/credentials/', + activity_stream: '/api/v2/credential_types/10/activity_stream/', + }, + summary_fields: { + user_capabilities: { + edit: false, + delete: false, + }, + }, + created: '2020-04-09T19:20:27.090665Z', + modified: '2020-04-09T19:21:11.575214Z', + name: 'Google Compute Engine', + description: '', + kind: 'cloud', + namespace: 'gce', + managed_by_tower: true, + inputs: { + fields: [ + { + id: 'username', + label: 'Service Account Email Address', + type: 'string', + help_text: + 'The email address assigned to the Google Compute Engine service account.', + }, + { + id: 'project', + label: 'Project', + type: 'string', + help_text: + 'The Project ID is the GCE assigned identification. It is often constructed as three words or two words followed by a three-digit number. Examples: project-id-000 and another-project-id', + }, + { + id: 'ssh_key_data', + label: 'RSA Private Key', + type: 'string', + format: 'ssh_private_key', + secret: true, + multiline: true, + help_text: + 'Paste the contents of the PEM file associated with the service account email.', + }, + ], + required: ['username', 'ssh_key_data'], + }, + injectors: {}, + }, ]; describe('', () => { let wrapper; - let onCancel; - let onSubmit; + const onCancel = jest.fn(); + const onSubmit = jest.fn(); const addFieldExpects = () => { + expect(wrapper.find('FormGroup').length).toBe(4); expect(wrapper.find('FormGroup[label="Name"]').length).toBe(1); expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1); expect(wrapper.find('FormGroup[label="Organization"]').length).toBe(1); expect(wrapper.find('FormGroup[label="Credential Type"]').length).toBe(1); - expect(wrapper.find('FormGroup[label="Username"]').length).toBe(0); - expect(wrapper.find('FormGroup[label="Password"]').length).toBe(0); - expect(wrapper.find('FormGroup[label="SSH Private Key"]').length).toBe(0); - expect( - wrapper.find('FormGroup[label="Signed SSH Certificate"]').length - ).toBe(0); - expect( - wrapper.find('FormGroup[label="Private Key Passphrase"]').length - ).toBe(0); - expect( - wrapper.find('FormGroup[label="Privelege Escalation Method"]').length - ).toBe(0); - expect( - wrapper.find('FormGroup[label="Privilege Escalation Username"]').length - ).toBe(0); - expect( - wrapper.find('FormGroup[label="Privilege Escalation Password"]').length - ).toBe(0); }; const machineFieldExpects = () => { @@ -371,6 +506,7 @@ describe('', () => { }; const sourceFieldExpects = () => { + expect(wrapper.find('FormGroup').length).toBe(8); expect(wrapper.find('FormGroup[label="Name"]').length).toBe(1); expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1); expect(wrapper.find('FormGroup[label="Organization"]').length).toBe(1); @@ -378,139 +514,218 @@ describe('', () => { expect(wrapper.find('FormGroup[label="Username"]').length).toBe(1); expect(wrapper.find('FormGroup[label="Password"]').length).toBe(1); expect(wrapper.find('FormGroup[label="SSH Private Key"]').length).toBe(1); - expect( - wrapper.find('FormGroup[label="Signed SSH Certificate"]').length - ).toBe(0); expect( wrapper.find('FormGroup[label="Private Key Passphrase"]').length ).toBe(1); - expect( - wrapper.find('FormGroup[label="Privelege Escalation Method"]').length - ).toBe(0); - expect( - wrapper.find('FormGroup[label="Privilege Escalation Username"]').length - ).toBe(0); - expect( - wrapper.find('FormGroup[label="Privilege Escalation Password"]').length - ).toBe(0); }; - beforeEach(() => { - onCancel = jest.fn(); - onSubmit = jest.fn(); - wrapper = mountWithContexts( - - ); - }); + const gceFieldExpects = () => { + expect(wrapper.find('FormGroup').length).toBe(8); + expect(wrapper.find('FormGroup[label="Name"]').length).toBe(1); + expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1); + expect(wrapper.find('FormGroup[label="Organization"]').length).toBe(1); + expect(wrapper.find('FormGroup[label="Credential Type"]').length).toBe(1); + expect( + wrapper.find('FormGroup[label="Service account JSON file"]').length + ).toBe(1); + expect( + wrapper.find('FormGroup[label="Service account email address"]').length + ).toBe(1); + expect(wrapper.find('FormGroup[label="Project"]').length).toBe(1); + expect(wrapper.find('FormGroup[label="RSA private key"]').length).toBe(1); + }; - afterEach(() => { - wrapper.unmount(); - }); - - test('Initially renders successfully', () => { - expect(wrapper.length).toBe(1); - }); - - test('should display form fields on add properly', () => { - wrapper = mountWithContexts( - - ); - addFieldExpects(); - }); - - test('should display form fields for machine credential properly', () => { - wrapper = mountWithContexts( - - ); - machineFieldExpects(); - }); - - test('should display form fields for source control credential properly', () => { - wrapper = mountWithContexts( - - ); - sourceFieldExpects(); - }); - - test('should update form values', async () => { - // name and description change - act(() => { - wrapper.find('input#credential-name').simulate('change', { - target: { value: 'new Foo', name: 'name' }, - }); - wrapper.find('input#credential-description').simulate('change', { - target: { value: 'new Bar', name: 'description' }, + describe('Add', () => { + beforeAll(async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); }); }); - wrapper.update(); - expect(wrapper.find('input#credential-name').prop('value')).toEqual( - 'new Foo' - ); - expect(wrapper.find('input#credential-description').prop('value')).toEqual( - 'new Bar' - ); - // organization change - act(() => { - wrapper.find('OrganizationLookup').invoke('onBlur')(); - wrapper.find('OrganizationLookup').invoke('onChange')({ + afterAll(() => { + wrapper.unmount(); + }); + test('should display form fields on add properly', async () => { + addFieldExpects(); + }); + test('should update form values', async () => { + // name and description change + await act(async () => { + wrapper.find('input#credential-name').simulate('change', { + target: { value: 'new Foo', name: 'name' }, + }); + wrapper.find('input#credential-description').simulate('change', { + target: { value: 'new Bar', name: 'description' }, + }); + }); + wrapper.update(); + expect(wrapper.find('input#credential-name').prop('value')).toEqual( + 'new Foo' + ); + expect( + wrapper.find('input#credential-description').prop('value') + ).toEqual('new Bar'); + // organization change + await act(async () => { + wrapper.find('OrganizationLookup').invoke('onBlur')(); + wrapper.find('OrganizationLookup').invoke('onChange')({ + id: 3, + name: 'organization', + }); + }); + wrapper.update(); + expect(wrapper.find('OrganizationLookup').prop('value')).toEqual({ id: 3, name: 'organization', }); }); - wrapper.update(); - expect(wrapper.find('OrganizationLookup').prop('value')).toEqual({ - id: 3, - name: 'organization', + test('should display cred type subform when scm type select has a value', async () => { + await act(async () => { + await wrapper + .find('AnsibleSelect[id="credential_type"]') + .invoke('onChange')(null, 1); + }); + wrapper.update(); + machineFieldExpects(); + await act(async () => { + await wrapper + .find('AnsibleSelect[id="credential_type"]') + .invoke('onChange')(null, 2); + }); + wrapper.update(); + sourceFieldExpects(); + }); + test('should update expected fields when gce service account json file uploaded', async () => { + await act(async () => { + await wrapper + .find('AnsibleSelect[id="credential_type"]') + .invoke('onChange')(null, 10); + }); + wrapper.update(); + gceFieldExpects(); + expect(wrapper.find('input#credential-username').prop('value')).toBe(''); + expect(wrapper.find('input#credential-project').prop('value')).toBe(''); + expect(wrapper.find('textarea#credential-sshKeyData').prop('value')).toBe( + '' + ); + await act(async () => { + wrapper.find('FileUpload').invoke('onChange')({ + name: 'foo.json', + text: () => + '{"client_email":"testemail@ansible.com","project_id":"test123","private_key":"-----BEGIN PRIVATE KEY-----\\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\n-----END PRIVATE KEY-----\\n"}', + }); + }); + wrapper.update(); + expect(wrapper.find('input#credential-username').prop('value')).toBe( + 'testemail@ansible.com' + ); + expect(wrapper.find('input#credential-project').prop('value')).toBe( + 'test123' + ); + expect(wrapper.find('textarea#credential-sshKeyData').prop('value')).toBe( + '-----BEGIN PRIVATE KEY-----\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n-----END PRIVATE KEY-----\n' + ); + }); + test('should clear expected fields when file clear button clicked', async () => { + await act(async () => { + wrapper.find('FileUploadField').invoke('onClearButtonClick')(); + }); + wrapper.update(); + expect(wrapper.find('input#credential-username').prop('value')).toBe(''); + expect(wrapper.find('input#credential-project').prop('value')).toBe(''); + expect(wrapper.find('textarea#credential-sshKeyData').prop('value')).toBe( + '' + ); + }); + test('should show error when error thrown parsing JSON', async () => { + expect(wrapper.find('#credential-gce-file-helper').text()).toBe( + 'Select a JSON formatted service account key to autopopulate the following fields.' + ); + await act(async () => { + wrapper.find('FileUpload').invoke('onChange')({ + name: 'foo.json', + text: () => '{not good json}', + }); + }); + wrapper.update(); + expect(wrapper.find('#credential-gce-file-helper').text()).toBe( + 'There was an error parsing the file. Please check the file formatting and try again.' + ); + }); + test('should call handleCancel when Cancel button is clicked', async () => { + expect(onCancel).not.toHaveBeenCalled(); + wrapper.find('button[aria-label="Cancel"]').invoke('onClick')(); + expect(onCancel).toBeCalled(); }); }); - test('should display cred type subform when scm type select has a value', async () => { - wrapper = mountWithContexts( - - ); - addFieldExpects(); - await act(async () => { - await wrapper - .find('AnsibleSelect[id="credential_type"]') - .invoke('onChange')(null, 1); + describe('Edit', () => { + afterEach(() => { + wrapper.unmount(); }); - wrapper.update(); - machineFieldExpects(); - await act(async () => { - await wrapper - .find('AnsibleSelect[id="credential_type"]') - .invoke('onChange')(null, 2); - }); - wrapper.update(); - sourceFieldExpects(); - }); + test('Initially renders successfully', async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); + }); - test('should call handleCancel when Cancel button is clicked', async () => { - expect(onCancel).not.toHaveBeenCalled(); - wrapper.find('button[aria-label="Cancel"]').invoke('onClick')(); - expect(onCancel).toBeCalled(); + expect(wrapper.length).toBe(1); + }); + + test('should display form fields for machine credential properly', async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + + machineFieldExpects(); + }); + + test('should display form fields for source control credential properly', async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + + sourceFieldExpects(); + }); + + test('should display form fields for gce credential properly', async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + + gceFieldExpects(); + }); }); }); diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/GoogleComputeEngineSubForm.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/GoogleComputeEngineSubForm.jsx new file mode 100644 index 0000000000..d1050b9375 --- /dev/null +++ b/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/GoogleComputeEngineSubForm.jsx @@ -0,0 +1,120 @@ +import React, { useState } from 'react'; +import { withI18n } from '@lingui/react'; +import { t } from '@lingui/macro'; +import { useField } from 'formik'; +import { FileUpload, FormGroup } from '@patternfly/react-core'; +import FormField from '@components/FormField'; +import { FormColumnLayout, FormFullWidthLayout } from '@components/FormLayout'; +import { required } from '@util/validators'; + +const GoogleComputeEngineSubForm = ({ i18n }) => { + const [fileError, setFileError] = useState(null); + const [filename, setFilename] = useState(''); + const [file, setFile] = useState(''); + const inputsUsernameHelpers = useField({ + name: 'inputs.username', + })[2]; + const inputsProjectHelpers = useField({ + name: 'inputs.project', + })[2]; + const inputsSSHKeyDataHelpers = useField({ + name: 'inputs.ssh_key_data', + })[2]; + + return ( + + + { + if (value) { + try { + setFile(value); + setFilename(value.name); + const fileText = await value.text(); + const fileJSON = JSON.parse(fileText); + if ( + !fileJSON.client_email && + !fileJSON.project_id && + !fileJSON.private_key + ) { + setFileError( + i18n._( + t`Expected at least one of client_email, project_id or private_key to be present in the file.` + ) + ); + } else { + inputsUsernameHelpers.setValue(fileJSON.client_email || ''); + inputsProjectHelpers.setValue(fileJSON.project_id || ''); + inputsSSHKeyDataHelpers.setValue(fileJSON.private_key || ''); + setFileError(null); + } + } catch { + setFileError( + i18n._( + t`There was an error parsing the file. Please check the file formatting and try again.` + ) + ); + } + } else { + setFile(''); + setFilename(''); + inputsUsernameHelpers.setValue(''); + inputsProjectHelpers.setValue(''); + inputsSSHKeyDataHelpers.setValue(''); + setFileError(null); + } + }} + dropzoneProps={{ + accept: '.json', + onDropRejected: () => { + setFileError( + i18n._( + t`File upload rejected. Please select a single .json file.` + ) + ); + }, + }} + /> + + + + + + + + ); +}; + +export default withI18n()(GoogleComputeEngineSubForm); diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/SharedFields.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/SharedFields.jsx index 0073d30a1f..51a95292bd 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/SharedFields.jsx +++ b/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/SharedFields.jsx @@ -26,6 +26,7 @@ export const SSHKeyDataField = withI18n()(({ i18n }) => ( label={i18n._(t`SSH Private Key`)} name="inputs.ssh_key_data" type="textarea" + rows={6} /> )); diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/SourceControlSubForm.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/SourceControlSubForm.jsx index 9bd6cfa19a..49bdc9fcfc 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/SourceControlSubForm.jsx +++ b/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/SourceControlSubForm.jsx @@ -12,16 +12,14 @@ import { } from './SharedFields'; const SourceControlSubForm = () => ( - <> - - - - - + + + + - + ); export default withI18n()(SourceControlSubForm); diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/index.js b/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/index.js index 04cad7240e..8c6d7d19f4 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/index.js +++ b/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/index.js @@ -1,2 +1,5 @@ +export { + default as GoogleComputeEngineSubForm, +} from './GoogleComputeEngineSubForm'; export { default as ManualSubForm } from './ManualSubForm'; export { default as SourceControlSubForm } from './SourceControlSubForm'; From 428527052ceff33bb562e9e5aa5cfd57a3f998d8 Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 23 Apr 2020 15:24:28 -0400 Subject: [PATCH 2/4] Use credential type value from hook value instead of accessing it directly --- .../Credential/shared/CredentialForm.jsx | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx index 466dd43bf3..bccfa50583 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx +++ b/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx @@ -105,19 +105,18 @@ function CredentialFormFields({ }} /> - {formik.values.credential_type !== undefined && - formik.values.credential_type !== '' && ( - - {i18n._(t`Type Details`)} + {credTypeField.value !== undefined && credTypeField.value !== '' && ( + + {i18n._(t`Type Details`)} + { { - { - [gceCredentialTypeId]: , - [sshCredentialTypeId]: , - [scmCredentialTypeId]: , - }[formik.values.credential_type] - } - - )} + [gceCredentialTypeId]: , + [sshCredentialTypeId]: , + [scmCredentialTypeId]: , + }[credTypeField.value] + } + + )} ); } From 67b826b43836f5bdb7c9af87a51a8113e275e0ed Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 23 Apr 2020 15:53:36 -0400 Subject: [PATCH 3/4] Moves credential mocks out to data json files --- .../screens/Credential/Credential.test.jsx | 2 +- .../Credential/shared/CredentialForm.test.jsx | 468 +----------------- .../shared/data.credentialTypes.json | 182 +++++++ .../Credential/shared/data.gceCredential.json | 96 ++++ .../shared/data.machineCredential.json | 92 ++++ ...redential.json => data.scmCredential.json} | 0 6 files changed, 376 insertions(+), 464 deletions(-) create mode 100644 awx/ui_next/src/screens/Credential/shared/data.credentialTypes.json create mode 100644 awx/ui_next/src/screens/Credential/shared/data.gceCredential.json create mode 100644 awx/ui_next/src/screens/Credential/shared/data.machineCredential.json rename awx/ui_next/src/screens/Credential/shared/{data.credential.json => data.scmCredential.json} (100%) diff --git a/awx/ui_next/src/screens/Credential/Credential.test.jsx b/awx/ui_next/src/screens/Credential/Credential.test.jsx index 36d4595bb0..e1308aa750 100644 --- a/awx/ui_next/src/screens/Credential/Credential.test.jsx +++ b/awx/ui_next/src/screens/Credential/Credential.test.jsx @@ -6,7 +6,7 @@ import { mountWithContexts, waitForElement, } from '../../../testUtils/enzymeHelpers'; -import mockCredential from './shared/data.credential.json'; +import mockCredential from './shared/data.scmCredential.json'; import mockOrgCredential from './shared/data.orgCredential.json'; import Credential from './Credential'; diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx index dd517529c2..5ff5980048 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx +++ b/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx @@ -1,472 +1,14 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; - +import machineCredential from './data.machineCredential.json'; +import gceCredential from './data.gceCredential.json'; +import scmCredential from './data.scmCredential.json'; +import credentialTypes from './data.credentialTypes.json'; import CredentialForm from './CredentialForm'; jest.mock('@api'); -const machineCredential = { - id: 3, - type: 'credential', - url: '/api/v2/credentials/3/', - related: { - named_url: '/api/v2/credentials/oersdgfasf++Machine+ssh++org/', - created_by: '/api/v2/users/1/', - modified_by: '/api/v2/users/1/', - organization: '/api/v2/organizations/1/', - activity_stream: '/api/v2/credentials/3/activity_stream/', - access_list: '/api/v2/credentials/3/access_list/', - object_roles: '/api/v2/credentials/3/object_roles/', - owner_users: '/api/v2/credentials/3/owner_users/', - owner_teams: '/api/v2/credentials/3/owner_teams/', - copy: '/api/v2/credentials/3/copy/', - input_sources: '/api/v2/credentials/3/input_sources/', - credential_type: '/api/v2/credential_types/1/', - }, - summary_fields: { - organization: { - id: 1, - name: 'org', - description: '', - }, - credential_type: { - id: 1, - name: 'Machine', - description: '', - }, - created_by: { - id: 1, - username: 'admin', - first_name: '', - last_name: '', - }, - modified_by: { - id: 1, - username: 'admin', - first_name: '', - last_name: '', - }, - object_roles: { - admin_role: { - description: 'Can manage all aspects of the credential', - name: 'Admin', - id: 36, - }, - use_role: { - description: 'Can use the credential in a job template', - name: 'Use', - id: 37, - }, - read_role: { - description: 'May view settings for the credential', - name: 'Read', - id: 38, - }, - }, - user_capabilities: { - edit: true, - delete: true, - copy: true, - use: true, - }, - owners: [ - { - id: 1, - type: 'user', - name: 'admin', - description: ' ', - url: '/api/v2/users/1/', - }, - { - id: 1, - type: 'organization', - name: 'org', - description: '', - url: '/api/v2/organizations/1/', - }, - ], - }, - created: '2020-02-18T15:35:04.563928Z', - modified: '2020-02-18T15:35:04.563957Z', - name: 'oersdgfasf', - description: '', - organization: 1, - credential_type: 1, - inputs: {}, - kind: 'ssh', - cloud: false, - kubernetes: false, -}; - -const sourceControlCredential = { - id: 4, - type: 'credential', - url: '/api/v2/credentials/4/', - related: { - named_url: '/api/v2/credentials/joijoij++Source Control+scm++/', - created_by: '/api/v2/users/1/', - modified_by: '/api/v2/users/1/', - activity_stream: '/api/v2/credentials/4/activity_stream/', - access_list: '/api/v2/credentials/4/access_list/', - object_roles: '/api/v2/credentials/4/object_roles/', - owner_users: '/api/v2/credentials/4/owner_users/', - owner_teams: '/api/v2/credentials/4/owner_teams/', - copy: '/api/v2/credentials/4/copy/', - input_sources: '/api/v2/credentials/4/input_sources/', - credential_type: '/api/v2/credential_types/2/', - user: '/api/v2/users/1/', - }, - summary_fields: { - credential_type: { - id: 2, - name: 'Source Control', - description: '', - }, - created_by: { - id: 1, - username: 'admin', - first_name: '', - last_name: '', - }, - modified_by: { - id: 1, - username: 'admin', - first_name: '', - last_name: '', - }, - object_roles: { - admin_role: { - description: 'Can manage all aspects of the credential', - name: 'Admin', - id: 39, - }, - use_role: { - description: 'Can use the credential in a job template', - name: 'Use', - id: 40, - }, - read_role: { - description: 'May view settings for the credential', - name: 'Read', - id: 41, - }, - }, - user_capabilities: { - edit: true, - delete: true, - copy: true, - use: true, - }, - owners: [ - { - id: 1, - type: 'user', - name: 'admin', - description: ' ', - url: '/api/v2/users/1/', - }, - ], - }, - created: '2020-02-18T16:03:01.366287Z', - modified: '2020-02-18T16:03:01.366315Z', - name: 'joijoij', - description: 'ojiojojo', - organization: null, - credential_type: 2, - inputs: { - ssh_key_unlock: '$encrypted$', - }, - kind: 'scm', - cloud: false, - kubernetes: false, -}; - -const gceCredential = { - id: 9, - type: 'credential', - url: '/api/v2/credentials/9/', - related: { - named_url: - '/api/v2/credentials/a gce cred++Google Compute Engine+cloud++Default/', - created_by: '/api/v2/users/1/', - modified_by: '/api/v2/users/1/', - organization: '/api/v2/organizations/4/', - activity_stream: '/api/v2/credentials/9/activity_stream/', - access_list: '/api/v2/credentials/9/access_list/', - object_roles: '/api/v2/credentials/9/object_roles/', - owner_users: '/api/v2/credentials/9/owner_users/', - owner_teams: '/api/v2/credentials/9/owner_teams/', - copy: '/api/v2/credentials/9/copy/', - input_sources: '/api/v2/credentials/9/input_sources/', - credential_type: '/api/v2/credential_types/10/', - }, - summary_fields: { - organization: { - id: 4, - name: 'Default', - description: '', - }, - credential_type: { - id: 10, - name: 'Google Compute Engine', - description: '', - }, - created_by: { - id: 1, - username: 'admin', - first_name: '', - last_name: '', - }, - modified_by: { - id: 1, - username: 'admin', - first_name: '', - last_name: '', - }, - object_roles: { - admin_role: { - description: 'Can manage all aspects of the credential', - name: 'Admin', - id: 287, - }, - use_role: { - description: 'Can use the credential in a job template', - name: 'Use', - id: 288, - }, - read_role: { - description: 'May view settings for the credential', - name: 'Read', - id: 289, - }, - }, - user_capabilities: { - edit: true, - delete: true, - copy: true, - use: true, - }, - owners: [ - { - id: 1, - type: 'user', - name: 'admin', - description: ' ', - url: '/api/v2/users/1/', - }, - { - id: 4, - type: 'organization', - name: 'Default', - description: '', - url: '/api/v2/organizations/4/', - }, - ], - }, - created: '2020-04-13T17:33:27.625773Z', - modified: '2020-04-13T17:33:27.625882Z', - name: 'a gce cred', - description: '', - organization: 4, - credential_type: 10, - inputs: { - project: 'test123', - username: 'test123.iam.gserviceaccount.com', - ssh_key_data: '$encrypted$', - }, - kind: 'gce', - cloud: true, - kubernetes: false, -}; - -const credentialTypes = [ - { - id: 2, - type: 'credential_type', - url: '/api/v2/credential_types/2/', - related: { - credentials: '/api/v2/credential_types/2/credentials/', - activity_stream: '/api/v2/credential_types/2/activity_stream/', - }, - summary_fields: { - user_capabilities: { - edit: false, - delete: false, - }, - }, - created: '2020-02-12T19:42:43.551238Z', - modified: '2020-02-12T19:43:03.164800Z', - name: 'Source Control', - description: '', - kind: 'scm', - namespace: 'scm', - managed_by_tower: true, - inputs: { - fields: [ - { - id: 'username', - label: 'Username', - type: 'string', - }, - { - id: 'password', - label: 'Password', - type: 'string', - secret: true, - }, - { - id: 'ssh_key_data', - label: 'Source Control Private Key', - type: 'string', - format: 'ssh_private_key', - secret: true, - multiline: true, - }, - { - id: 'ssh_key_unlock', - label: 'Private Key Passphrase', - type: 'string', - secret: true, - }, - ], - }, - injectors: {}, - }, - { - id: 1, - type: 'credential_type', - url: '/api/v2/credential_types/1/', - related: { - credentials: '/api/v2/credential_types/1/credentials/', - activity_stream: '/api/v2/credential_types/1/activity_stream/', - }, - summary_fields: { - user_capabilities: { - edit: false, - delete: false, - }, - }, - created: '2020-02-12T19:42:43.539626Z', - modified: '2020-02-12T19:43:03.159739Z', - name: 'Machine', - description: '', - kind: 'ssh', - namespace: 'ssh', - managed_by_tower: true, - inputs: { - fields: [ - { - id: 'username', - label: 'Username', - type: 'string', - }, - { - id: 'password', - label: 'Password', - type: 'string', - secret: true, - ask_at_runtime: true, - }, - { - id: 'ssh_key_data', - label: 'SSH Private Key', - type: 'string', - format: 'ssh_private_key', - secret: true, - multiline: true, - }, - { - id: 'ssh_public_key_data', - label: 'Signed SSH Certificate', - type: 'string', - multiline: true, - secret: true, - }, - { - id: 'ssh_key_unlock', - label: 'Private Key Passphrase', - type: 'string', - secret: true, - ask_at_runtime: true, - }, - { - id: 'become_method', - label: 'Privilege Escalation Method', - type: 'string', - help_text: - 'Specify a method for "become" operations. This is equivalent to specifying the --become-method Ansible parameter.', - }, - { - id: 'become_username', - label: 'Privilege Escalation Username', - type: 'string', - }, - { - id: 'become_password', - label: 'Privilege Escalation Password', - type: 'string', - secret: true, - ask_at_runtime: true, - }, - ], - }, - injectors: {}, - }, - { - id: 10, - type: 'credential_type', - url: '/api/v2/credential_types/10/', - related: { - credentials: '/api/v2/credential_types/10/credentials/', - activity_stream: '/api/v2/credential_types/10/activity_stream/', - }, - summary_fields: { - user_capabilities: { - edit: false, - delete: false, - }, - }, - created: '2020-04-09T19:20:27.090665Z', - modified: '2020-04-09T19:21:11.575214Z', - name: 'Google Compute Engine', - description: '', - kind: 'cloud', - namespace: 'gce', - managed_by_tower: true, - inputs: { - fields: [ - { - id: 'username', - label: 'Service Account Email Address', - type: 'string', - help_text: - 'The email address assigned to the Google Compute Engine service account.', - }, - { - id: 'project', - label: 'Project', - type: 'string', - help_text: - 'The Project ID is the GCE assigned identification. It is often constructed as three words or two words followed by a three-digit number. Examples: project-id-000 and another-project-id', - }, - { - id: 'ssh_key_data', - label: 'RSA Private Key', - type: 'string', - format: 'ssh_private_key', - secret: true, - multiline: true, - help_text: - 'Paste the contents of the PEM file associated with the service account email.', - }, - ], - required: ['username', 'ssh_key_data'], - }, - injectors: {}, - }, -]; - describe('', () => { let wrapper; const onCancel = jest.fn(); @@ -704,7 +246,7 @@ describe('', () => { ); diff --git a/awx/ui_next/src/screens/Credential/shared/data.credentialTypes.json b/awx/ui_next/src/screens/Credential/shared/data.credentialTypes.json new file mode 100644 index 0000000000..75e1cf1481 --- /dev/null +++ b/awx/ui_next/src/screens/Credential/shared/data.credentialTypes.json @@ -0,0 +1,182 @@ +[ + { + "id": 2, + "type": "credential_type", + "url": "/api/v2/credential_types/2/", + "related": { + "credentials": "/api/v2/credential_types/2/credentials/", + "activity_stream": "/api/v2/credential_types/2/activity_stream/" + }, + "summary_fields": { + "user_capabilities": { + "edit": false, + "delete": false + } + }, + "created": "2020-02-12T19:42:43.551238Z", + "modified": "2020-02-12T19:43:03.164800Z", + "name": "Source Control", + "description": "", + "kind": "scm", + "namespace": "scm", + "managed_by_tower": true, + "inputs": { + "fields": [ + { + "id": "username", + "label": "Username", + "type": "string" + }, + { + "id": "password", + "label": "Password", + "type": "string", + "secret": true + }, + { + "id": "ssh_key_data", + "label": "Source Control Private Key", + "type": "string", + "format": "ssh_private_key", + "secret": true, + "multiline": true + }, + { + "id": "ssh_key_unlock", + "label": "Private Key Passphrase", + "type": "string", + "secret": true + } + ] + }, + "injectors": {} + }, + { + "id": 1, + "type": "credential_type", + "url": "/api/v2/credential_types/1/", + "related": { + "credentials": "/api/v2/credential_types/1/credentials/", + "activity_stream": "/api/v2/credential_types/1/activity_stream/" + }, + "summary_fields": { + "user_capabilities": { + "edit": false, + "delete": false + } + }, + "created": "2020-02-12T19:42:43.539626Z", + "modified": "2020-02-12T19:43:03.159739Z", + "name": "Machine", + "description": "", + "kind": "ssh", + "namespace": "ssh", + "managed_by_tower": true, + "inputs": { + "fields": [ + { + "id": "username", + "label": "Username", + "type": "string" + }, + { + "id": "password", + "label": "Password", + "type": "string", + "secret": true, + "ask_at_runtime": true + }, + { + "id": "ssh_key_data", + "label": "SSH Private Key", + "type": "string", + "format": "ssh_private_key", + "secret": true, + "multiline": true + }, + { + "id": "ssh_public_key_data", + "label": "Signed SSH Certificate", + "type": "string", + "multiline": true, + "secret": true + }, + { + "id": "ssh_key_unlock", + "label": "Private Key Passphrase", + "type": "string", + "secret": true, + "ask_at_runtime": true + }, + { + "id": "become_method", + "label": "Privilege Escalation Method", + "type": "string", + "help_text": "Specify a method for \"become\" operations. This is equivalent to specifying the --become-method Ansible parameter." + }, + { + "id": "become_username", + "label": "Privilege Escalation Username", + "type": "string" + }, + { + "id": "become_password", + "label": "Privilege Escalation Password", + "type": "string", + "secret": true, + "ask_at_runtime": true + } + ] + }, + "injectors": {} + }, + { + "id": 10, + "type": "credential_type", + "url": "/api/v2/credential_types/10/", + "related": { + "credentials": "/api/v2/credential_types/10/credentials/", + "activity_stream": "/api/v2/credential_types/10/activity_stream/" + }, + "summary_fields": { + "user_capabilities": { + "edit": false, + "delete": false + } + }, + "created": "2020-04-09T19:20:27.090665Z", + "modified": "2020-04-09T19:21:11.575214Z", + "name": "Google Compute Engine", + "description": "", + "kind": "cloud", + "namespace": "gce", + "managed_by_tower": true, + "inputs": { + "fields": [ + { + "id": "username", + "label": "Service Account Email Address", + "type": "string", + "help_text": "The email address assigned to the Google Compute Engine service account." + }, + { + "id": "project", + "label": "Project", + "type": "string", + "help_text": "The Project ID is the GCE assigned identification. It is often constructed as three words or two words followed by a three-digit number. Examples: project-id-000 and another-project-id" + }, + { + "id": "ssh_key_data", + "label": "RSA Private Key", + "type": "string", + "format": "ssh_private_key", + "secret": true, + "multiline": true, + "help_text": "Paste the contents of the PEM file associated with the service account email." + } + ], + "required": ["username", "ssh_key_data"] + }, + "injectors": {} + } +] diff --git a/awx/ui_next/src/screens/Credential/shared/data.gceCredential.json b/awx/ui_next/src/screens/Credential/shared/data.gceCredential.json new file mode 100644 index 0000000000..9359b09a06 --- /dev/null +++ b/awx/ui_next/src/screens/Credential/shared/data.gceCredential.json @@ -0,0 +1,96 @@ +{ + "id": 9, + "type": "credential", + "url": "/api/v2/credentials/9/", + "related": { + "named_url": "/api/v2/credentials/a gce cred++Google Compute Engine+cloud++Default/", + "created_by": "/api/v2/users/1/", + "modified_by": "/api/v2/users/1/", + "organization": "/api/v2/organizations/4/", + "activity_stream": "/api/v2/credentials/9/activity_stream/", + "access_list": "/api/v2/credentials/9/access_list/", + "object_roles": "/api/v2/credentials/9/object_roles/", + "owner_users": "/api/v2/credentials/9/owner_users/", + "owner_teams": "/api/v2/credentials/9/owner_teams/", + "copy": "/api/v2/credentials/9/copy/", + "input_sources": "/api/v2/credentials/9/input_sources/", + "credential_type": "/api/v2/credential_types/10/" + }, + "summary_fields": { + "organization": { + "id": 4, + "name": "Default", + "description": "" + }, + "credential_type": { + "id": 10, + "name": "Google Compute Engine", + "description": "" + }, + "created_by": { + "id": 1, + "username": "admin", + "first_name": "", + "last_name": "" + }, + "modified_by": { + "id": 1, + "username": "admin", + "first_name": "", + "last_name": "" + }, + "object_roles": { + "admin_role": { + "description": "Can manage all aspects of the credential", + "name": "Admin", + "id": 287 + }, + "use_role": { + "description": "Can use the credential in a job template", + "name": "Use", + "id": 288 + }, + "read_role": { + "description": "May view settings for the credential", + "name": "Read", + "id": 289 + } + }, + "user_capabilities": { + "edit": true, + "delete": true, + "copy": true, + "use": true + }, + "owners": [ + { + "id": 1, + "type": "user", + "name": "admin", + "description": " ", + "url": "/api/v2/users/1/" + }, + { + "id": 4, + "type": "organization", + "name": "Default", + "description": "", + "url": "/api/v2/organizations/4/" + } + ] + }, + "created": "2020-04-13T17:33:27.625773Z", + "modified": "2020-04-13T17:33:27.625882Z", + "name": "a gce cred", + "description": "", + "organization": 4, + "credential_type": 10, + "inputs": { + "project": "test123", + "username": "test123.iam.gserviceaccount.com", + "ssh_key_data": "$encrypted$" + }, + "kind": "gce", + "cloud": true, + "kubernetes": false +} diff --git a/awx/ui_next/src/screens/Credential/shared/data.machineCredential.json b/awx/ui_next/src/screens/Credential/shared/data.machineCredential.json new file mode 100644 index 0000000000..e2576ca487 --- /dev/null +++ b/awx/ui_next/src/screens/Credential/shared/data.machineCredential.json @@ -0,0 +1,92 @@ +{ + "id": 3, + "type": "credential", + "url": "/api/v2/credentials/3/", + "related": { + "named_url": "/api/v2/credentials/oersdgfasf++Machine+ssh++org/", + "created_by": "/api/v2/users/1/", + "modified_by": "/api/v2/users/1/", + "organization": "/api/v2/organizations/1/", + "activity_stream": "/api/v2/credentials/3/activity_stream/", + "access_list": "/api/v2/credentials/3/access_list/", + "object_roles": "/api/v2/credentials/3/object_roles/", + "owner_users": "/api/v2/credentials/3/owner_users/", + "owner_teams": "/api/v2/credentials/3/owner_teams/", + "copy": "/api/v2/credentials/3/copy/", + "input_sources": "/api/v2/credentials/3/input_sources/", + "credential_type": "/api/v2/credential_types/1/" + }, + "summary_fields": { + "organization": { + "id": 1, + "name": "org", + "description": "" + }, + "credential_type": { + "id": 1, + "name": "Machine", + "description": "" + }, + "created_by": { + "id": 1, + "username": "admin", + "first_name": "", + "last_name": "" + }, + "modified_by": { + "id": 1, + "username": "admin", + "first_name": "", + "last_name": "" + }, + "object_roles": { + "admin_role": { + "description": "Can manage all aspects of the credential", + "name": "Admin", + "id": 36 + }, + "use_role": { + "description": "Can use the credential in a job template", + "name": "Use", + "id": 37 + }, + "read_role": { + "description": "May view settings for the credential", + "name": "Read", + "id": 38 + } + }, + "user_capabilities": { + "edit": true, + "delete": true, + "copy": true, + "use": true + }, + "owners": [ + { + "id": 1, + "type": "user", + "name": "admin", + "description": " ", + "url": "/api/v2/users/1/" + }, + { + "id": 1, + "type": "organization", + "name": "org", + "description": "", + "url": "/api/v2/organizations/1/" + } + ] + }, + "created": "2020-02-18T15:35:04.563928Z", + "modified": "2020-02-18T15:35:04.563957Z", + "name": "oersdgfasf", + "description": "", + "organization": 1, + "credential_type": 1, + "inputs": {}, + "kind": "ssh", + "cloud": false, + "kubernetes": false +} diff --git a/awx/ui_next/src/screens/Credential/shared/data.credential.json b/awx/ui_next/src/screens/Credential/shared/data.scmCredential.json similarity index 100% rename from awx/ui_next/src/screens/Credential/shared/data.credential.json rename to awx/ui_next/src/screens/Credential/shared/data.scmCredential.json From da1a19ce88f4d8e76632fc271f150f9704441ef5 Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 18 May 2020 15:06:20 -0400 Subject: [PATCH 4/4] Fixes pathing after aliases removed --- .../screens/Credential/shared/CredentialForm.test.jsx | 2 +- .../CredentialSubForms/GoogleComputeEngineSubForm.jsx | 9 ++++++--- .../Credential/shared/CredentialSubForms/index.js | 4 +--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx index 5ff5980048..c77089b758 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx +++ b/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx @@ -7,7 +7,7 @@ import scmCredential from './data.scmCredential.json'; import credentialTypes from './data.credentialTypes.json'; import CredentialForm from './CredentialForm'; -jest.mock('@api'); +jest.mock('../../../api'); describe('', () => { let wrapper; diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/GoogleComputeEngineSubForm.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/GoogleComputeEngineSubForm.jsx index d1050b9375..89584b956c 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/GoogleComputeEngineSubForm.jsx +++ b/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/GoogleComputeEngineSubForm.jsx @@ -3,9 +3,12 @@ import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { useField } from 'formik'; import { FileUpload, FormGroup } from '@patternfly/react-core'; -import FormField from '@components/FormField'; -import { FormColumnLayout, FormFullWidthLayout } from '@components/FormLayout'; -import { required } from '@util/validators'; +import FormField from '../../../../components/FormField'; +import { + FormColumnLayout, + FormFullWidthLayout, +} from '../../../../components/FormLayout'; +import { required } from '../../../../util/validators'; const GoogleComputeEngineSubForm = ({ i18n }) => { const [fileError, setFileError] = useState(null); diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/index.js b/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/index.js index 8c6d7d19f4..fcd719a65d 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/index.js +++ b/awx/ui_next/src/screens/Credential/shared/CredentialSubForms/index.js @@ -1,5 +1,3 @@ -export { - default as GoogleComputeEngineSubForm, -} from './GoogleComputeEngineSubForm'; +export { default as GoogleComputeEngineSubForm } from './GoogleComputeEngineSubForm'; export { default as ManualSubForm } from './ManualSubForm'; export { default as SourceControlSubForm } from './SourceControlSubForm';