You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AlmIntegration.tsx 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2022 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. countBindedProjects,
  23. deleteConfiguration,
  24. getAlmDefinitions,
  25. validateAlmSettings
  26. } from '../../../../api/alm-settings';
  27. import withAppStateContext from '../../../../app/components/app-state/withAppStateContext';
  28. import { Location, Router, withRouter } from '../../../../components/hoc/withRouter';
  29. import {
  30. AlmBindingDefinitionBase,
  31. AlmKeys,
  32. AlmSettingsBindingDefinitions,
  33. AlmSettingsBindingStatus,
  34. AlmSettingsBindingStatusType
  35. } from '../../../../types/alm-settings';
  36. import { AppState } from '../../../../types/appstate';
  37. import { Dict } from '../../../../types/types';
  38. import AlmIntegrationRenderer from './AlmIntegrationRenderer';
  39. interface Props {
  40. appState: AppState;
  41. location: Location;
  42. router: Router;
  43. }
  44. export type AlmTabs = AlmKeys.Azure | AlmKeys.GitHub | AlmKeys.GitLab | AlmKeys.BitbucketServer;
  45. interface State {
  46. currentAlmTab: AlmTabs;
  47. definitionKeyForDeletion?: string;
  48. definitions: AlmSettingsBindingDefinitions;
  49. definitionStatus: Dict<AlmSettingsBindingStatus>;
  50. loadingAlmDefinitions: boolean;
  51. loadingProjectCount: boolean;
  52. projectCount?: number;
  53. }
  54. export class AlmIntegration extends React.PureComponent<Props, State> {
  55. mounted = false;
  56. state: State;
  57. constructor(props: Props) {
  58. super(props);
  59. let currentAlmTab = props.location.query.alm || AlmKeys.GitHub;
  60. if (currentAlmTab === AlmKeys.BitbucketCloud) {
  61. currentAlmTab = AlmKeys.BitbucketServer;
  62. }
  63. this.state = {
  64. currentAlmTab,
  65. definitions: {
  66. [AlmKeys.Azure]: [],
  67. [AlmKeys.BitbucketServer]: [],
  68. [AlmKeys.BitbucketCloud]: [],
  69. [AlmKeys.GitHub]: [],
  70. [AlmKeys.GitLab]: []
  71. },
  72. definitionStatus: {},
  73. loadingAlmDefinitions: true,
  74. loadingProjectCount: false
  75. };
  76. }
  77. componentDidMount() {
  78. this.mounted = true;
  79. return this.fetchPullRequestDecorationSetting().then(definitions => {
  80. if (definitions) {
  81. // Validate all alms on load:
  82. [
  83. AlmKeys.Azure,
  84. AlmKeys.BitbucketCloud,
  85. AlmKeys.BitbucketServer,
  86. AlmKeys.GitHub,
  87. AlmKeys.GitLab
  88. ].forEach(alm => {
  89. this.state.definitions[alm].forEach((def: AlmBindingDefinitionBase) =>
  90. this.handleCheck(def.key, false)
  91. );
  92. });
  93. }
  94. });
  95. }
  96. componentDidUpdate() {
  97. const { location } = this.props;
  98. if (location.query.alm && this.mounted) {
  99. this.setState({ currentAlmTab: location.query.alm });
  100. }
  101. }
  102. componentWillUnmount() {
  103. this.mounted = false;
  104. }
  105. handleConfirmDelete = (definitionKey: string) => {
  106. return deleteConfiguration(definitionKey)
  107. .then(() => {
  108. if (this.mounted) {
  109. this.setState({ definitionKeyForDeletion: undefined, projectCount: undefined });
  110. }
  111. })
  112. .then(this.fetchPullRequestDecorationSetting);
  113. };
  114. fetchPullRequestDecorationSetting = () => {
  115. this.setState({ loadingAlmDefinitions: true });
  116. return getAlmDefinitions()
  117. .then(definitions => {
  118. if (this.mounted) {
  119. this.setState({
  120. definitions,
  121. loadingAlmDefinitions: false
  122. });
  123. return definitions;
  124. }
  125. })
  126. .catch(() => {
  127. if (this.mounted) {
  128. this.setState({ loadingAlmDefinitions: false });
  129. }
  130. });
  131. };
  132. handleSelectAlm = (currentAlmTab: AlmTabs) => {
  133. const { location, router } = this.props;
  134. location.query.alm = currentAlmTab;
  135. location.hash = '';
  136. router.push(location);
  137. this.setState({ currentAlmTab });
  138. };
  139. handleCancelDelete = () => {
  140. this.setState({ definitionKeyForDeletion: undefined, projectCount: undefined });
  141. };
  142. handleDelete = (definitionKey: string) => {
  143. this.setState({ loadingProjectCount: true });
  144. return countBindedProjects(definitionKey)
  145. .then(projectCount => {
  146. if (this.mounted) {
  147. this.setState({
  148. definitionKeyForDeletion: definitionKey,
  149. loadingProjectCount: false,
  150. projectCount
  151. });
  152. }
  153. })
  154. .catch(() => {
  155. if (this.mounted) {
  156. this.setState({ loadingProjectCount: false });
  157. }
  158. });
  159. };
  160. handleCheck = async (definitionKey: string, alertSuccess = true) => {
  161. this.setState(({ definitionStatus }) => {
  162. definitionStatus[definitionKey] = {
  163. ...definitionStatus[definitionKey],
  164. type: AlmSettingsBindingStatusType.Validating
  165. };
  166. return { definitionStatus: { ...definitionStatus } };
  167. });
  168. let type: AlmSettingsBindingStatusType;
  169. let failureMessage = '';
  170. try {
  171. failureMessage = await validateAlmSettings(definitionKey);
  172. type = failureMessage
  173. ? AlmSettingsBindingStatusType.Failure
  174. : AlmSettingsBindingStatusType.Success;
  175. } catch (_) {
  176. type = AlmSettingsBindingStatusType.Warning;
  177. }
  178. if (this.mounted) {
  179. this.setState(({ definitionStatus }) => {
  180. definitionStatus[definitionKey] = {
  181. alertSuccess,
  182. failureMessage,
  183. type
  184. };
  185. return { definitionStatus: { ...definitionStatus } };
  186. });
  187. }
  188. };
  189. render() {
  190. const {
  191. appState: { branchesEnabled, multipleAlmEnabled }
  192. } = this.props;
  193. const {
  194. currentAlmTab,
  195. definitionKeyForDeletion,
  196. definitions,
  197. definitionStatus,
  198. loadingAlmDefinitions,
  199. loadingProjectCount,
  200. projectCount
  201. } = this.state;
  202. return (
  203. <AlmIntegrationRenderer
  204. branchesEnabled={Boolean(branchesEnabled)}
  205. multipleAlmEnabled={Boolean(multipleAlmEnabled)}
  206. onCancelDelete={this.handleCancelDelete}
  207. onConfirmDelete={this.handleConfirmDelete}
  208. onCheckConfiguration={this.handleCheck}
  209. onDelete={this.handleDelete}
  210. onSelectAlmTab={this.handleSelectAlm}
  211. onUpdateDefinitions={this.fetchPullRequestDecorationSetting}
  212. currentAlmTab={currentAlmTab}
  213. definitionKeyForDeletion={definitionKeyForDeletion}
  214. definitions={definitions}
  215. definitionStatus={definitionStatus}
  216. loadingAlmDefinitions={loadingAlmDefinitions}
  217. loadingProjectCount={loadingProjectCount}
  218. projectCount={projectCount}
  219. />
  220. );
  221. }
  222. }
  223. export default withRouter(withAppStateContext(AlmIntegration));