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.

GitlabProjectCreate.tsx 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 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 { getGitlabProjects } from '../../../../api/alm-integrations';
  22. import { Location, Router } from '../../../../components/hoc/withRouter';
  23. import { GitlabProject } from '../../../../types/alm-integration';
  24. import { AlmSettingsInstance } from '../../../../types/alm-settings';
  25. import { Paging } from '../../../../types/types';
  26. import { ImportProjectParam } from '../CreateProjectPage';
  27. import { CreateProjectModes } from '../types';
  28. import GitlabProjectCreateRenderer from './GitlabProjectCreateRenderer';
  29. interface Props {
  30. canAdmin: boolean;
  31. loadingBindings: boolean;
  32. almInstances: AlmSettingsInstance[];
  33. location: Location;
  34. router: Router;
  35. onProjectSetupDone: (importProjects: ImportProjectParam) => void;
  36. }
  37. interface State {
  38. loading: boolean;
  39. loadingMore: boolean;
  40. projects?: GitlabProject[];
  41. projectsPaging: Paging;
  42. resetPat: boolean;
  43. searching: boolean;
  44. searchQuery: string;
  45. selectedAlmInstance: AlmSettingsInstance;
  46. showPersonalAccessTokenForm: boolean;
  47. }
  48. const GITLAB_PROJECTS_PAGESIZE = 20;
  49. export default class GitlabProjectCreate extends React.PureComponent<Props, State> {
  50. mounted = false;
  51. constructor(props: Props) {
  52. super(props);
  53. this.state = {
  54. loading: false,
  55. loadingMore: false,
  56. projectsPaging: { pageIndex: 1, total: 0, pageSize: GITLAB_PROJECTS_PAGESIZE },
  57. resetPat: false,
  58. showPersonalAccessTokenForm: true,
  59. searching: false,
  60. searchQuery: '',
  61. selectedAlmInstance: props.almInstances[0],
  62. };
  63. }
  64. componentDidMount() {
  65. this.mounted = true;
  66. }
  67. componentDidUpdate(prevProps: Props) {
  68. const { almInstances } = this.props;
  69. if (prevProps.almInstances.length === 0 && this.props.almInstances.length > 0) {
  70. this.setState({ selectedAlmInstance: almInstances[0] }, () => {
  71. this.fetchInitialData().catch(() => {
  72. /* noop */
  73. });
  74. });
  75. }
  76. }
  77. componentWillUnmount() {
  78. this.mounted = false;
  79. }
  80. fetchInitialData = async () => {
  81. const { showPersonalAccessTokenForm } = this.state;
  82. if (!showPersonalAccessTokenForm) {
  83. this.setState({ loading: true });
  84. const result = await this.fetchProjects();
  85. if (this.mounted && result) {
  86. const { projects, projectsPaging } = result;
  87. this.setState({
  88. loading: false,
  89. projects,
  90. projectsPaging,
  91. });
  92. } else {
  93. this.setState({
  94. loading: false,
  95. });
  96. }
  97. }
  98. };
  99. handleError = () => {
  100. if (this.mounted) {
  101. this.setState({ resetPat: true, showPersonalAccessTokenForm: true });
  102. }
  103. return undefined;
  104. };
  105. fetchProjects = async (pageIndex = 1, query?: string) => {
  106. const { selectedAlmInstance } = this.state;
  107. if (!selectedAlmInstance) {
  108. return Promise.resolve(undefined);
  109. }
  110. try {
  111. // eslint-disable-next-line local-rules/no-api-imports
  112. return await getGitlabProjects({
  113. almSetting: selectedAlmInstance.key,
  114. page: pageIndex,
  115. pageSize: GITLAB_PROJECTS_PAGESIZE,
  116. query,
  117. });
  118. } catch (_) {
  119. return this.handleError();
  120. }
  121. };
  122. handleImport = (gitlabProjectId: string) => {
  123. const { selectedAlmInstance } = this.state;
  124. if (selectedAlmInstance) {
  125. this.props.onProjectSetupDone({
  126. creationMode: CreateProjectModes.GitLab,
  127. almSetting: selectedAlmInstance.key,
  128. monorepo: false,
  129. projects: [{ gitlabProjectId }],
  130. });
  131. }
  132. };
  133. handleLoadMore = async () => {
  134. this.setState({ loadingMore: true });
  135. const {
  136. projectsPaging: { pageIndex },
  137. searchQuery,
  138. } = this.state;
  139. const result = await this.fetchProjects(pageIndex + 1, searchQuery);
  140. if (this.mounted) {
  141. this.setState(({ projects = [], projectsPaging }) => ({
  142. loadingMore: false,
  143. projects: result ? [...projects, ...result.projects] : projects,
  144. projectsPaging: result ? result.projectsPaging : projectsPaging,
  145. }));
  146. }
  147. };
  148. handleSearch = async (searchQuery: string) => {
  149. this.setState({ searching: true, searchQuery });
  150. const result = await this.fetchProjects(1, searchQuery);
  151. if (this.mounted) {
  152. this.setState(({ projects, projectsPaging }) => ({
  153. searching: false,
  154. projects: result ? result.projects : projects,
  155. projectsPaging: result ? result.projectsPaging : projectsPaging,
  156. }));
  157. }
  158. };
  159. cleanUrl = () => {
  160. const { location, router } = this.props;
  161. delete location.query.resetPat;
  162. router.replace(location);
  163. };
  164. handlePersonalAccessTokenCreated = () => {
  165. this.cleanUrl();
  166. this.setState({ showPersonalAccessTokenForm: false, resetPat: false }, () => {
  167. this.fetchInitialData();
  168. });
  169. };
  170. onSelectedAlmInstanceChange = (instance: AlmSettingsInstance) => {
  171. this.setState({
  172. selectedAlmInstance: instance,
  173. showPersonalAccessTokenForm: true,
  174. projects: undefined,
  175. resetPat: false,
  176. searchQuery: '',
  177. });
  178. };
  179. render() {
  180. const { loadingBindings, location, almInstances, canAdmin } = this.props;
  181. const {
  182. loading,
  183. loadingMore,
  184. projects,
  185. projectsPaging,
  186. resetPat,
  187. searching,
  188. searchQuery,
  189. selectedAlmInstance,
  190. showPersonalAccessTokenForm,
  191. } = this.state;
  192. return (
  193. <GitlabProjectCreateRenderer
  194. canAdmin={canAdmin}
  195. almInstances={almInstances}
  196. selectedAlmInstance={selectedAlmInstance}
  197. loading={loading || loadingBindings}
  198. loadingMore={loadingMore}
  199. onImport={this.handleImport}
  200. onLoadMore={this.handleLoadMore}
  201. onPersonalAccessTokenCreated={this.handlePersonalAccessTokenCreated}
  202. onSearch={this.handleSearch}
  203. projects={projects}
  204. projectsPaging={projectsPaging}
  205. resetPat={resetPat || Boolean(location.query.resetPat)}
  206. searching={searching}
  207. searchQuery={searchQuery}
  208. showPersonalAccessTokenForm={
  209. showPersonalAccessTokenForm || Boolean(location.query.resetPat)
  210. }
  211. onSelectedAlmInstanceChange={this.onSelectedAlmInstanceChange}
  212. />
  213. );
  214. }
  215. }