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;
168 case AlmKeys.Azure: {
169 const projectName = almSpecificFields?.slug;
170 const repositoryName = almSpecificFields?.repository;
171 const monorepo = almSpecificFields?.monorepo ?? false;
172 if (!projectName || !repositoryName) {
173 return Promise.reject();
175 return setProjectAzureBinding({
183 case AlmKeys.Bitbucket: {
184 const repository = almSpecificFields?.repository;
185 const slug = almSpecificFields?.slug;
186 const monorepo = almSpecificFields?.monorepo ?? false;
187 if (!repository || !slug) {
188 return Promise.reject();
190 return setProjectBitbucketBinding({
198 case AlmKeys.GitHub: {
199 const repository = almSpecificFields?.repository;
200 // By default it must remain true.
201 const summaryCommentEnabled =
202 almSpecificFields?.summaryCommentEnabled === undefined
204 : almSpecificFields?.summaryCommentEnabled;
205 const monorepo = almSpecificFields?.monorepo ?? false;
207 return Promise.reject();
209 return setProjectGithubBinding({
213 summaryCommentEnabled,
218 case AlmKeys.GitLab: {
219 const repository = almSpecificFields && almSpecificFields.repository;
220 return setProjectGitlabBinding({
228 return Promise.reject();
232 handleSubmit = () => {
233 this.setState({ saving: true });
235 formData: { key, ...additionalFields },
239 const selected = instances.find(i => i.key === key);
240 if (!key || !selected) {
244 this.submitProjectAlmBinding(selected.alm, key, additionalFields)
253 .then(this.fetchDefinitions)
254 .catch(this.catchError);
258 { key, repository = '', slug = '', summaryCommentEnabled = false, monorepo = false }: FormData,
261 repository: oRepository = '',
263 summaryCommentEnabled: osummaryCommentEnabled = false,
264 monorepo: omonorepo = false
269 repository === oRepository &&
271 summaryCommentEnabled === osummaryCommentEnabled &&
272 monorepo === omonorepo
276 handleFieldChange = (id: keyof ProjectAlmBindingResponse, value: string | boolean) => {
277 this.setState(({ formData, orignalData }) => {
278 const newFormData = {
284 formData: newFormData,
285 isValid: this.validateForm(newFormData),
286 isChanged: !this.isDataSame(newFormData, orignalData || { key: '' }),
292 validateForm = ({ key, ...additionalFields }: State['formData']) => {
293 const { instances } = this.state;
294 const selected = instances.find(i => i.key === key);
295 if (!key || !selected) {
298 return REQUIRED_FIELDS_BY_ALM[selected.alm].reduce(
299 (result: boolean, field) => result && Boolean(additionalFields[field]),
305 const { monorepoEnabled } = this.props;
308 <PRDecorationBindingRenderer
309 onFieldChange={this.handleFieldChange}
310 onReset={this.handleReset}
311 onSubmit={this.handleSubmit}
312 monorepoEnabled={monorepoEnabled}
319 const mapStateToProps = (state: Store): StateProps => ({
320 // This feature trigger will be replaced when SONAR-14349 is implemented
321 monorepoEnabled: [EditionKey.enterprise, EditionKey.datacenter].includes(
322 getAppState(state).edition as EditionKey
326 export default connect(mapStateToProps)(PRDecorationBinding);