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 setProjectGithubBinding,
30 setProjectGitlabBinding
31 } from '../../../../api/alm-settings';
32 import throwGlobalError from '../../../../app/utils/throwGlobalError';
33 import { getAppState, Store } from '../../../../store/rootReducer';
37 ProjectAlmBindingResponse
38 } from '../../../../types/alm-settings';
39 import { EditionKey } from '../../../../types/editions';
40 import PRDecorationBindingRenderer from './PRDecorationBindingRenderer';
42 type FormData = T.Omit<ProjectAlmBindingResponse, 'alm'>;
44 interface StateProps {
45 monorepoEnabled: boolean;
49 component: T.Component;
54 instances: AlmSettingsInstance[];
56 isConfigured: boolean;
59 orignalData?: FormData;
64 const REQUIRED_FIELDS_BY_ALM: {
65 [almKey in AlmKeys]: Array<keyof T.Omit<FormData, 'key'>>;
67 [AlmKeys.Azure]: ['repository', 'slug'],
68 [AlmKeys.Bitbucket]: ['repository', 'slug'],
69 [AlmKeys.GitHub]: ['repository'],
70 [AlmKeys.GitLab]: ['repository']
73 export class PRDecorationBinding extends React.PureComponent<Props & StateProps, State> {
76 formData: { key: '' },
88 this.fetchDefinitions();
91 componentWillUnmount() {
95 fetchDefinitions = () => {
96 const project = this.props.component.key;
97 return Promise.all([getAlmSettings(project), this.getProjectBinding(project)])
98 .then(([instances, originalData]) => {
100 this.setState(({ formData }) => {
101 const newFormData = originalData || formData;
103 formData: newFormData,
104 instances: instances || [],
106 isConfigured: !!originalData,
107 isValid: this.validateForm(newFormData),
109 orignalData: newFormData
116 this.setState({ loading: false });
121 getProjectBinding(project: string): Promise<ProjectAlmBindingResponse | undefined> {
122 return getProjectAlmBinding(project).catch((response: Response) => {
123 if (response && response.status === HttpStatus.NotFound) {
126 return throwGlobalError(response);
132 this.setState({ saving: false });
136 handleReset = () => {
137 const { component } = this.props;
138 this.setState({ saving: true });
139 deleteProjectAlmBinding(component.key)
148 orignalData: undefined,
156 .catch(this.catchError);
159 submitProjectAlmBinding(
162 almSpecificFields?: T.Omit<FormData, 'key'>
164 const almSetting = key;
165 const project = this.props.component.key;
166 const repository = almSpecificFields?.repository;
167 const slug = almSpecificFields?.slug;
168 const monorepo = almSpecificFields?.monorepo ?? false;
171 case AlmKeys.Azure: {
172 if (!slug || !repository) {
173 return Promise.reject();
175 return setProjectAzureBinding({
179 repositoryName: repository,
183 case AlmKeys.Bitbucket: {
184 if (!repository || !slug) {
185 return Promise.reject();
187 return setProjectBitbucketBinding({
195 case AlmKeys.GitHub: {
196 // By default it must remain true.
197 const summaryCommentEnabled =
198 almSpecificFields?.summaryCommentEnabled === undefined
200 : almSpecificFields?.summaryCommentEnabled;
202 return Promise.reject();
204 return setProjectGithubBinding({
208 summaryCommentEnabled,
213 case AlmKeys.GitLab: {
215 return Promise.reject();
217 return setProjectGitlabBinding({
226 return Promise.reject();
230 handleSubmit = () => {
231 this.setState({ saving: true });
233 formData: { key, ...additionalFields },
237 const selected = instances.find(i => i.key === key);
238 if (!key || !selected) {
242 this.submitProjectAlmBinding(selected.alm, key, additionalFields)
251 .then(this.fetchDefinitions)
252 .catch(this.catchError);
256 { key, repository = '', slug = '', summaryCommentEnabled = false, monorepo = false }: FormData,
259 repository: oRepository = '',
261 summaryCommentEnabled: osummaryCommentEnabled = false,
262 monorepo: omonorepo = false
267 repository === oRepository &&
269 summaryCommentEnabled === osummaryCommentEnabled &&
270 monorepo === omonorepo
274 handleFieldChange = (id: keyof ProjectAlmBindingResponse, value: string | boolean) => {
275 this.setState(({ formData, orignalData }) => {
276 const newFormData = {
282 formData: newFormData,
283 isValid: this.validateForm(newFormData),
284 isChanged: !this.isDataSame(newFormData, orignalData || { key: '' }),
290 validateForm = ({ key, ...additionalFields }: State['formData']) => {
291 const { instances } = this.state;
292 const selected = instances.find(i => i.key === key);
293 if (!key || !selected) {
296 return REQUIRED_FIELDS_BY_ALM[selected.alm].reduce(
297 (result: boolean, field) => result && Boolean(additionalFields[field]),
303 const { monorepoEnabled } = this.props;
306 <PRDecorationBindingRenderer
307 onFieldChange={this.handleFieldChange}
308 onReset={this.handleReset}
309 onSubmit={this.handleSubmit}
310 monorepoEnabled={monorepoEnabled}
317 const mapStateToProps = (state: Store): StateProps => ({
318 // This feature trigger will be replaced when SONAR-14349 is implemented
319 monorepoEnabled: [EditionKey.enterprise, EditionKey.datacenter].includes(
320 getAppState(state).edition as EditionKey
324 export default connect(mapStateToProps)(PRDecorationBinding);