diff options
author | Jeremy Davis <jeremy.davis@sonarsource.com> | 2019-10-21 17:36:17 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2019-11-06 10:04:27 +0100 |
commit | efe02985a999dcc36c966ade6153e3af4829bdab (patch) | |
tree | f9527ff4235929e1a2dad25ba5549f3b9031f8e7 | |
parent | 7c707a301f8a970f67a0bd34a7f02dace253c579 (diff) | |
download | sonarqube-efe02985a999dcc36c966ade6153e3af4829bdab.tar.gz sonarqube-efe02985a999dcc36c966ade6153e3af4829bdab.zip |
SONAR-12515 UI for project-level form - Azure
7 files changed, 138 insertions, 58 deletions
diff --git a/server/sonar-web/src/main/js/api/almSettings.ts b/server/sonar-web/src/main/js/api/almSettings.ts index 52cf1b61366..e7aaee50827 100644 --- a/server/sonar-web/src/main/js/api/almSettings.ts +++ b/server/sonar-web/src/main/js/api/almSettings.ts @@ -57,13 +57,17 @@ export function countBindedProjects(almSetting: string) { } export function getProjectAlmBinding(project: string): Promise<T.ProjectAlmBinding> { - return getJSON('/api/alm_settings/get_github_binding', { project }); + return getJSON('/api/alm_settings/get_binding', { project }); } export function deleteProjectAlmBinding(project: string): Promise<void> { return post('/api/alm_settings/delete_binding', { project }).catch(throwGlobalError); } -export function setProjectAlmBinding(data: T.GithubProjectAlmBinding) { +export function setProjectAzureBinding(data: T.AzureProjectAlmBinding) { + return post('/api/alm_settings/set_azure_binding', data).catch(throwGlobalError); +} + +export function setProjectGithubBinding(data: T.GithubProjectAlmBinding) { return post('/api/alm_settings/set_github_binding', data).catch(throwGlobalError); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx index 82482cbcbfc..6c31725727a 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx @@ -22,9 +22,11 @@ import { deleteProjectAlmBinding, getAlmSettings, getProjectAlmBinding, - setProjectAlmBinding + setProjectAzureBinding, + setProjectGithubBinding } from '../../../../api/almSettings'; import throwGlobalError from '../../../../app/utils/throwGlobalError'; +import { ALM_KEYS } from '../../utils'; import PRDecorationBindingRenderer from './PRDecorationBindingRenderer'; interface Props { @@ -32,7 +34,7 @@ interface Props { } interface State { - formData: T.GithubBinding; + formData: T.ProjectAlmBinding; hasBinding: boolean; instances: T.AlmSettingsInstance[]; isValid: boolean; @@ -41,6 +43,11 @@ interface State { success: boolean; } +const FIELDS_BY_ALM: { [almKey: string]: Array<'repository'> } = { + [ALM_KEYS.AZURE]: [], + [ALM_KEYS.GITHUB]: ['repository'] +}; + export default class PRDecorationBinding extends React.PureComponent<Props, State> { mounted = false; state: State = { @@ -70,13 +77,16 @@ export default class PRDecorationBinding extends React.PureComponent<Props, Stat return Promise.all([getAlmSettings(project), this.getProjectBinding(project)]) .then(([instances, data]) => { if (this.mounted) { - this.setState(({ formData }) => ({ - formData: data || formData, - hasBinding: Boolean(data), - instances, - isValid: this.validateForm(), - loading: false - })); + this.setState(({ formData }) => { + const newFormData = data || formData; + return { + formData: newFormData, + hasBinding: Boolean(data), + instances, + isValid: this.validateForm(newFormData), + loading: false + }; + }); if (!data && instances.length === 1) { this.handleFieldChange('key', instances[0].key); @@ -125,18 +135,50 @@ export default class PRDecorationBinding extends React.PureComponent<Props, Stat .catch(this.catchError); }; + submitProjectAlmBinding( + alm: ALM_KEYS, + key: string, + almSpecificFields?: { repository?: string } + ): Promise<void> { + const almSetting = key; + const project = this.props.component.key; + + switch (alm) { + case ALM_KEYS.AZURE: + return setProjectAzureBinding({ + almSetting, + project + }); + case ALM_KEYS.GITHUB: { + const repository = almSpecificFields && almSpecificFields.repository; + if (!repository) { + return Promise.reject(); + } + return setProjectGithubBinding({ + almSetting, + project, + repository + }); + } + default: + return Promise.reject(); + } + } + handleSubmit = () => { this.setState({ saving: true }); const { - formData: { key, repository } + formData: { key, ...additionalFields }, + instances } = this.state; - if (key && repository) { - setProjectAlmBinding({ - almSetting: key, - project: this.props.component.key, - repository - }) + const selected = instances.find(i => i.key === key); + if (!key || !selected) { + return; + } + + if (key) { + this.submitProjectAlmBinding(selected.alm as ALM_KEYS, key, additionalFields) .then(() => { if (this.mounted) { this.setState({ @@ -150,21 +192,28 @@ export default class PRDecorationBinding extends React.PureComponent<Props, Stat } }; - handleFieldChange = (id: keyof T.GithubBinding, value: string) => { - this.setState(({ formData: formdata }) => ({ - formData: { - ...formdata, + handleFieldChange = (id: keyof T.ProjectAlmBinding, value: string) => { + this.setState(({ formData }) => { + const newFormData = { + ...formData, [id]: value - }, - isValid: this.validateForm(), - success: false - })); + }; + return { + formData: newFormData, + isValid: this.validateForm(newFormData), + success: false + }; + }); }; - validateForm = () => { - const { formData } = this.state; - return Object.values(formData).reduce( - (result: boolean, value) => result && Boolean(value), + validateForm = ({ key, ...additionalFields }: State['formData']) => { + const { instances } = this.state; + const selected = instances.find(i => i.key === key); + if (!key || !selected) { + return false; + } + return FIELDS_BY_ALM[selected.alm as ALM_KEYS].reduce( + (result: boolean, field) => result && Boolean(additionalFields[field]), true ); }; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx index a36aca0c226..3fa7892e320 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx @@ -26,14 +26,15 @@ import AlertSuccessIcon from 'sonar-ui-common/components/icons/AlertSuccessIcon' import { Alert } from 'sonar-ui-common/components/ui/Alert'; import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; import { translate } from 'sonar-ui-common/helpers/l10n'; +import { ALM_KEYS } from '../../utils'; export interface PRDecorationBindingRendererProps { - formData: T.GithubBinding; + formData: T.ProjectAlmBinding; hasBinding: boolean; instances: T.AlmSettingsInstance[]; isValid: boolean; loading: boolean; - onFieldChange: (id: keyof T.GithubBinding, value: string) => void; + onFieldChange: (id: keyof T.ProjectAlmBinding, value: string) => void; onReset: () => void; onSubmit: () => void; saving: boolean; @@ -79,6 +80,9 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe ); } + const selected = key && instances.find(i => i.key === key); + const alm = selected && (selected.alm as ALM_KEYS); + return ( <div> <header className="page-header"> @@ -110,7 +114,7 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe /> </div> - {key && ( + {alm === ALM_KEYS.GITHUB && ( <div className="form-field"> <label htmlFor="repository"> {translate('settings.pr_decoration.binding.form.repository')} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx index 4129a868c6f..e0e1b899c4a 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx @@ -24,15 +24,18 @@ import { deleteProjectAlmBinding, getAlmSettings, getProjectAlmBinding, - setProjectAlmBinding + setProjectAzureBinding, + setProjectGithubBinding } from '../../../../../api/almSettings'; import { mockComponent } from '../../../../../helpers/testMocks'; +import { ALM_KEYS } from '../../../utils'; import PRDecorationBinding from '../PRDecorationBinding'; jest.mock('../../../../../api/almSettings', () => ({ getAlmSettings: jest.fn().mockResolvedValue([]), getProjectAlmBinding: jest.fn().mockResolvedValue(undefined), - setProjectAlmBinding: jest.fn().mockResolvedValue(undefined), + setProjectAzureBinding: jest.fn().mockResolvedValue(undefined), + setProjectGithubBinding: jest.fn().mockResolvedValue(undefined), deleteProjectAlmBinding: jest.fn().mockResolvedValue(undefined) })); @@ -48,7 +51,7 @@ it('should render correctly', () => { it('should fill selects and fill formdata', async () => { const url = 'github.com'; - const instances = [{ key: 'instance1', url, alm: 'github' }]; + const instances = [{ key: 'instance1', url, alm: ALM_KEYS.GITHUB }]; const formdata = { key: 'instance1', repository: 'account/repo' @@ -96,18 +99,38 @@ it('should handle reset', async () => { expect(wrapper.state().hasBinding).toBe(false); }); -it('should handle submit', async () => { +it('should handle submit to github or azure', async () => { const wrapper = shallowRender(); await waitAndUpdate(wrapper); - wrapper.setState({ formData }); + const instances = [ + { key: 'github', alm: ALM_KEYS.GITHUB }, + { key: 'azure', alm: ALM_KEYS.AZURE } + ]; + // Github + const githubKey = 'github'; + const repository = 'repo/path'; + wrapper.setState({ formData: { key: githubKey, repository }, instances }); wrapper.instance().handleSubmit(); await waitAndUpdate(wrapper); - expect(setProjectAlmBinding).toBeCalledWith({ - almSetting: formData.key, + expect(setProjectGithubBinding).toBeCalledWith({ + almSetting: githubKey, project: PROJECT_KEY, - repository: formData.repository + repository + }); + expect(wrapper.state().hasBinding).toBe(true); + expect(wrapper.state().success).toBe(true); + + // azure + const azureKey = 'azure'; + wrapper.setState({ formData: { key: azureKey } }); + wrapper.instance().handleSubmit(); + await waitAndUpdate(wrapper); + + expect(setProjectAzureBinding).toBeCalledWith({ + almSetting: azureKey, + project: PROJECT_KEY }); expect(wrapper.state().hasBinding).toBe(true); expect(wrapper.state().success).toBe(true); @@ -115,7 +138,7 @@ it('should handle submit', async () => { it('should handle failures gracefully', async () => { (getProjectAlmBinding as jest.Mock).mockRejectedValueOnce({ status: 500 }); - (setProjectAlmBinding as jest.Mock).mockRejectedValueOnce({ status: 500 }); + (setProjectGithubBinding as jest.Mock).mockRejectedValueOnce({ status: 500 }); (deleteProjectAlmBinding as jest.Mock).mockRejectedValueOnce({ status: 500 }); const wrapper = shallowRender(); @@ -158,13 +181,15 @@ it('should validate form', async () => { const wrapper = shallowRender(); await waitAndUpdate(wrapper); - expect(wrapper.instance().validateForm()).toBe(false); - - wrapper.setState({ formData: { key: '', repository: 'c' } }); - expect(wrapper.instance().validateForm()).toBe(false); + expect(wrapper.instance().validateForm({ key: '', repository: '' })).toBe(false); + expect(wrapper.instance().validateForm({ key: '', repository: 'c' })).toBe(false); - wrapper.setState({ formData: { key: 'a', repository: 'c' } }); - expect(wrapper.instance().validateForm()).toBe(true); + wrapper.setState({ + instances: [{ key: 'azure', alm: ALM_KEYS.AZURE }, { key: 'github', alm: ALM_KEYS.GITHUB }] + }); + expect(wrapper.instance().validateForm({ key: 'azure' })).toBe(true); + expect(wrapper.instance().validateForm({ key: 'github', repository: '' })).toBe(false); + expect(wrapper.instance().validateForm({ key: 'github', repository: 'asdf' })).toBe(true); }); function shallowRender(props: Partial<PRDecorationBinding['props']> = {}) { diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx index f47e0713fcf..48c7c098208 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx @@ -74,7 +74,7 @@ it('should render multiple instances correctly', () => { expect( shallowRender({ formData: { - key: 'Github - main instance', + key: 'i1', repository: 'account/repo' }, hasBinding: true, diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap index 06ad8d9c9c6..a57c9bfa856 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap @@ -373,7 +373,7 @@ exports[`should render multiple instances correctly 2`] = ` ] } searchable={false} - value="Github - main instance" + value="i1" /> </div> <div diff --git a/server/sonar-web/src/main/js/types/alm-settings.d.ts b/server/sonar-web/src/main/js/types/alm-settings.d.ts index 0944092c96f..16fd0be23da 100644 --- a/server/sonar-web/src/main/js/types/alm-settings.d.ts +++ b/server/sonar-web/src/main/js/types/alm-settings.d.ts @@ -45,19 +45,17 @@ declare namespace T { export interface ProjectAlmBinding { key: string; - alm: string; - url: string; - repository: string; + repository?: string; } - export interface GithubProjectAlmBinding { + export interface AzureProjectAlmBinding { almSetting: string; project: string; - repository: string; } - export interface GithubBinding { - key: string; - repository?: string; + export interface GithubProjectAlmBinding { + almSetting: string; + project: string; + repository: string; } } |