]> source.dussan.org Git - sonarqube.git/blob
f43c1f2201b39c3ca3c12236388121be8f798c6a
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2020 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 import * as React from 'react';
21 import {
22   deleteProjectAlmBinding,
23   getAlmSettings,
24   getProjectAlmBinding,
25   setProjectAzureBinding,
26   setProjectBitbucketBinding,
27   setProjectGithubBinding,
28   setProjectGitlabBinding
29 } from '../../../../api/alm-settings';
30 import throwGlobalError from '../../../../app/utils/throwGlobalError';
31 import { AlmKeys, AlmSettingsInstance, ProjectAlmBinding } from '../../../../types/alm-settings';
32 import PRDecorationBindingRenderer from './PRDecorationBindingRenderer';
33
34 interface Props {
35   component: T.Component;
36 }
37
38 interface State {
39   formData: ProjectAlmBinding;
40   instances: AlmSettingsInstance[];
41   isValid: boolean;
42   loading: boolean;
43   originalData?: ProjectAlmBinding;
44   saving: boolean;
45   success: boolean;
46 }
47
48 const REQUIRED_FIELDS_BY_ALM: {
49   [almKey in AlmKeys]: Array<keyof T.Omit<ProjectAlmBinding, 'key'>>;
50 } = {
51   [AlmKeys.Azure]: [],
52   [AlmKeys.Bitbucket]: ['repository', 'slug'],
53   [AlmKeys.GitHub]: ['repository'],
54   [AlmKeys.GitLab]: []
55 };
56
57 export default class PRDecorationBinding extends React.PureComponent<Props, State> {
58   mounted = false;
59   state: State = {
60     formData: { key: '' },
61     instances: [],
62     isValid: false,
63     loading: true,
64     saving: false,
65     success: false
66   };
67
68   componentDidMount() {
69     this.mounted = true;
70     this.fetchDefinitions();
71   }
72
73   componentWillUnmount() {
74     this.mounted = false;
75   }
76
77   fetchDefinitions = () => {
78     const project = this.props.component.key;
79     return Promise.all([getAlmSettings(project), this.getProjectBinding(project)])
80       .then(([instances, originalData]) => {
81         if (this.mounted) {
82           this.setState(({ formData }) => {
83             const newFormData = originalData || formData;
84             return {
85               formData: newFormData,
86               instances: instances || [],
87               isValid: this.validateForm(newFormData),
88               loading: false,
89               originalData
90             };
91           });
92         }
93       })
94       .catch(() => {
95         if (this.mounted) {
96           this.setState({ loading: false });
97         }
98       });
99   };
100
101   getProjectBinding(project: string): Promise<ProjectAlmBinding | undefined> {
102     return getProjectAlmBinding(project).catch((response: Response) => {
103       if (response && response.status === 404) {
104         return Promise.resolve(undefined);
105       }
106       return throwGlobalError(response);
107     });
108   }
109
110   catchError = () => {
111     if (this.mounted) {
112       this.setState({ saving: false });
113     }
114   };
115
116   handleReset = () => {
117     const { component } = this.props;
118     this.setState({ saving: true });
119     deleteProjectAlmBinding(component.key)
120       .then(() => {
121         if (this.mounted) {
122           this.setState({
123             formData: {
124               key: '',
125               repository: '',
126               slug: ''
127             },
128             originalData: undefined,
129             saving: false,
130             success: true
131           });
132         }
133       })
134       .catch(this.catchError);
135   };
136
137   submitProjectAlmBinding(
138     alm: AlmKeys,
139     key: string,
140     almSpecificFields?: T.Omit<ProjectAlmBinding, 'key'>
141   ): Promise<void> {
142     const almSetting = key;
143     const project = this.props.component.key;
144
145     switch (alm) {
146       case AlmKeys.Azure:
147         return setProjectAzureBinding({
148           almSetting,
149           project
150         });
151       case AlmKeys.Bitbucket: {
152         if (!almSpecificFields) {
153           return Promise.reject();
154         }
155         const { repository = '', slug = '' } = almSpecificFields;
156         return setProjectBitbucketBinding({
157           almSetting,
158           project,
159           repository,
160           slug
161         });
162       }
163       case AlmKeys.GitHub: {
164         const repository = almSpecificFields && almSpecificFields.repository;
165         if (!repository) {
166           return Promise.reject();
167         }
168         return setProjectGithubBinding({
169           almSetting,
170           project,
171           repository
172         });
173       }
174
175       case AlmKeys.GitLab: {
176         const repository = almSpecificFields && almSpecificFields.repository;
177         return setProjectGitlabBinding({
178           almSetting,
179           project,
180           repository
181         });
182       }
183
184       default:
185         return Promise.reject();
186     }
187   }
188
189   handleSubmit = () => {
190     this.setState({ saving: true });
191     const {
192       formData: { key, ...additionalFields },
193       instances
194     } = this.state;
195
196     const selected = instances.find(i => i.key === key);
197     if (!key || !selected) {
198       return;
199     }
200
201     if (key) {
202       this.submitProjectAlmBinding(selected.alm, key, additionalFields)
203         .then(() => {
204           if (this.mounted) {
205             this.setState({
206               saving: false,
207               success: true
208             });
209           }
210         })
211         .then(this.fetchDefinitions)
212         .catch(this.catchError);
213     }
214   };
215
216   handleFieldChange = (id: keyof ProjectAlmBinding, value: string) => {
217     this.setState(({ formData }) => {
218       const newFormData = {
219         ...formData,
220         [id]: value
221       };
222       return {
223         formData: newFormData,
224         isValid: this.validateForm(newFormData),
225         success: false
226       };
227     });
228   };
229
230   validateForm = ({ key, ...additionalFields }: State['formData']) => {
231     const { instances } = this.state;
232     const selected = instances.find(i => i.key === key);
233     if (!key || !selected) {
234       return false;
235     }
236     return REQUIRED_FIELDS_BY_ALM[selected.alm].reduce(
237       (result: boolean, field) => result && Boolean(additionalFields[field]),
238       true
239     );
240   };
241
242   render() {
243     return (
244       <PRDecorationBindingRenderer
245         onFieldChange={this.handleFieldChange}
246         onReset={this.handleReset}
247         onSubmit={this.handleSubmit}
248         {...this.state}
249       />
250     );
251   }
252 }