]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14394 Add frontend for bitbucket cloud project binding.
authorMathieu Suen <mathieu.suen@sonarsource.com>
Wed, 27 Jan 2021 08:31:09 +0000 (09:31 +0100)
committersonartech <sonartech@sonarsource.com>
Mon, 8 Feb 2021 20:07:45 +0000 (20:07 +0000)
server/sonar-web/src/main/js/api/alm-settings.ts
server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCreationMenu-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/AlmSpecificForm-test.tsx.snap
server/sonar-web/src/main/js/types/alm-settings.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 4c4f6430ad1ecc1c939d8aaf8fb30b96301dc1ca..9491b1809ec28d9c9b2fa37a983280463743e1c3 100644 (file)
@@ -26,6 +26,7 @@ import {
   AzureProjectAlmBindingParams,
   BitbucketBindingDefinition,
   BitbucketCloudBindingDefinition,
+  BitbucketCloudProjectAlmBindingParams,
   BitbucketProjectAlmBindingParams,
   GithubBindingDefinition,
   GithubProjectAlmBindingParams,
@@ -132,6 +133,10 @@ export function setProjectBitbucketBinding(data: BitbucketProjectAlmBindingParam
   return post('/api/alm_settings/set_bitbucket_binding', data).catch(throwGlobalError);
 }
 
+export function setProjectBitbucketCloudBinding(data: BitbucketCloudProjectAlmBindingParams) {
+  return post('/api/alm_settings/set_bitbucketcloud_binding', data).catch(throwGlobalError);
+}
+
 export function setProjectGithubBinding(data: GithubProjectAlmBindingParams) {
   return post('/api/alm_settings/set_github_binding', data).catch(throwGlobalError);
 }
index 90e7b675473f54c129ba24e84bd2ef343ebe3e08..4488367b0661a1b99fd46009fff731bdd4dd97d4 100644 (file)
@@ -53,19 +53,48 @@ it('should not fetch alm bindings if user cannot create projects', async () => {
 });
 
 it('should filter alm bindings appropriately', async () => {
-  (getAlmSettings as jest.Mock).mockResolvedValueOnce([
-    { alm: AlmKeys.Azure },
-    { alm: AlmKeys.BitbucketServer, url: 'b1' },
-    { alm: AlmKeys.BitbucketServer, url: 'b2' },
-    { alm: AlmKeys.GitHub },
-    { alm: AlmKeys.GitLab, url: 'gitlab.com' }
-  ]);
+  (getAlmSettings as jest.Mock)
+    .mockResolvedValueOnce([
+      // Only faulty configs.
+      { alm: AlmKeys.Azure }, // Missing some configuration; will be ignored.
+      { alm: AlmKeys.BitbucketCloud }, // Bitbucket Cloud isn't supported.
+      { alm: AlmKeys.GitLab } // Missing some configuration; will be ignored.
+    ])
+    .mockResolvedValueOnce([
+      // All correct configs.
+      { alm: AlmKeys.Azure, url: 'http://ado.example.com' },
+      { alm: AlmKeys.BitbucketServer, url: 'b1' },
+      { alm: AlmKeys.GitHub },
+      { alm: AlmKeys.GitLab, url: 'gitlab.com' }
+    ])
+    .mockResolvedValueOnce([
+      // Only duplicate ALMs; should all be ignored.
+      { alm: AlmKeys.Azure, url: 'http://ado.example.com' },
+      { alm: AlmKeys.Azure, url: 'http://ado.example.com' },
+      { alm: AlmKeys.BitbucketServer, url: 'b1' },
+      { alm: AlmKeys.BitbucketServer, url: 'b1' },
+      { alm: AlmKeys.GitHub },
+      { alm: AlmKeys.GitHub },
+      { alm: AlmKeys.GitLab, url: 'gitlab.com' },
+      { alm: AlmKeys.GitLab, url: 'gitlab.com' }
+    ]);
 
-  const wrapper = shallowRender();
+  let wrapper = shallowRender();
+  await waitAndUpdate(wrapper);
+  expect(wrapper.state().boundAlms).toEqual([]);
 
+  wrapper = shallowRender();
   await waitAndUpdate(wrapper);
+  expect(wrapper.state().boundAlms).toEqual([
+    AlmKeys.Azure,
+    AlmKeys.BitbucketServer,
+    AlmKeys.GitHub,
+    AlmKeys.GitLab
+  ]);
 
-  expect(wrapper.state().boundAlms).toEqual([AlmKeys.GitHub, AlmKeys.GitLab]);
+  wrapper = shallowRender();
+  await waitAndUpdate(wrapper);
+  expect(wrapper.state().boundAlms).toEqual([]);
 });
 
 function shallowRender(overrides: Partial<ProjectCreationMenu['props']> = {}) {
index 52a985f6fb00bfe72fb408b05d975c0f67105af1..c6d297e1564b995b90028d719940534cb183d56f 100644 (file)
@@ -217,7 +217,27 @@ export default function AlmSpecificForm(props: AlmSpecificFormProps) {
             propKey: 'slug',
             value: slug || ''
           })}
