]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19456 New code definition is made part of Bitbucket Server project onboarding
authorPhilippe Perrin <philippe.perrin@sonarsource.com>
Fri, 9 Jun 2023 09:52:38 +0000 (11:52 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 14 Jun 2023 09:51:06 +0000 (09:51 +0000)
server/sonar-web/src/main/js/api/alm-integrations.ts
server/sonar-web/src/main/js/api/mocks/AlmIntegrationsServiceMock.ts
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketImportRepositoryForm.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectAccordion.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectCreate.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectCreateRenderer.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketRepositories.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketSearchResults.tsx
server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
server/sonar-web/src/main/js/apps/create/project/__tests__/Bitbucket-it.tsx

index 451e20fbfb5eebfaf6a42966d4a8e4e613709cb2..7e81ff101ba075b9f8cbabad333637e6392a2564 100644 (file)
@@ -118,16 +118,25 @@ export function getBitbucketServerRepositories(
   });
 }
 
-export function importBitbucketServerProject(
-  almSetting: string,
-  projectKey: string,
-  repositorySlug: string
-): Promise<{ project: ProjectBase }> {
-  return postJSON('/api/alm_integrations/import_bitbucketserver_project', {
-    almSetting,
-    projectKey,
-    repositorySlug,
-  }).catch(throwGlobalError);
+export function setupBitbucketServerProjectCreation(data: {
+  almSetting: string;
+  projectKey: string;
+  repositorySlug: string;
+}) {
+  return (newCodeDefinitionType?: string, newCodeDefinitionValue?: string) =>
+    importBitbucketServerProject({ ...data, newCodeDefinitionType, newCodeDefinitionValue });
+}
+
+export function importBitbucketServerProject(data: {
+  almSetting: string;
+  projectKey: string;
+  repositorySlug: string;
+  newCodeDefinitionType?: string;
+  newCodeDefinitionValue?: string;
+}): Promise<{ project: ProjectBase }> {
+  return postJSON('/api/alm_integrations/import_bitbucketserver_project', data).catch(
+    throwGlobalError
+  );
 }
 
 export function searchForBitbucketServerRepositories(
index 0a1002c8842047e9bce3763385f8c850f7617eaa..05a6d32f6b8c0de50347456a99b27b7dcc458cdb 100644 (file)
@@ -58,6 +58,7 @@ import {
   searchForBitbucketServerRepositories,
   setAlmPersonalAccessToken,
   setupAzureProjectCreation,
+  setupBitbucketServerProjectCreation,
 } from '../alm-integrations';
 
 export default class AlmIntegrationsServiceMock {
@@ -204,6 +205,9 @@ export default class AlmIntegrationsServiceMock {
       .mocked(getBitbucketServerRepositories)
       .mockImplementation(this.getBitbucketServerRepositories);
     jest.mocked(importBitbucketServerProject).mockImplementation(this.importBitbucketServerProject);
+    jest
+      .mocked(setupBitbucketServerProjectCreation)
+      .mockReturnValue(() => this.importBitbucketServerProject());
     jest
       .mocked(searchForBitbucketServerRepositories)
       .mockImplementation(this.searchForBitbucketServerRepositories);
index 8b22d219f30cd413e36daa533937773ff1e33984..bee7ea6a447ade7e04124dd9a01b5c544f9c505f 100644 (file)
@@ -29,13 +29,11 @@ import {
   BitbucketProjectRepositories,
   BitbucketRepository,
 } from '../../../../types/alm-integration';
-import InstanceNewCodeDefinitionComplianceWarning from '../components/InstanceNewCodeDefinitionComplianceWarning';
 import { CreateProjectModes } from '../types';
 import BitbucketRepositories from './BitbucketRepositories';
 import BitbucketSearchResults from './BitbucketSearchResults';
 
 export interface BitbucketImportRepositoryFormProps {
-  disableRepositories: boolean;
   onSearch: (query: string) => void;
   onSelectRepository: (repo: BitbucketRepository) => void;
   projects?: BitbucketProject[];
@@ -47,7 +45,6 @@ export interface BitbucketImportRepositoryFormProps {
 
 export default function BitbucketImportRepositoryForm(props: BitbucketImportRepositoryFormProps) {
   const {
-    disableRepositories,
     projects = [],
     projectRepositories = {},
     searchResults,
@@ -80,8 +77,6 @@ export default function BitbucketImportRepositoryForm(props: BitbucketImportRepo
 
   return (
     <div className="create-project-import-bbs">
-      <InstanceNewCodeDefinitionComplianceWarning />
-
       <SearchBox
         onChange={props.onSearch}
         placeholder={translate('onboarding.create_project.search_repositories_by_name')}
@@ -89,7 +84,6 @@ export default function BitbucketImportRepositoryForm(props: BitbucketImportRepo
 
       {searching || searchResults ? (
         <BitbucketSearchResults
-          disableRepositories={disableRepositories}
           onSelectRepository={props.onSelectRepository}
           projects={projects}
           searchResults={searchResults}
@@ -98,7 +92,6 @@ export default function BitbucketImportRepositoryForm(props: BitbucketImportRepo
         />
       ) : (
         <BitbucketRepositories
-          disableRepositories={disableRepositories}
           onSelectRepository={props.onSelectRepository}
           projectRepositories={projectRepositories}
           projects={projects}
index df14cad4b27992246670fb67432e562a26b83b27..d6dda0e868043e15742c46722c9101c0f92006ea 100644 (file)
@@ -32,7 +32,6 @@ import { BitbucketProject, BitbucketRepository } from '../../../../types/alm-int
 import { CreateProjectModes } from '../types';
 
 export interface BitbucketProjectAccordionProps {
-  disableRepositories: boolean;
   onClick?: () => void;
   onSelectRepository: (repo: BitbucketRepository) => void;
   open: boolean;
@@ -43,14 +42,7 @@ export interface BitbucketProjectAccordionProps {
 }
 
 export default function BitbucketProjectAccordion(props: BitbucketProjectAccordionProps) {
-  const {
-    disableRepositories,
-    open,
-    project,
-    repositories,
-    selectedRepository,
-    showingAllRepositories,
-  } = props;
+  const { open, project, repositories, selectedRepository, showingAllRepositories } = props;
 
   const repositoryCount = repositories.length;
 
@@ -119,12 +111,7 @@ export default function BitbucketProjectAccordion(props: BitbucketProjectAccordi
               ) : (
                 <Radio
                   checked={selectedRepository?.id === repo.id}
-                  className={classNames(
-                    'display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo overflow-hidden',
-                    {
-                      disabled: disableRepositories,
-                    }
-                  )}
+                  className="display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo overflow-hidden"
                   key={repo.id}
                   onCheck={() => props.onSelectRepository(repo)}
                   value={String(repo.id)}
index 49f19542cb18f4e5f43c42b30c53d658a4b9217e..0613375131aa10bfc29fe16031cef91c41e604c6 100644 (file)
@@ -21,8 +21,8 @@ import * as React from 'react';
 import {
   getBitbucketServerProjects,
   getBitbucketServerRepositories,
-  importBitbucketServerProject,
   searchForBitbucketServerRepositories,
+  setupBitbucketServerProjectCreation,
 } from '../../../../api/alm-integrations';
 import { Location, Router } from '../../../../components/hoc/withRouter';
 import {
@@ -32,20 +32,20 @@ import {
 } from '../../../../types/alm-integration';
 import { AlmSettingsInstance } from '../../../../types/alm-settings';
 import { DEFAULT_BBS_PAGE_SIZE } from '../constants';
+import { CreateProjectApiCallback } from '../types';
 import BitbucketCreateProjectRenderer from './BitbucketProjectCreateRenderer';
 
 interface Props {
   canAdmin: boolean;
   almInstances: AlmSettingsInstance[];
   loadingBindings: boolean;
-  onProjectCreate: (projectKey: string) => void;
   location: Location;
   router: Router;
+  onProjectSetupDone: (createProject: CreateProjectApiCallback) => void;
 }
 
 interface State {
   selectedAlmInstance?: AlmSettingsInstance;
-  importing: boolean;
   loading: boolean;
   projects?: BitbucketProject[];
   projectRepositories?: BitbucketProjectRepositories;
@@ -61,10 +61,7 @@ export default class BitbucketProjectCreate extends React.PureComponent<Props, S
   constructor(props: Props) {
     super(props);
     this.state = {
-      // For now, we only handle a single instance. So we always use the first
-      // one from the list.
       selectedAlmInstance: props.almInstances[0],
-      importing: false,
       loading: false,
       searching: false,
       showPersonalAccessTokenForm: true,
@@ -187,27 +184,15 @@ export default class BitbucketProjectCreate extends React.PureComponent<Props, S
   handleImportRepository = () => {
     const { selectedAlmInstance, selectedRepository } = this.state;
 
-    if (!selectedAlmInstance || !selectedRepository) {
-      return;
+    if (selectedAlmInstance && selectedRepository) {
+      this.props.onProjectSetupDone(
+        setupBitbucketServerProjectCreation({
+          almSetting: selectedAlmInstance.key,
+          projectKey: selectedRepository.projectKey,
+          repositorySlug: selectedRepository.slug,
+        })
+      );
     }
-
-    this.setState({ importing: true });
-    importBitbucketServerProject(
-      selectedAlmInstance.key,
-      selectedRepository.projectKey,
-      selectedRepository.slug
-    )
-      .then(({ project: { key } }) => {
-        if (this.mounted) {
-          this.setState({ importing: false });
-          this.props.onProjectCreate(key);
-        }
-      })
-      .catch(() => {
-        if (this.mounted) {
-          this.setState({ importing: false });
-        }
-      });
   };
 
   handleSearch = (query: string) => {
@@ -253,7 +238,6 @@ export default class BitbucketProjectCreate extends React.PureComponent<Props, S
     const { canAdmin, loadingBindings, location, almInstances } = this.props;
     const {
       selectedAlmInstance,
-      importing,
       loading,
       projectRepositories,
       projects,
@@ -268,7 +252,6 @@ export default class BitbucketProjectCreate extends React.PureComponent<Props, S
         selectedAlmInstance={selectedAlmInstance}
         almInstances={almInstances}
         canAdmin={canAdmin}
-        importing={importing}
         loading={loading || loadingBindings}
         onImportRepository={this.handleImportRepository}
         onPersonalAccessTokenCreated={this.handlePersonalAccessTokenCreated}
index 679c30c06e5b481f40c789997ac06b53b7ecfcb3..426079c2836dedbc4f33859658ac205340d4dc39 100644 (file)
@@ -19,7 +19,6 @@
  */
 import * as React from 'react';
 import { Button } from '../../../../components/controls/buttons';
-import DeferredSpinner from '../../../../components/ui/DeferredSpinner';
 import { translate } from '../../../../helpers/l10n';
 import { getBaseUrl } from '../../../../helpers/system';
 import {
@@ -38,7 +37,6 @@ export interface BitbucketProjectCreateRendererProps {
   selectedAlmInstance?: AlmSettingsInstance;
   almInstances: AlmSettingsInstance[];
   canAdmin?: boolean;
-  importing: boolean;
   loading: boolean;
   onImportRepository: () => void;
   onSearch: (query: string) => void;
@@ -59,7 +57,6 @@ export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCr
     almInstances,
     selectedAlmInstance,
     canAdmin,
-    importing,
     loading,
     projects,
     projectRepositories,
@@ -76,10 +73,9 @@ export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCr
         additionalActions={
           !showPersonalAccessTokenForm && (
             <div className="display-flex-center pull-right">
-              <DeferredSpinner className="spacer-right" loading={importing} />
               <Button
                 className="button-large button-primary"
-                disabled={!selectedRepository || importing}
+                disabled={!selectedRepository}
                 onClick={props.onImportRepository}
               >
                 {translate('onboarding.create_project.import_selected_repo')}
@@ -123,7 +119,6 @@ export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCr
           />
         ) : (
           <BitbucketImportRepositoryForm
-            disableRepositories={importing}
             onSearch={props.onSearch}
             onSelectRepository={props.onSelectRepository}
             projectRepositories={projectRepositories}
index a7a225b510b9e12bae9465a6ca9757ac017793e1..ef53100e336a9e03144202bd0f5f9eb26e4d51ee 100644 (file)
@@ -29,7 +29,6 @@ import {
 import BitbucketProjectAccordion from './BitbucketProjectAccordion';
 
 export interface BitbucketRepositoriesProps {
-  disableRepositories: boolean;
   onSelectRepository: (repo: BitbucketRepository) => void;
   projects: BitbucketProject[];
   projectRepositories: BitbucketProjectRepositories;
@@ -37,7 +36,7 @@ export interface BitbucketRepositoriesProps {
 }
 
 export default function BitbucketRepositories(props: BitbucketRepositoriesProps) {
-  const { disableRepositories, projects, projectRepositories, selectedRepository } = props;
+  const { projects, projectRepositories, selectedRepository } = props;
 
   const [openProjectKeys, setOpenProjectKeys] = React.useState(
     projects.length > 0 ? [projects[0].key] : []
@@ -68,7 +67,6 @@ export default function BitbucketRepositories(props: BitbucketRepositoriesProps)
 
         return (
           <BitbucketProjectAccordion
-            disableRepositories={disableRepositories}
             key={project.key}
             onClick={() => handleClick(isOpen, project.key)}
             onSelectRepository={props.onSelectRepository}
index 8ac22fa3bd436962b23df631f069cbfe651ec06b..a72b1d95e502600dd0c3e540e3eed919c1b2321c 100644 (file)
@@ -25,7 +25,6 @@ import { BitbucketProject, BitbucketRepository } from '../../../../types/alm-int
 import BitbucketProjectAccordion from './BitbucketProjectAccordion';
 
 export interface BitbucketSearchResultsProps {
-  disableRepositories: boolean;
   onSelectRepository: (repo: BitbucketRepository) => void;
   projects: BitbucketProject[];
   searching: boolean;
@@ -34,13 +33,7 @@ export interface BitbucketSearchResultsProps {
 }
 
 export default function BitbucketSearchResults(props: BitbucketSearchResultsProps) {
-  const {
-    disableRepositories,
-    projects,
-    searching,
-    searchResults = [],
-    selectedRepository,
-  } = props;
+  const { projects, searching, searchResults = [], selectedRepository } = props;
 
   if (searchResults.length === 0 && !searching) {
     return (
@@ -63,7 +56,6 @@ export default function BitbucketSearchResults(props: BitbucketSearchResultsProp
       <DeferredSpinner loading={searching}>
         {filteredSearchResults.length > 0 && (
           <BitbucketProjectAccordion
-            disableRepositories={disableRepositories}
             onSelectRepository={props.onSelectRepository}
             open
             repositories={filteredSearchResults}
@@ -77,7 +69,6 @@ export default function BitbucketSearchResults(props: BitbucketSearchResultsProp
 
           return (
             <BitbucketProjectAccordion
-              disableRepositories={disableRepositories}
               key={project.key}
               onSelectRepository={props.onSelectRepository}
               open
index 7ab2f68ee7c5705e62feed1c6ac644d17853cb99..2f0734b648127df294a3db149de13b067a5f8343 100644 (file)
@@ -220,8 +220,8 @@ export class CreateProjectPage extends React.PureComponent<CreateProjectPageProp
             almInstances={bitbucketSettings}
             loadingBindings={loading}
             location={location}
-            onProjectCreate={this.handleProjectCreate}
             router={router}
+            onProjectSetupDone={this.handleProjectSetupDone}
           />
         );
       }
index 0112268c36fda5cd70f16f2e1500295c206f6c88..d980905f2e9c4092a0036eced53e98eabcbc2a95 100644 (file)
@@ -125,6 +125,18 @@ it('should show import project feature when PAT is already set', async () => {
   await user.click(radioButton);
   expect(importButton).toBeEnabled();
   await user.click(importButton);
+
+  expect(
+    screen.getByRole('heading', { name: 'onboarding.create_project.new_code_definition.title' })
+  ).toBeInTheDocument();
+
+  await user.click(screen.getByRole('radio', { name: 'new_code_definition.global_setting' }));
+  await user.click(
+    screen.getByRole('button', {
+      name: 'onboarding.create_project.new_code_definition.create_project',
+    })
+  );
+
   expect(await screen.findByText('/dashboard?id=key')).toBeInTheDocument();
 });