]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19457 New code definition is made part of Bitbucket Cloud project onboarding
authorPhilippe Perrin <philippe.perrin@sonarsource.com>
Fri, 9 Jun 2023 15:21:05 +0000 (17:21 +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/BitbucketCloud/BitbucketCloudProjectCreate.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudProjectCreateRender.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudSearchForm.tsx
server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketCloud-it.tsx

index 7e81ff101ba075b9f8cbabad333637e6392a2564..f33562053c0a51d8eb4a0723511ca32559eb05df 100644 (file)
@@ -173,14 +173,21 @@ export function getGithubClientId(almSetting: string): Promise<{ clientId?: stri
   return getJSON('/api/alm_integrations/get_github_client_id', { almSetting });
 }
 
-export function importBitbucketCloudRepository(
-  almSetting: string,
-  repositorySlug: string
-): Promise<{ project: ProjectBase }> {
-  return postJSON('/api/alm_integrations/import_bitbucketcloud_repo', {
-    almSetting,
-    repositorySlug,
-  }).catch(throwGlobalError);
+export function setupBitbucketCloudProjectCreation(data: {
+  almSetting: string;
+  repositorySlug: string;
+}) {
+  return (newCodeDefinitionType?: string, newCodeDefinitionValue?: string) =>
+    importBitbucketCloudRepository({ ...data, newCodeDefinitionType, newCodeDefinitionValue });
+}
+
+export function importBitbucketCloudRepository(data: {
+  almSetting: string;
+  repositorySlug: string;
+  newCodeDefinitionType?: string;
+  newCodeDefinitionValue?: string;
+}): Promise<{ project: ProjectBase }> {
+  return postJSON('/api/alm_integrations/import_bitbucketcloud_repo', data).catch(throwGlobalError);
 }
 
 export function importGithubRepository(
index 05a6d32f6b8c0de50347456a99b27b7dcc458cdb..59c1c9b30118602f601d714aab114b0e1ebfaa72 100644 (file)
@@ -58,6 +58,7 @@ import {
   searchForBitbucketServerRepositories,
   setAlmPersonalAccessToken,
   setupAzureProjectCreation,
+  setupBitbucketCloudProjectCreation,
   setupBitbucketServerProjectCreation,
 } from '../alm-integrations';
 
@@ -187,6 +188,7 @@ export default class AlmIntegrationsServiceMock {
     jest.mocked(setAlmPersonalAccessToken).mockImplementation(this.setAlmPersonalAccessToken);
     jest.mocked(getGitlabProjects).mockImplementation(this.getGitlabProjects);
     jest.mocked(importGitlabProject).mockImplementation(this.importProject);
+    jest.mocked(setupBitbucketCloudProjectCreation).mockReturnValue(() => this.importProject());
     jest.mocked(importBitbucketCloudRepository).mockImplementation(this.importProject);
     jest.mocked(getGithubClientId).mockImplementation(this.getGithubClientId);
     jest.mocked(getGithubOrganizations).mockImplementation(this.getGithubOrganizations);
index 41adf4c9baa8ddd5bb468c6b2d59a7bdb0a3de93..0a4a2baf20c2f51ab000ff4521306d86475ffa6c 100644 (file)
  */
 import * as React from 'react';
 import {
-  importBitbucketCloudRepository,
   searchForBitbucketCloudRepositories,
+  setupBitbucketCloudProjectCreation,
 } from '../../../../api/alm-integrations';
 import { Location, Router } from '../../../../components/hoc/withRouter';
 import { BitbucketCloudRepository } from '../../../../types/alm-integration';
 import { AlmSettingsInstance } from '../../../../types/alm-settings';
 import { Paging } from '../../../../types/types';
+import { CreateProjectApiCallback } from '../types';
 import BitbucketCloudProjectCreateRenderer from './BitbucketCloudProjectCreateRender';
 
 interface Props {
   canAdmin: boolean;
   almInstances: AlmSettingsInstance[];
   loadingBindings: boolean;
-  onProjectCreate: (projectKey: string) => void;
   location: Location;
   router: Router;
+  onProjectSetupDone: (createProject: CreateProjectApiCallback) => void;
 }
 
 interface State {
-  importingSlug?: string;
   isLastPage?: boolean;
   loading: boolean;
   loadingMore: boolean;
@@ -189,26 +189,16 @@ export default class BitbucketCloudProjectCreate extends React.PureComponent<Pro
     );
   };
 
-  handleImport = async (repositorySlug: string) => {
+  handleImport = (repositorySlug: string) => {
     const { selectedAlmInstance } = this.state;
 
-    if (!selectedAlmInstance) {
-      return;
-    }
-
-    this.setState({ importingSlug: repositorySlug });
-
-    const result = await importBitbucketCloudRepository(
-      selectedAlmInstance.key,
-      repositorySlug
-    ).catch(() => undefined);
-
-    if (this.mounted) {
-      this.setState({ importingSlug: undefined });
-
-      if (result) {
-        this.props.onProjectCreate(result.project.key);
-      }
+    if (selectedAlmInstance) {
+      this.props.onProjectSetupDone(
+        setupBitbucketCloudProjectCreation({
+          almSetting: selectedAlmInstance.key,
+          repositorySlug,
+        })
+      );
     }
   };
 
@@ -226,7 +216,6 @@ export default class BitbucketCloudProjectCreate extends React.PureComponent<Pro
   render() {
     const { canAdmin, loadingBindings, location, almInstances } = this.props;
     const {
-      importingSlug,
       isLastPage = true,
       selectedAlmInstance,
       loading,
@@ -239,7 +228,6 @@ export default class BitbucketCloudProjectCreate extends React.PureComponent<Pro
     } = this.state;
     return (
       <BitbucketCloudProjectCreateRenderer
-        importingSlug={importingSlug}
         isLastPage={isLastPage}
         selectedAlmInstance={selectedAlmInstance}
         almInstances={almInstances}
index 28356e43449f8ccd296ff6f98991b9efa977b5be..770e8cac2ecb325957351e645b6bc5e0d6560ba9 100644 (file)
@@ -29,7 +29,6 @@ import WrongBindingCountAlert from '../components/WrongBindingCountAlert';
 import BitbucketCloudSearchForm from './BitbucketCloudSearchForm';
 
 export interface BitbucketCloudProjectCreateRendererProps {
-  importingSlug?: string;
   isLastPage: boolean;
   canAdmin?: boolean;
   loading: boolean;
@@ -53,7 +52,6 @@ export default function BitbucketCloudProjectCreateRenderer(
 ) {
   const {
     almInstances,
-    importingSlug,
     isLastPage,
     selectedAlmInstance,
     canAdmin,
@@ -105,7 +103,6 @@ export default function BitbucketCloudProjectCreateRenderer(
           />
         ) : (
           <BitbucketCloudSearchForm
-            importingSlug={importingSlug}
             isLastPage={isLastPage}
             loadingMore={loadingMore}
             searchQuery={searchQuery}
index d52b70a176a41f6b6991cbdebe479a091ac06c33..8b90691f34810f99ae62a677f399838be64d0895 100644 (file)
@@ -33,11 +33,9 @@ import { getProjectUrl, queryToSearch } from '../../../../helpers/urls';
 import { BitbucketCloudRepository } from '../../../../types/alm-integration';
 import { ComponentQualifier } from '../../../../types/component';
 import { MetricType } from '../../../../types/metrics';
-import InstanceNewCodeDefinitionComplianceWarning from '../components/InstanceNewCodeDefinitionComplianceWarning';
 import { CreateProjectModes } from '../types';
 
 export interface BitbucketCloudSearchFormProps {
-  importingSlug?: string;
   isLastPage: boolean;
   loadingMore: boolean;
   onImport: (repositorySlug: string) => void;
@@ -53,14 +51,7 @@ function getRepositoryUrl(workspace: string, slug: string) {
 }
 
 export default function BitbucketCloudSearchForm(props: BitbucketCloudSearchFormProps) {
-  const {
-    importingSlug,
-    isLastPage,
-    loadingMore,
-    repositories = [],
-    searching,
-    searchQuery,
-  } = props;
+  const { isLastPage, loadingMore, repositories = [], searching, searchQuery } = props;
 
   if (repositories.length === 0 && searchQuery.length === 0 && !searching) {
     return (
@@ -86,109 +77,101 @@ export default function BitbucketCloudSearchForm(props: BitbucketCloudSearchForm
   }
 
   return (
-    <>
-      <InstanceNewCodeDefinitionComplianceWarning />
-      <div className="boxed-group big-padded create-project-import">
-        <SearchBox
-          className="spacer"
-          loading={searching}
-          minLength={3}
-          onChange={props.onSearch}
-          placeholder={translate('onboarding.create_project.search_prompt')}
-        />
+    <div className="boxed-group big-padded create-project-import">
+      <SearchBox
+        className="spacer"
+        loading={searching}
+        minLength={3}
+        onChange={props.onSearch}
+        placeholder={translate('onboarding.create_project.search_prompt')}
+      />
 
-        <hr />
+      <hr />
 
-        {repositories.length === 0 ? (
-          <div className="padded">{translate('no_results')}</div>
-        ) : (
-          <table className="data zebra zebra-hover">
-            <tbody>
-              {repositories.map((repository) => (
-                <tr key={repository.uuid}>
+      {repositories.length === 0 ? (
+        <div className="padded">{translate('no_results')}</div>
+      ) : (
+        <table className="data zebra zebra-hover">
+          <tbody>
+            {repositories.map((repository) => (
+              <tr key={repository.uuid}>
+                <td>
+                  <Tooltip overlay={repository.slug}>
+                    <strong className="project-name display-inline-block text-ellipsis">
+                      {repository.sqProjectKey ? (
+                        <Link to={getProjectUrl(repository.sqProjectKey)}>
+                          <QualifierIcon
+                            className="spacer-right"
+                            qualifier={ComponentQualifier.Project}
+                          />
+                          {repository.name}
+                        </Link>
+                      ) : (
+                        repository.name
+                      )}
+                    </strong>
+                  </Tooltip>
+                  <br />
+                  <Tooltip overlay={repository.projectKey}>
+                    <span className="text-muted project-path display-inline-block text-ellipsis">
+                      {repository.projectKey}
+                    </span>
+                  </Tooltip>
+                </td>
+                <td>
+                  <Link
+                    className="display-inline-flex-center big-spacer-right"
+                    to={getRepositoryUrl(repository.workspace, repository.slug)}
+                    target="_blank"
+                  >
+                    {translate('onboarding.create_project.bitbucketcloud.link')}
+                  </Link>
+                </td>
+                {repository.sqProjectKey ? (
                   <td>
-                    <Tooltip overlay={repository.slug}>
-                      <strong className="project-name display-inline-block text-ellipsis">
-                        {repository.sqProjectKey ? (
-                          <Link to={getProjectUrl(repository.sqProjectKey)}>
-                            <QualifierIcon
-                              className="spacer-right"
-                              qualifier={ComponentQualifier.Project}
-                            />
-                            {repository.name}
-                          </Link>
-                        ) : (
-                          repository.name
-                        )}
-                      </strong>
-                    </Tooltip>
-                    <br />
-                    <Tooltip overlay={repository.projectKey}>
-                      <span className="text-muted project-path display-inline-block text-ellipsis">
-                        {repository.projectKey}
-                      </span>
-                    </Tooltip>
+                    <span className="display-flex-center display-flex-justify-end already-set-up">
+                      <CheckIcon className="little-spacer-right" size={12} />
+                      {translate('onboarding.create_project.repository_imported')}
+                    </span>
                   </td>
-                  <td>
-                    <Link
-                      className="display-inline-flex-center big-spacer-right"
-                      to={getRepositoryUrl(repository.workspace, repository.slug)}
-                      target="_blank"
+                ) : (
+                  <td className="text-right">
+                    <Button
+                      onClick={() => {
+                        props.onImport(repository.slug);
+                      }}
                     >
-                      {translate('onboarding.create_project.bitbucketcloud.link')}
-                    </Link>
+                      {translate('onboarding.create_project.set_up')}
+                    </Button>
                   </td>
-                  {repository.sqProjectKey ? (
-                    <td>
-                      <span className="display-flex-center display-flex-justify-end already-set-up">
-                        <CheckIcon className="little-spacer-right" size={12} />
-                        {translate('onboarding.create_project.repository_imported')}
-                      </span>
-                    </td>
-                  ) : (
-                    <td className="text-right">
-                      <Button
-                        disabled={Boolean(importingSlug)}
-                        onClick={() => {
-                          props.onImport(repository.slug);
-                        }}
-                      >
-                        {translate('onboarding.create_project.set_up')}
-                        <DeferredSpinner
-                          className="spacer-left"
-                          loading={importingSlug === repository.slug}
-                        />
-                      </Button>
-                    </td>
-                  )}
-                </tr>
-              ))}
-            </tbody>
-          </table>
-        )}
-        <footer className="spacer-top note text-center">
-          {isLastPage &&
-            translateWithParameters(
-              'x_of_y_shown',
-              formatMeasure(repositories.length, MetricType.Integer, null),
-              formatMeasure(repositories.length, MetricType.Integer, null)
-            )}
-          {!isLastPage && (
-            <Button
-              className="spacer-left"
-              disabled={loadingMore}
-              data-test="show-more"
-              onClick={props.onLoadMore}
-            >
-              {translate('show_more')}
-            </Button>
+                )}
+              </tr>
+            ))}
+          </tbody>
+        </table>
+      )}
+      <footer className="spacer-top note text-center">
+        {isLastPage &&
+          translateWithParameters(
+            'x_of_y_shown',
+            formatMeasure(repositories.length, MetricType.Integer, null),
+            formatMeasure(repositories.length, MetricType.Integer, null)
           )}
-          <DeferredSpinner
-            className="text-bottom spacer-left position-absolute"
-            loading={loadingMore}
-          />
-        </footer>
-      </div>
-    </>
+        {!isLastPage && (
+          <Button
+            className="spacer-left"
+            disabled={loadingMore}
+            data-test="show-more"
+            onClick={props.onLoadMore}
+          >
+            {translate('show_more')}
+          </Button>
+        )}
+        <DeferredSpinner
+          className="text-bottom spacer-left position-absolute"
+          loading={loadingMore}
+        />
+      </footer>
+    </div>
   );
 }
index 2f0734b648127df294a3db149de13b067a5f8343..fe3e7e1dda0eeaf4701556d2f7735e18b29a4195 100644 (file)
@@ -231,7 +231,7 @@ export class CreateProjectPage extends React.PureComponent<CreateProjectPageProp
             canAdmin={!!canAdmin}
             loadingBindings={loading}
             location={location}
-            onProjectCreate={this.handleProjectCreate}
+            onProjectSetupDone={this.handleProjectSetupDone}
             router={router}
             almInstances={bitbucketCloudSettings}
           />
index ee4792f5d1999c68348240aee2349c4cc4c59e20..376024a2e447b3f96cb743ec427110154edff739 100644 (file)
@@ -124,9 +124,7 @@ it('should show import project feature when PAT is already set', async () => {
   expect(screen.getByText('BitbucketCloud Repo 1')).toBeInTheDocument();
   expect(screen.getByText('BitbucketCloud Repo 2')).toBeInTheDocument();
 
-  projectItem = screen.getByRole('row', {
-    name: 'qualifier.TRK BitbucketCloud Repo 1 project opens_in_new_window onboarding.create_project.bitbucketcloud.link onboarding.create_project.repository_imported',
-  });
+  projectItem = screen.getByRole('row', { name: /BitbucketCloud Repo 1/ });
   expect(
     within(projectItem).getByText('onboarding.create_project.repository_imported')
   ).toBeInTheDocument();
@@ -139,14 +137,24 @@ it('should show import project feature when PAT is already set', async () => {
     '/dashboard?id=key'
   );
 
-  projectItem = screen.getByRole('row', {
-    name: 'BitbucketCloud Repo 2 project opens_in_new_window onboarding.create_project.bitbucketcloud.link onboarding.create_project.set_up',
-  });
-  const importProjectButton = within(projectItem).getByRole('button', {
+  projectItem = screen.getByRole('row', { name: /BitbucketCloud Repo 2/ });
+  const setupButton = within(projectItem).getByRole('button', {
     name: 'onboarding.create_project.set_up',
   });
 
-  await user.click(importProjectButton);
+  await user.click(setupButton);
+
+  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();
 });