-          {renderMonoRepoFieldWithDocLink(ALM_DOCUMENTATION_PATHS[AlmKeys.Bitbucket])}
+          {renderMonoRepoFieldWithDocLink(ALM_DOCUMENTATION_PATHS[AlmKeys.BitbucketServer])}
+        </>
+      );
+    case AlmKeys.BitbucketCloud:
+      return (
+        <>
+          {renderField({
+            help: true,
+            helpParams: {
+              example: (
+                <>
+                  {'https://bitbucket.org/{workspace}/'}
+                  <strong>{'{repository}'}</strong>
+                </>
+              )
+            },
+            id: 'bitbucketcloud.repository',
+            onFieldChange: props.onFieldChange,
+            propKey: 'repository',
+            value: repository || ''
+          })}
         </>
       );
     case AlmKeys.GitHub:
index 4f4d34a1564713649dcd36f771d2cab6b6510947..c38e99da161f7e058fb021d89d78c58c4c4c70b5 100644 (file)
@@ -26,6 +26,7 @@ import {
   getProjectAlmBinding,
   setProjectAzureBinding,
   setProjectBitbucketBinding,
+  setProjectBitbucketCloudBinding,
   setProjectGithubBinding,
   setProjectGitlabBinding
 } from '../../../../api/alm-settings';
@@ -193,6 +194,16 @@ export class PRDecorationBinding extends React.PureComponent<Props & StateProps,
           monorepo
         });
       }
