]> source.dussan.org Git - sonarqube.git/commitdiff
SC-764 Don't sync members when binding personal github org.
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Thu, 27 Jun 2019 08:56:15 +0000 (10:56 +0200)
committerSonarTech <sonartech@sonarsource.com>
Fri, 5 Jul 2019 18:21:12 +0000 (20:21 +0200)
19 files changed:
server/sonar-web/src/main/js/api/organizations.ts
server/sonar-web/src/main/js/app/types.d.ts
server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationSelect-test.tsx.snap
server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.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/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap
server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/PlanStep-test.tsx.snap
server/sonar-web/src/main/js/apps/create/organization/__tests__/actions-test.ts
server/sonar-web/src/main/js/apps/create/organization/actions.ts
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AutoProjectCreate-test.tsx.snap
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPageSonarCloud-test.tsx.snap
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/OrganizationInput-test.tsx.snap
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/RemoteRepositories-test.tsx.snap
server/sonar-web/src/main/js/apps/organizationMembers/MembersPageHeader.tsx
server/sonar-web/src/main/js/apps/organizationMembers/__tests__/MembersPageHeader-test.tsx
server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/MembersPageHeader-test.tsx.snap
server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationHeader-test.tsx.snap
server/sonar-web/src/main/js/helpers/testMocks.ts

