]> source.dussan.org Git - sonarqube.git/commitdiff
SONARCLOUD-380 Drop members sync feature for Bitbucket
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Tue, 26 Feb 2019 10:52:56 +0000 (11:52 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 6 Mar 2019 10:30:43 +0000 (11:30 +0100)
14 files changed:
server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationAlmBinding.java
server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationBind.tsx
server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationBind-test.tsx
server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx
server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap
server/sonar-web/src/main/js/apps/organizationMembers/MembersPageHeader.tsx
server/sonar-web/src/main/js/apps/organizationMembers/SyncMemberForm.tsx
server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersPageHeader-test.tsx
server/sonar-web/src/main/js/apps/organizationMembers/__tests__/SyncMemberForm-test.tsx
server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersPageHeader-test.tsx.snap
server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/SyncMemberForm-test.tsx.snap
server/sonar-web/src/main/js/helpers/testMocks.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 452e948eba696d99ceb8001ab04c28f4d599e7f8..80584ff7f47a9ede0d904821eed15ca37c3647ac 100644 (file)
@@ -26,5 +26,5 @@ import org.sonar.db.organization.OrganizationDto;
 @ServerSide
 public interface OrganizationAlmBinding {
 
-  void bindOrganization(DbSession dbSession, OrganizationDto organization, String installationId, boolean enableMembersSync);
+  void bindOrganization(DbSession dbSession, OrganizationDto organization, String installationId, boolean isNewOrganization);
 }
index 77a7d627ff1e0a8380d93c5558b9a8e5d26fdcac..7936eaa3df298da389aad005f8a4d4cf41f5b1e1 100644 (file)
@@ -24,6 +24,7 @@ import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import { Alert } from '../../../components/ui/Alert';
 import { SubmitButton } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { isGithub } from '../../../helpers/almIntegrations';
 
 interface Props {
   almKey: string;
@@ -79,6 +80,7 @@ export default class AutoOrganizationBind extends React.PureComponent<Props, Sta
   };
 
   render() {
+    const { almKey } = this.props;
     const { organization, submitting } = this.state;
     return (
       <form id="bind-organization-form" onSubmit={this.handleSubmit}>
@@ -87,18 +89,20 @@ export default class AutoOrganizationBind extends React.PureComponent<Props, Sta
           organization={organization}
           organizations={this.props.unboundOrganizations}
         />
-        <Alert className="abs-width-400 big-spacer-top" display="block" variant="info">
-          {translateWithParameters(
-            'onboarding.import_organization.bind_members_not_sync_info_x',
-            translate('organization', this.props.almKey)
-          )}
-          <Link
-            className="spacer-left"
-            target="_blank"
-            to={{ pathname: '/documentation/organizations/manage-team/' }}>
-            {translate('learn_more')}
-          </Link>
-        </Alert>
+        {isGithub(almKey) && (
+          <Alert className="abs-width-400 big-spacer-top" display="block" variant="info">
+            {translateWithParameters(
+              'onboarding.import_organization.bind_members_not_sync_info_x',
+              translate('organization', almKey)
+            )}
+            <Link
+              className="spacer-left"
+              target="_blank"
+              to={{ pathname: '/documentation/organizations/manage-team/' }}>
+              {translate('learn_more')}
+            </Link>
+          </Alert>
+        )}
         <div className="display-flex-center big-spacer-top">
           <SubmitButton disabled={submitting || !organization}>
             {translate('onboarding.import_organization.bind')}
index 3a40718de567f6ff08595101813c30b4bdd49cd9..4df6b9d29d8ec5ad9dafefe02e9a1d022d5a5223 100644 (file)
@@ -28,7 +28,7 @@ import { Alert } from '../../../components/ui/Alert';
 import { DeleteButton } from '../../../components/ui/buttons';
 import RadioToggle from '../../../components/controls/RadioToggle';
 import { bindAlmOrganization } from '../../../api/alm-integration';
-import { sanitizeAlmId, getAlmMembersUrl } from '../../../helpers/almIntegrations';
+import { sanitizeAlmId, getAlmMembersUrl, isGithub } from '../../../helpers/almIntegrations';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { getBaseUrl } from '../../../helpers/urls';
 
@@ -164,25 +164,27 @@ export default class AutoOrganizationCreate extends React.PureComponent<Props, S
           {filter === Filters.Create && (
             <OrganizationDetailsForm
               infoBlock={
-                <Alert className="abs-width-600 big-spacer-top" display="block" variant="info">
-                  <p>
-                    {translateWithParameters(
-                      'onboarding.import_organization.members_sync_info_x',
-                      translate('organization', almKey),
-                      almOrganization.name,
-                      translate(almKey)
-                    )}
-                  </p>
-                  <a
-                    href={getAlmMembersUrl(almApplication.key, almOrganization.almUrl)}
-                    rel="noopener noreferrer"
-                    target="_blank">
-                    {translateWithParameters(
-                      'organization.members.see_all_members_on_x',
-                      translate(almKey)
-                    )}
-                  </a>
-                </Alert>
+                isGithub(almKey) && (
+                  <Alert className="abs-width-600 big-spacer-top" display="block" variant="info">
+                    <p>
+                      {translateWithParameters(
+                        'onboarding.import_organization.members_sync_info_x',
+                        translate('organization', almKey),
+                        almOrganization.name,
+                        translate(almKey)
+                      )}
+                    </p>
+                    <a
+                      href={getAlmMembersUrl(almApplication.key, almOrganization.almUrl)}
+                      rel="noopener noreferrer"
+                      target="_blank">
+                      {translateWithParameters(
+                        'organization.members.see_all_members_on_x',
+                        translate(almKey)
+                      )}
+                    </a>
+                  </Alert>
+                )
               }
               onContinue={this.props.handleOrgDetailsFinish}
               organization={almOrganization}
index 729c237a4c166e4731099155a284fe03e717df86..76234a23f865789422c9ce15d6f51d801b6daa62 100644 (file)
@@ -32,6 +32,14 @@ it('should render correctly', () => {
   expect(onBindOrganization).toHaveBeenCalled();
 });
 
+it('should not show member sync info box for Bitbucket', () => {
+  expect(
+    shallowRender({ almKey: 'bitbucket' })
+      .find('Alert')
+      .exists()
+  ).toBe(false);
+});
+
 function shallowRender(props: Partial<AutoOrganizationBind['props']> = {}) {
   return shallow(
     <AutoOrganizationBind
index b3fe16d362c8db071fc16a7465f8979da91665b1..151c5c51ed8b64e0168e5a06962cd14a8924c91d 100644 (file)
@@ -34,10 +34,8 @@ const organization = mockAlmOrganization();
 it('should render prefilled and create org', async () => {
   const createOrganization = jest.fn().mockResolvedValue({ key: 'foo' });
   const handleOrgDetailsFinish = jest.fn();
-  const almApplication = mockAlmApplication({ key: 'github' });
   const almOrganization = mockAlmOrganization({ almUrl: 'http://github.com/thing' });
   const wrapper = shallowRender({
-    almApplication,
     almOrganization,
     createOrganization,
     handleOrgDetailsFinish
@@ -96,6 +94,14 @@ it('should bind existing organization', async () => {
   expect(onOrgCreated).toHaveBeenCalledWith('foo');
 });
 
+it('should not show member sync info box for Bitbucket', () => {
+  expect(
+    shallowRender({ almApplication: mockAlmApplication({ key: 'bitbucket-cloud' }) })
+      .find('Alert')
+      .exists()
+  ).toBe(false);
+});
+
 function shallowRender(props: Partial<AutoOrganizationCreate['props']> = {}) {
   return shallow(
     <AutoOrganizationCreate
index ca0868ae1051318f43243c1fdeb6c313d03a20da..1af9195ea6814f644c3562e68407f0bd2fc901a6 100644 (file)
@@ -20,9 +20,9 @@ exports[`should display choice between import or creation 1`] = `
           values={
             Object {
               "avatar": <img
-                alt="BitBucket"
+                alt="GitHub"
                 className="little-spacer-left"
-                src="/images/sonarcloud/bitbucket.svg"
+                src="/images/sonarcloud/github.svg"
                 width={16}
               />,
               "name": <strong>
@@ -59,11 +59,11 @@ exports[`should display choice between import or creation 1`] = `
   <PlanStep
     almApplication={
       Object {
-        "backgroundColor": "#0052CC",
-        "iconPath": "\\"/static/authbitbucket/bitbucket.svg\\"",
-        "installationUrl": "https://bitbucket.org/install/app",
-        "key": "bitbucket",
-        "name": "BitBucket",
+        "backgroundColor": "#444444",
+        "iconPath": "/images/sonarcloud/github-white.svg",
+        "installationUrl": "https://github.com/apps/greg-sonarcloud/installations/new",
+        "key": "github",
+        "name": "GitHub",
       }
     }
     almOrganization={
@@ -119,7 +119,7 @@ exports[`should render prefilled and create org 1`] = `
           values={
             Object {
               "avatar": <img
-                alt="BitBucket"
+                alt="GitHub"
                 className="little-spacer-left"
                 src="/images/sonarcloud/github.svg"
                 width={16}
@@ -175,11 +175,11 @@ exports[`should render prefilled and create org 1`] = `
   <PlanStep
     almApplication={
       Object {
-        "backgroundColor": "#0052CC",
-        "iconPath": "\\"/static/authbitbucket/bitbucket.svg\\"",
-        "installationUrl": "https://bitbucket.org/install/app",
+        "backgroundColor": "#444444",
+        "iconPath": "/images/sonarcloud/github-white.svg",
+        "installationUrl": "https://github.com/apps/greg-sonarcloud/installations/new",
         "key": "github",
-        "name": "BitBucket",
+        "name": "GitHub",
       }
     }
     almOrganization={
index 5cf908cce7ccace580fe30a114993d9f6afaca0a..5bf10ce8799b51a89a042dcf580a27efdfd2cf01 100644 (file)
@@ -24,7 +24,7 @@ import AddMemberForm from './AddMemberForm';
 import SyncMemberForm from './SyncMemberForm';
 import DeferredSpinner from '../../components/common/DeferredSpinner';
 import DocTooltip from '../../components/docs/DocTooltip';
-import { sanitizeAlmId } from '../../helpers/almIntegrations';
+import { sanitizeAlmId, isGithub } from '../../helpers/almIntegrations';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 import { Alert } from '../../components/ui/Alert';
 
@@ -51,6 +51,7 @@ export default function MembersPageHeader(props: Props) {
       {isAdmin && (
         <div className="page-actions text-right">
           {almKey &&
+            isGithub(almKey) &&
             !showSyncNotif && (
               <SyncMemberForm organization={organization} refreshMembers={refreshMembers} />
             )}
@@ -82,6 +83,7 @@ export default function MembersPageHeader(props: Props) {
           }}
         />
         {almKey &&
+          isGithub(almKey) &&
           showSyncNotif && (
             <Alert className="spacer-top" display="inline" variant="info">
               {translateWithParameters(
index 9276aa1df567f549426da881043f23035b174c55..826f70a4b1ce33acab6570f520d3d313c428b763 100644 (file)
@@ -25,7 +25,7 @@ import RadioCard from '../../components/controls/RadioCard';
 import { Alert } from '../../components/ui/Alert';
 import { Button } from '../../components/ui/buttons';
 import { setOrganizationMemberSync, syncMembers } from '../../api/organizations';
-import { sanitizeAlmId, isGithub } from '../../helpers/almIntegrations';
+import { sanitizeAlmId } from '../../helpers/almIntegrations';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 import { fetchOrganization } from '../../store/rootActions';
 
@@ -56,7 +56,7 @@ export class SyncMemberForm extends React.PureComponent<Props, State> {
       enabled: membersSync
     }).then(() => {
       this.props.fetchOrganization(organization.key);
-      if (membersSync && isGithub(organization.alm && organization.alm.key)) {
+      if (membersSync) {
         return this.handleMemberSync();
       }
       return Promise.resolve();
index bc81f0149f4a51c1e67d552cdfeeea2f2add7338..43921cdcace83ca7866337dd2faa5fe6e6f22828 100644 (file)
@@ -36,7 +36,14 @@ it('should render for admin', () => {
   ).toMatchSnapshot();
 });
 
-it('should render for bound organization without sync', () => {
+it('should render for Bitbucket bound organization', () => {
+  const organization = mockOrganizationWithAlm(mockOrganizationWithAdminActions(), {
+    key: 'bitbucket'
+  });
+  expect(shallowRender({ organization })).toMatchSnapshot();
+});
+
+it('should render for GitHub bound organization without sync', () => {
   const organization = mockOrganizationWithAlm(mockOrganizationWithAdminActions());
   expect(shallowRender({ organization })).toMatchSnapshot();
 });
index 1218009c845d2e80fa304767fa15bf0029163da7..2d959fdb109ed1bfaa4194e1047a6ad8f8de2a97 100644 (file)
@@ -33,7 +33,7 @@ beforeEach(() => {
   jest.clearAllMocks();
 });
 
-it('should allow to switch to automatic mode with github', async () => {
+it('should allow to switch to automatic mode', async () => {
   const fetchOrganization = jest.fn();
   const refreshMembers = jest.fn().mockResolvedValue({});
   const wrapper = shallowRender({ fetchOrganization, refreshMembers });
@@ -49,23 +49,6 @@ it('should allow to switch to automatic mode with github', async () => {
   expect(refreshMembers).toBeCalled();
 });
 
-it('should allow to switch to automatic mode with bitbucket', async () => {
-  const fetchOrganization = jest.fn();
-  const wrapper = shallowRender({
-    fetchOrganization,
-    organization: mockOrganizationWithAlm({}, { key: 'bitbucket' })
-  });
-  expect(wrapper).toMatchSnapshot();
-
-  wrapper.setState({ membersSync: true });
-  wrapper.find('ConfirmButton').prop<Function>('onConfirm')();
-  expect(setOrganizationMemberSync).toHaveBeenCalledWith({ organization: 'foo', enabled: true });
-
-  await waitAndUpdate(wrapper);
-  expect(fetchOrganization).toHaveBeenCalledWith('foo');
-  expect(syncMembers).not.toHaveBeenCalled();
-});
-
 it('should allow to switch to manual mode', async () => {
   const fetchOrganization = jest.fn();
   const wrapper = shallowRender({
index c21bb177f1c43974216000f48a6222666b07608c..ca7ac6f19fb482a2d648ef3f6ff20f74fe353c77 100644 (file)
@@ -36,7 +36,7 @@ exports[`should render correctly 1`] = `
 </header>
 `;
 
-exports[`should render for admin 1`] = `
+exports[`should render for Bitbucket bound organization 1`] = `
 <header
   className="page-header"
 >
@@ -63,6 +63,11 @@ exports[`should render for admin 1`] = `
             "actions": Object {
               "admin": true,
             },
+            "alm": Object {
+              "key": "bitbucket",
+              "membersSync": false,
+              "url": "https://github.com/foo",
+            },
             "key": "foo",
             "name": "Foo",
           }
@@ -97,7 +102,7 @@ exports[`should render for admin 1`] = `
 </header>
 `;
 
-exports[`should render for bound organization without sync 1`] = `
+exports[`should render for GitHub bound organization without sync 1`] = `
 <header
   className="page-header"
 >
@@ -190,3 +195,64 @@ exports[`should render for bound organization without sync 1`] = `
   </div>
 </header>
 `;
+
+exports[`should render for admin 1`] = `
+<header
+  className="page-header"
+>
+  <h1
+    className="page-title"
+  >
+    organization.members.page
+  </h1>
+  <DeferredSpinner
+    loading={false}
+    timeout={100}
+  />
+  <div
+    className="page-actions text-right"
+  >
+    <div
+      className="display-inline-block spacer-left spacer-bottom"
+    >
+      <AddMemberForm
+        addMember={[MockFunction]}
+        memberLogins={Array []}
+        organization={
+          Object {
+            "actions": Object {
+              "admin": true,
+            },
+            "key": "foo",
+            "name": "Foo",
+          }
+        }
+      />
+      <DocTooltip
+        className="spacer-left"
+        doc={Promise {}}
+      />
+    </div>
+  </div>
+  <div
+    className="page-description"
+  >
+    <FormattedMessage
+      defaultMessage="organization.members.page.description"
+      id="organization.members.page.description"
+      values={
+        Object {
+          "link": <Link
+            onlyActiveOnIndex={false}
+            style={Object {}}
+            target="_blank"
+            to="/documentation/organizations/manage-team/"
+          >
+            organization.members.manage_a_team
+          </Link>,
+        }
+      }
+    />
+  </div>
+</header>
+`;
index 1be6de36a4c7a0c0ef332f85e73d1cfb2d0b97e1..c0a6253566e55dad9a6a791c182ca2de769e69d4 100644 (file)
@@ -1,102 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`should allow to switch to automatic mode with bitbucket 1`] = `
-<ConfirmButton
-  cancelButtonText="close"
-  confirmButtonText="save"
-  confirmDisable={true}
-  modalBody={
-    <div
-      className="display-flex-stretch big-spacer-top"
-    >
-      <RadioCard
-        onClick={[Function]}
-        selected={true}
-        title="organization.members.management.manual"
-      >
-        <div
-          className="spacer-left"
-        >
-          <ul
-            className="big-spacer-left note"
-          >
-            <li
-              className="spacer-bottom"
-            >
-              organization.members.management.manual.add_members_manually
-            </li>
-            <li>
-              organization.members.management.choose_members_permissions
-            </li>
-          </ul>
-        </div>
-      </RadioCard>
-      <RadioCard
-        onClick={[Function]}
-        selected={false}
-        title="organization.members.management.automatic.bitbucket"
-      >
-        <div
-          className="spacer-left"
-        >
-          <ul
-            className="big-spacer-left note"
-          >
-            <React.Fragment>
-              <li
-                className="spacer-bottom"
-              >
-                organization.members.management.automatic.synchronized_from_x.organization.bitbucket
-              </li>
-              <li
-                className="spacer-bottom"
-              >
-                organization.members.management.automatic.members_changes_reflected.bitbucket
-              </li>
-            </React.Fragment>
-            <li>
-              organization.members.management.choose_members_permissions
-            </li>
-          </ul>
-        </div>
-        <Alert
-          className="big-spacer-top"
-          variant="warning"
-        >
-          organization.members.management.automatic.warning
-        </Alert>
-      </RadioCard>
-    </div>
-  }
-  modalHeader="organization.members.management.title"
-  modalHeaderDescription={
-    <p
-      className="spacer-top"
-    >
-      organization.members.management.description
-      <Link
-        className="spacer-left"
-        onlyActiveOnIndex={false}
-        style={Object {}}
-        target="_blank"
-        to={
-          Object {
-            "pathname": "/documentation/organizations/manage-team/",
-          }
-        }
-      >
-        learn_more
-      </Link>
-    </p>
-  }
-  onConfirm={[Function]}
-  size="medium"
->
-  <Component />
-</ConfirmButton>
-`;
-
-exports[`should allow to switch to automatic mode with github 1`] = `
+exports[`should allow to switch to automatic mode 1`] = `
 <ConfirmButton
   cancelButtonText="close"
   confirmButtonText="save"
index a6e20a88178dabcce9f63b5dd344d8f0e75c458b..dc0927642f87bce0c8f740eb66005327c8f3dd14 100644 (file)
@@ -23,11 +23,11 @@ import { Profile } from '../apps/quality-profiles/types';
 
 export function mockAlmApplication(overrides: Partial<T.AlmApplication> = {}): T.AlmApplication {
   return {
-    backgroundColor: '#0052CC',
-    iconPath: '"/static/authbitbucket/bitbucket.svg"',
-    installationUrl: 'https://bitbucket.org/install/app',
-    key: 'bitbucket',
-    name: 'BitBucket',
+    backgroundColor: '#444444',
+    iconPath: '/images/sonarcloud/github-white.svg',
+    installationUrl: 'https://github.com/apps/greg-sonarcloud/installations/new',
+    key: 'github',
+    name: 'GitHub',
     ...overrides
   };
 }
index 605b0e96f0376bebd3534dd1b03db8f0367874bc..0aa845d5b527cec63dbf81edead11da7719fbfd3 100644 (file)
@@ -2680,7 +2680,6 @@ organization.members.add_to_members=Add to members
 organization.members.config_synchro=Configure Synchronization
 organization.members.auto_sync_with_x=Automatic sync with {0}
 organization.members.auto_sync_members_from_org_x=Now your members can be automatically synchronized with your {0}.
-organization.members.auto_sync_total_help.bitbucket=You might not see all members from your Bitbucket team yet, as they need to reconnect to SonarCloud to be members of the organization.
 organization.members.auto_sync_total_help.github=You might not see all members from your GitHub organization yet, as they need to connect to SonarCloud at least once to appear in this list.
 organization.members.see_all_members_on_x=See all members on {0}
 organization.members.management.title=Members Management
@@ -2689,7 +2688,6 @@ organization.members.management.manual=Manual
 organization.members.management.manual.add_members_manually=Admin add members manually from SonarCloud existing users
 organization.members.management.automatic=Automatic sync with {0}
 organization.members.management.automatic.synchronized_from_x=Members are synchronized automatically from your {0}
-organization.members.management.automatic.members_changes_reflected.bitbucket=Your team members must reconnect to SonarCloud to be automatically added to correct SonarCloud organization
 organization.members.management.automatic.members_changes_reflected.github=If you add or remove a member on GitHub, SonarCloud immediately reflects the changes
 organization.members.management.automatic.warning=This will override your current Members and Permissions configuration
 organization.members.management.choose_members_permissions=Admin manages permissions for each member in SonarCloud