3 * Copyright (C) 2009-2021 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 import * as React from 'react';
21 import { connect } from 'react-redux';
22 import { HttpStatus } from 'sonar-ui-common/helpers/request';
24 deleteProjectAlmBinding,
27 setProjectAzureBinding,
28 setProjectBitbucketBinding,
29 setProjectBitbucketCloudBinding,
30 setProjectGithubBinding,
31 setProjectGitlabBinding
32 } from '../../../../api/alm-settings';
33 import throwGlobalError from '../../../../app/utils/throwGlobalError';
34 import { getAppState, Store } from '../../../../store/rootReducer';
38 ProjectAlmBindingResponse
39 } from '../../../../types/alm-settings';
40 import { EditionKey } from '../../../../types/editions';
41 import PRDecorationBindingRenderer from './PRDecorationBindingRenderer';
43 type FormData = T.Omit<ProjectAlmBindingResponse, 'alm'>;
45 interface StateProps {
46 monorepoEnabled: boolean;
50 component: T.Component;
55 instances: AlmSettingsInstance[];
57 isConfigured: boolean;
60 orignalData?: FormData;
65 const REQUIRED_FIELDS_BY_ALM: {
66 [almKey in AlmKeys]: Array<keyof T.Omit<FormData, 'key'>>;
68 [AlmKeys.Azure]: ['repository', 'slug'],
69 [AlmKeys.BitbucketServer]: ['repository', 'slug'],
70 [AlmKeys.BitbucketCloud]: ['repository'],
71 [AlmKeys.GitHub]: ['repository'],
72 [AlmKeys.GitLab]: ['repository']
75 export class PRDecorationBinding extends React.PureComponent<Props & StateProps, State> {
78 formData: { key: '' },
90 this.fetchDefinitions();
93 componentWillUnmount() {
97 fetchDefinitions = () => {
98 const project = this.props.component.key;
99 return Promise.all([getAlmSettings(project), this.getProjectBinding(project)])
100 .then(([instances, originalData]) => {
102 this.setState(({ formData }) => {
103 const newFormData = originalData || formData;
105 formData: newFormData,
106 instances: instances || [],
108 isConfigured: !!originalData,
109 isValid: this.validateForm(newFormData),
111 orignalData: newFormData
118 this.setState({ loading: false });
123 getProjectBinding(project: string): Promise<ProjectAlmBindingResponse | undefined> {
124 return getProjectAlmBinding(project).catch((response: Response) => {
125 if (response && response.status === HttpStatus.NotFound) {
128 return throwGlobalError(response);
134 this.setState({ saving: false });
138 handleReset = () => {
139 const { component } = this.props;
140 this.setState({ saving: true });
141 deleteProjectAlmBinding(component.key)
150 orignalData: undefined,
158 .catch(this.catchError);
161 submitProjectAlmBinding(
164 almSpecificFields?: T.Omit<FormData, 'key'>
166 const almSetting = key;
167 const project = this.props.component.key;
168 const repository = almSpecificFields?.repository;
169 const slug = almSpecificFields?.slug;
170 const monorepo = almSpecificFields?.monorepo ?? false;
173 case AlmKeys.Azure: {
174 if (!slug || !repository) {
175 return Promise.reject();
177 return setProjectAzureBinding({
181 repositoryName: repository,
185 case AlmKeys.BitbucketServer: {
186 if (!repository || !slug) {
187 return Promise.reject();
189 return setProjectBitbucketBinding({
197 case AlmKeys.BitbucketCloud: {
199 return Promise.reject();
201 return setProjectBitbucketCloudBinding({
207 case AlmKeys.GitHub: {
208 // By default it must remain true.
209 const summaryCommentEnabled =
210 almSpecificFields?.summaryCommentEnabled === undefined
212 : almSpecificFields?.summaryCommentEnabled;
214 return Promise.reject();
216 return setProjectGithubBinding({
220 summaryCommentEnabled,
225 case AlmKeys.GitLab: {
227 return Promise.reject();
229 return setProjectGitlabBinding({
238 return Promise.reject();
242 handleSubmit = () => {
243 this.setState({ saving: true });
245 formData: { key, ...additionalFields },
249 const selected = instances.find(i => i.key === key);
250 if (!key || !selected) {
254 this.submitProjectAlmBinding(selected.alm, key, additionalFields)
263 .then(this.fetchDefinitions)
264 .catch(this.catchError);
268 { key, repository = '', slug = '', summaryCommentEnabled = false, monorepo = false }: FormData,
271 repository: oRepository = '',
273 summaryCommentEnabled: osummaryCommentEnabled = false,
274 monorepo: omonorepo = false
279 repository === oRepository &&
281 summaryCommentEnabled === osummaryCommentEnabled &&
282 monorepo === omonorepo
286 handleFieldChange = (id: keyof ProjectAlmBindingResponse, value: string | boolean) => {
287 this.setState(({ formData, orignalData }) => {
288 const newFormData = {
294 formData: newFormData,
295 isValid: this.validateForm(newFormData),
296 isChanged: !this.isDataSame(newFormData, orignalData || { key: '' }),
302 validateForm = ({ key, ...additionalFields }: State['formData']) => {
303 const { instances } = this.state;
304 const selected = instances.find(i => i.key === key);
305 if (!key || !selected) {
308 return REQUIRED_FIELDS_BY_ALM[selected.alm].reduce(
309 (result: boolean, field) => result && Boolean(additionalFields[field]),
315 const { monorepoEnabled } = this.props;
318 <PRDecorationBindingRenderer
319 onFieldChange={this.handleFieldChange}
320 onReset={this.handleReset}
321 onSubmit={this.handleSubmit}
322 monorepoEnabled={monorepoEnabled}
329 const mapStateToProps = (state: Store): StateProps => ({
330 // This feature trigger will be replaced when SONAR-14349 is implemented
331 monorepoEnabled: [EditionKey.enterprise, EditionKey.datacenter].includes(
332 getAppState(state).edition as EditionKey
336 export default connect(mapStateToProps)(PRDecorationBinding);