index cc2d8c0f20576626788e729d434aa5e382be9bb9..140ed7398874c9926dada0da15d3fd2a68235dc2 100644 (file)
@@ -39,7 +39,7 @@ export function getOrganization(key: string): Promise<T.Organization | undefined
 
 interface GetOrganizationNavigation {
   adminPages: T.Extension[];
-  alm?: { key: string; membersSync: boolean; url: string };
+  alm?: { key: string; membersSync: boolean; personal: boolean; url: string };
   canUpdateProjectsVisibilityToPrivate: boolean;
   isDefault: boolean;
   pages: T.Extension[];
index 2f0712d6aeb9208c2e8859bed6aa9a88864eb948..93a3e9383b488d51ef177de2f9c42255d10342e0 100644 (file)
@@ -37,6 +37,7 @@ declare namespace T {
   export interface AlmOrganization extends OrganizationBase {
     almUrl: string;
     key: string;
+    personal: boolean;
     privateRepos: number;
     publicRepos: number;
   }
@@ -514,7 +515,7 @@ declare namespace T {
 
   export interface Organization extends OrganizationBase {
     actions?: OrganizationActions;
-    alm?: { key: string; membersSync: boolean; url: string };
+    alm?: { key: string; membersSync: boolean; personal: boolean; url: string };
     adminPages?: Extension[];
     canUpdateProjectsVisibilityToPrivate?: boolean;
     isDefault?: boolean;
index 8e373bfac39cf6c09bb31c38b4cd5e8f3fb3a5db..46e9dbfcba1b86a64308f421040bd8cb74d3c710 100644 (file)
@@ -15,6 +15,7 @@ exports[`should render correctly 1`] = `
         "alm": Object {
           "key": "github",
           "membersSync": false,
+          "personal": false,
           "url": "https://github.com/foo",
         },
         "key": "bar",
index 99c5c5028e729363a2b9607913810b517591eb93..d0fb9543cffe06541547feca7646ee2ba803964f 100644 (file)
@@ -86,6 +86,7 @@ export default class AutoOrganizationCreate extends React.PureComponent<Props, S
       alm: {
         key: almApplication.key,
         membersSync: true,
+        personal: almOrganization.personal,
         url: almOrganization.almUrl
       },
       installationId: this.props.almInstallId
index 0deff24f1954f5573a1dc2ce7ec3e7af1a327a15..bce785818874625096859f8a27a628516b20ba05 100644 (file)
@@ -50,9 +50,10 @@ it('should render prefilled and create org', async () => {
   wrapper.setProps({ organization });
   wrapper.find('PlanStep').prop<Function>('createOrganization')();
 
-  const alm = {
+  const alm: T.Organization['alm'] = {
     key: 'github',
     membersSync: true,
+    personal: false,
     url: 'http://github.com/thing'
   };
   expect(createOrganization).toBeCalledWith({ ...organization, alm, installationId: 'id-foo' });
index 21741e56c967c8563add427fbb6b3b16c1cf3ed0..9b74fb68d4771d6606538dcdae83f07fe5eda854 100644 (file)
@@ -73,6 +73,7 @@ exports[`should display choice between import or creation 1`] = `
         "description": "description-foo",
         "key": "foo",
         "name": "foo",
+        "personal": false,
         "privateRepos": 0,
         "publicRepos": 3,
         "url": "http://example.com/foo",
@@ -162,6 +163,7 @@ exports[`should render prefilled and create org 1`] = `
           "description": "description-foo",
           "key": "foo",
           "name": "foo",
+          "personal": false,
           "privateRepos": 0,
           "publicRepos": 3,
           "url": "http://example.com/foo",
@@ -187,6 +189,7 @@ exports[`should render prefilled and create org 1`] = `
         "description": "description-foo",
         "key": "foo",
         "name": "foo",
+        "personal": false,
         "privateRepos": 0,
         "publicRepos": 3,
         "url": "http://example.com/foo",
index 3e5bdda098aeeb21555161c1e8825e862ec06fd5..46829d9e422a3063b7743b0a2db3cad88534352e 100644 (file)
@@ -361,6 +361,7 @@ exports[`should render with organization bind page 2`] = `
           "description": "description-foo",
           "key": "foo",
           "name": "foo",
+          "personal": false,
           "privateRepos": 0,
           "publicRepos": 3,
           "url": "http://example.com/foo",
index 0d38c7b12b9f7b0104216ac6051fd18edd41d15a..ca7cb71194005156f5febf9f9a0594ee065c1197 100644 (file)
@@ -30,6 +30,7 @@ exports[`should preselect paid plan 1`] = `
             "description": "description-foo",
             "key": "foo",
             "name": "foo",
+            "personal": false,
             "privateRepos": 5,
             "publicRepos": 0,
             "url": "http://example.com/foo",
index eb0a5cb17fca8926ea5ffa751759e88da4c4c2f0..0a3bb15ea84a213eac40f572cadfd05f603c0667 100644 (file)
@@ -62,4 +62,18 @@ describe('#createOrganization', () => {
     await promise;
     expect(syncMembers).toHaveBeenCalledWith(org.key);
   });
+
+  it('should not sync members for personal Github orgs', async () => {
+    const { alm, ...org } = mockOrganizationWithAlm(
+      {},
+      { key: 'github', membersSync: true, personal: true, url: 'https://github.com/foo' }
+    );
+
+    (createOrganization as jest.Mock).mockResolvedValueOnce(org);
+    const promise = actions.createOrganization({ alm, ...org })(dispatch);
+
+    expect(createOrganization).toHaveBeenCalledWith(org);
+    await promise;
+    expect(syncMembers).not.toBeCalled();
+  });
 });
index 950156f2876aac487d6ce77b2b9d853f5011da8c..8184ad205b2da91f8e3c7f195dd17be473c81b01 100644 (file)
@@ -31,7 +31,7 @@ export function createOrganization({
       .createOrganization({ ...organization, name: organization.name || organization.key })
       .then((newOrganization: T.Organization) => {
         dispatch(actions.createOrganization({ ...newOrganization, alm }));
-        if (alm && alm.membersSync && isGithub(alm.key)) {
+        if (alm && alm.membersSync && !alm.personal && isGithub(alm.key)) {
           api.syncMembers(newOrganization.key);
         }
         return newOrganization.key;
index 61c5ce222bbddfad75e9e71ffc949fa13ea01aa4..9e3f0583a8bd1e6edd08a980be8ca31fce6f0b77 100644 (file)
@@ -15,6 +15,7 @@ exports[`should display the bound organizations dropdown with the remote reposit
           "alm": Object {
             "key": "github",
             "membersSync": false,
+            "personal": false,
             "url": "https://github.com/foo",
           },
           "key": "foo",
@@ -25,6 +26,7 @@ exports[`should display the bound organizations dropdown with the remote reposit
           "alm": Object {
             "key": "github",
             "membersSync": false,
+            "personal": false,
             "url": "https://github.com/foo",
           },
           "key": "bar",
@@ -54,6 +56,7 @@ exports[`should display the bound organizations dropdown with the remote reposit
         "alm": Object {
           "key": "github",
           "membersSync": false,
+          "personal": false,
           "url": "https://github.com/foo",
         },
         "key": "foo",
index e2fc083cdeec54800478846fcb7530da544c5422..71566a532668f699363fb48d66674cddc6f54864 100644 (file)
@@ -87,6 +87,7 @@ exports[`should render correctly 2`] = `
             "alm": Object {
               "key": "github",
               "membersSync": false,
+              "personal": false,
               "url": "https://github.com/foo",
             },
             "key": "bar",
@@ -152,6 +153,7 @@ exports[`should render with Custom creation only 1`] = `
             "alm": Object {
               "key": "github",
               "membersSync": false,
+              "personal": false,
               "url": "https://github.com/foo",
             },
             "key": "bar",
@@ -222,6 +224,7 @@ exports[`should switch tabs 1`] = `
             "alm": Object {
               "key": "github",
               "membersSync": false,
+              "personal": false,
               "url": "https://github.com/foo",
             },
             "key": "bar",
index a655ac75dbe09a5793570148707b87a7982a8b78..7f2483a621e4adc569844d07cd3107eb6b61f65c 100644 (file)
@@ -34,6 +34,7 @@ exports[`should render correctly 1`] = `
           "alm": Object {
             "key": "github",
             "membersSync": false,
+            "personal": false,
             "url": "https://github.com/foo",
           },
           "key": "bar",
index d5f81a64131f9a56bfa85623dcb8791d093da60c..ffd95a3904c9bdcb2269e1bd8ea5a48a24ee2656 100644 (file)
@@ -39,6 +39,7 @@ exports[`should display the list of repositories 1`] = `
             "alm": Object {
               "key": "github",
               "membersSync": false,
+              "personal": false,
               "url": "https://github.com/foo",
             },
             "key": "foo",
@@ -140,6 +141,7 @@ exports[`should display the list of repositories 2`] = `
             "alm": Object {
               "key": "github",
               "membersSync": false,
+              "personal": false,
               "url": "https://github.com/foo",
             },
             "key": "foo",
@@ -166,6 +168,7 @@ exports[`should display the organization upgrade box 1`] = `
       "alm": Object {
         "key": "github",
         "membersSync": false,
+        "personal": false,
         "url": "https://github.com/foo",
       },
       "key": "foo",
index a520f22a579a1d8247be6fc9b89f404a0693cf09..0fbc42ebcf3787f5d1facf87317421aa41c366a4 100644 (file)
@@ -43,6 +43,8 @@ export default function MembersPageHeader(props: Props) {
   const almKey = organization.alm && sanitizeAlmId(organization.alm.key);
   const hasMemberSync = organization.alm && organization.alm.membersSync;
   const showSyncNotif = isAdmin && organization.alm && !hasMemberSync;
+  const isSyncEligible =
+    almKey && isGithub(almKey) && organization.alm && !organization.alm.personal;
 
   return (
     <header className="page-header">
@@ -50,7 +52,7 @@ export default function MembersPageHeader(props: Props) {
       <DeferredSpinner loading={props.loading} />
       {isAdmin && (
         <div className="page-actions text-right">
-          {almKey && isGithub(almKey) && !showSyncNotif && (
+          {isSyncEligible && !showSyncNotif && (
             <SyncMemberForm
               buttonText={translate('organization.members.config_synchro')}
               hasOtherMembers={members && members.length > 1}
@@ -85,7 +87,7 @@ export default function MembersPageHeader(props: Props) {
             )
           }}
         />
-        {almKey && isGithub(almKey) && showSyncNotif && (
+        {almKey && isSyncEligible && showSyncNotif && (
           <Alert className="spacer-top" display="inline" variant="info">
             {translateWithParameters(
               'organization.members.auto_sync_members_from_org_x',
index 43921cdcace83ca7866337dd2faa5fe6e6f22828..98cae6d4a6922fbed5c6f968e06009651735bf13 100644 (file)
@@ -48,6 +48,13 @@ it('should render for GitHub bound organization without sync', () => {
   expect(shallowRender({ organization })).toMatchSnapshot();
 });
 
+it('should render for personal GitHub bound organization without sync', () => {
+  const organization = mockOrganizationWithAlm(mockOrganizationWithAdminActions(), {
+    personal: true
+  });
+  expect(shallowRender({ organization })).toMatchSnapshot();
+});
+
 it('should render for bound organization with sync', () => {
   const organization = mockOrganizationWithAlm(mockOrganizationWithAdminActions(), {
     membersSync: true
index 9cc1eb93b37ebb886751071110683b82338aa3c1..fe3b38ed7728a1d18fd24670a5a1ca9c40f06226 100644 (file)
@@ -66,6 +66,7 @@ exports[`should render for Bitbucket bound organization 1`] = `
             "alm": Object {
               "key": "bitbucket",
               "membersSync": false,
+              "personal": false,
               "url": "https://github.com/foo",
             },
             "key": "foo",
@@ -132,6 +133,7 @@ exports[`should render for GitHub bound organization without sync 1`] = `
             "alm": Object {
               "key": "github",
               "membersSync": false,
+              "personal": false,
               "url": "https://github.com/foo",
             },
             "key": "foo",
@@ -184,6 +186,7 @@ exports[`should render for GitHub bound organization without sync 1`] = `
               "alm": Object {
                 "key": "github",
                 "membersSync": false,
+                "personal": false,
                 "url": "https://github.com/foo",
               },
               "key": "foo",
@@ -258,3 +261,70 @@ exports[`should render for admin 1`] = `
   </div>
 </header>
 `;
+
+exports[`should render for personal GitHub bound organization without sync 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,
+            },
+            "alm": Object {
+              "key": "github",
+              "membersSync": false,
+              "personal": true,
+              "url": "https://github.com/foo",
+            },
+            "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 828f54920e8c90d9d3b9bbd266af7094e0c2cc05..0394cb0be1db70b511777fbce86c40a5b47ab6ce 100644 (file)
@@ -116,6 +116,7 @@ exports[`renders with alm integration 1`] = `
         "alm": Object {
           "key": "github",
           "membersSync": false,
+          "personal": false,
           "url": "https://github.com/foo",
         },
         "key": "foo",
index 7e9b7eb4d1e10e1ef3006433ae7431762b6c1de4..a9b33558b36b208c81619e565ed5b0a7301369a2 100644 (file)
@@ -43,6 +43,7 @@ export function mockAlmOrganization(overrides: Partial<T.AlmOrganization> = {}):
     description: 'description-foo',
     key: 'foo',
     name: 'foo',
+    personal: false,
     privateRepos: 0,
     publicRepos: 3,
     url: 'http://example.com/foo',
@@ -491,7 +492,13 @@ export function mockOrganizationWithAlm(
   almOverrides: Partial<T.Organization['alm']> = {}
 ): T.Organization {
   return mockOrganization({
-    alm: { key: 'github', membersSync: false, url: 'https://github.com/foo', ...almOverrides },
+    alm: {
+      key: 'github',
+      membersSync: false,
+      personal: false,
+      url: 'https://github.com/foo',
+      ...almOverrides
+    },
     ...overrides
   });
 }