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.3KB

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