+      case AlmKeys.BitbucketCloud: {
+        if (!repository) {
+          return Promise.reject();
+        }
+        return setProjectBitbucketCloudBinding({
+          almSetting,
+          project,
+          repository
+        });
+      }
       case AlmKeys.GitHub: {
         // By default it must remain true.
         const summaryCommentEnabled =
index 1edea77f342fe9fc0d6df48f6fd8751edbcb0aae..0bc17a08b699884e26786adab9852f788871d804 100644 (file)
@@ -27,6 +27,7 @@ it.each([
   [AlmKeys.Azure, true],
   [AlmKeys.BitbucketServer, false],
   [AlmKeys.BitbucketServer, true],
+  [AlmKeys.BitbucketCloud, false],
   [AlmKeys.GitHub, false],
   [AlmKeys.GitHub, true],
   [AlmKeys.GitLab, false],
index b5b4fd22e70657f13bfa130f39d27c6ec4e2db45..1232291c9930a5a70dadf13648dc06ce9c7f6976 100644 (file)
@@ -26,6 +26,7 @@ import {
   getProjectAlmBinding,
   setProjectAzureBinding,
   setProjectBitbucketBinding,
+  setProjectBitbucketCloudBinding,
   setProjectGithubBinding,
   setProjectGitlabBinding
 } from '../../../../../api/alm-settings';
@@ -40,6 +41,7 @@ jest.mock('../../../../../api/alm-settings', () => ({
   setProjectBitbucketBinding: jest.fn().mockResolvedValue(undefined),
   setProjectGithubBinding: jest.fn().mockResolvedValue(undefined),
   setProjectGitlabBinding: jest.fn().mockResolvedValue(undefined),
+  setProjectBitbucketCloudBinding: jest.fn().mockResolvedValue(undefined),
   deleteProjectAlmBinding: jest.fn().mockResolvedValue(undefined)
 }));
 
@@ -94,7 +96,8 @@ describe('handleSubmit', () => {
     { key: 'github', alm: AlmKeys.GitHub },
     { key: 'azure', alm: AlmKeys.Azure },
     { key: 'bitbucket', alm: AlmKeys.BitbucketServer },
-    { key: 'gitlab', alm: AlmKeys.GitLab }
+    { key: 'gitlab', alm: AlmKeys.GitLab },
+    { key: 'bitbucketcloud', alm: AlmKeys.BitbucketCloud }
   ];
 
   it('should work for github', async () => {
@@ -187,6 +190,28 @@ describe('handleSubmit', () => {
     });
     expect(wrapper.state().success).toBe(true);
   });
+
+  it('should work for bitbucket cloud', async () => {
+    const wrapper = shallowRender();
+    await waitAndUpdate(wrapper);
+    const bitbucketKey = 'bitbucketcloud';
+    const repository = 'repoKey';
+    wrapper.setState({ formData: { key: bitbucketKey, repository }, instances: [] });
+    wrapper.instance().handleSubmit();
+    await waitAndUpdate(wrapper);
+    expect(setProjectBitbucketCloudBinding).not.toBeCalled();
+
+    wrapper.setState({ formData: { key: bitbucketKey, repository }, instances });
+    wrapper.instance().handleSubmit();
+    await waitAndUpdate(wrapper);
+
+    expect(setProjectBitbucketCloudBinding).toBeCalledWith({
+      almSetting: bitbucketKey,
+      project: PROJECT_KEY,
+      repository
+    });
+    expect(wrapper.state().success).toBe(true);
+  });
 });
 
 describe.each([[500], [404]])('For status %i', status => {
@@ -266,6 +291,7 @@ it.each([
   [AlmKeys.BitbucketServer, {}],
   [AlmKeys.BitbucketServer, { slug: 'test' }],
   [AlmKeys.BitbucketServer, { repository: 'test' }],
+  [AlmKeys.BitbucketCloud, {}],
   [AlmKeys.GitHub, {}],
   [AlmKeys.GitLab, {}]
 ])('should properly reject promise for %s & %s', async (almKey: AlmKeys, params: {}) => {
@@ -290,6 +316,7 @@ it('should validate form', async () => {
     instances: [
       { key: 'azure', alm: AlmKeys.Azure },
       { key: 'bitbucket', alm: AlmKeys.BitbucketServer },
+      { key: 'bitbucketcloud', alm: AlmKeys.BitbucketCloud },
       { key: 'github', alm: AlmKeys.GitHub },
       { key: 'gitlab', alm: AlmKeys.GitLab }
     ]
@@ -303,6 +330,8 @@ it('should validate form', async () => {
     { values: { key: 'github', repository: 'asdf' }, result: true },
     { values: { key: 'bitbucket', repository: 'key' }, result: false },
     { values: { key: 'bitbucket', repository: 'key', slug: 'slug' }, result: true },
+    { values: { key: 'bitbucketcloud', repository: '' }, result: false },
+    { values: { key: 'bitbucketcloud', repository: 'key' }, result: true },
     { values: { key: 'gitlab' }, result: false },
     { values: { key: 'gitlab', repository: 'key' }, result: true }
   ].forEach(({ values, result }) => {
index 64af693dfb7ef7b80ed0ed76733a018eca2d78bf..572d371879f836af019bea054c335368b00af10d 100644 (file)
@@ -424,6 +424,55 @@ exports[`it should render correctly for bitbucket and monorepo=true 1`] = `
 </Fragment>
 `;
 
+exports[`it should render correctly for bitbucketcloud and monorepo=false 1`] = `
+<Fragment>
+  <div
+    className="form-field"
+  >
+    <label
+      className="display-flex-center"
+      htmlFor="bitbucketcloud.repository"
+    >
+      settings.pr_decoration.binding.form.bitbucketcloud.repository
+      <em
+        className="mandatory"
+      >
+        *
+      </em>
+      <HelpTooltip
+        className="spacer-left"
+        overlay={
+          <FormattedMessage
+            defaultMessage="settings.pr_decoration.binding.form.bitbucketcloud.repository.help"
+            id="settings.pr_decoration.binding.form.bitbucketcloud.repository.help"
+            values={
+              Object {
+                "example": <React.Fragment>
+                  https://bitbucket.org/{workspace}/
+                  <strong>
+                    {repository}
+                  </strong>
+                </React.Fragment>,
+              }
+            }
+          />
+        }
+        placement="right"
+      />
+    </label>
+    <input
+      className="input-super-large"
+      id="bitbucketcloud.repository"
+      maxLength={256}
+      name="bitbucketcloud.repository"
+      onChange={[Function]}
+      type="text"
+      value=""
+    />
+  </div>
+</Fragment>
+`;
+
 exports[`it should render correctly for github and monorepo=false 1`] = `
 <Fragment>
   <div
index 0e81f6d70a3e4516d98d02e6b467684f0cdc6da2..2a1a0d1893bff078f5318c5d82b486b2033becfc 100644 (file)
@@ -113,6 +113,10 @@ export interface BitbucketProjectAlmBindingParams extends ProjectAlmBindingParam
   monorepo: boolean;
 }
 
+export interface BitbucketCloudProjectAlmBindingParams extends ProjectAlmBindingParams {
+  repository: string;
+}
+
 export interface GithubProjectAlmBindingParams extends ProjectAlmBindingParams {
   repository: string;
   summaryCommentEnabled: boolean;
index 779baa23258a5029dbc4bb4e8c9b4ef9790c6341..52f8c5a608b3618bbed195fec99fb0af81629db2 100644 (file)
@@ -1165,6 +1165,9 @@ settings.pr_decoration.binding.form.bitbucket.repository=Project Key
 settings.pr_decoration.binding.form.bitbucket.repository.help=The project key is part of your Bitbucket Server repository URL. Example: ({example})
 settings.pr_decoration.binding.form.bitbucket.slug=Repository SLUG
 settings.pr_decoration.binding.form.bitbucket.slug.help=The Repository Slug is part of your Bitbucket Server repository URL. Example: ({example})
+settings.pr_decoration.binding.form.bitbucketcloud.repository=Repository SLUG
+settings.pr_decoration.binding.form.bitbucketcloud.repository.help=The Repository SLUG is part of your Bitbucket Cloud URL. Example: {example}
+
 settings.pr_decoration.binding.form.gitlab.repository=Project ID
 
 property.category.general=General