]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8994 Fix groups management for non default org
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Thu, 13 Apr 2017 10:00:23 +0000 (12:00 +0200)
committerGrégoire Aubert <gregaubert@users.noreply.github.com>
Thu, 13 Apr 2017 15:45:34 +0000 (17:45 +0200)
it/it-tests/src/test/java/it/organization/OrganizationMembershipTest.java
server/sonar-web/src/main/js/api/user_groups.js
server/sonar-web/src/main/js/apps/organizations/actions.js
server/sonar-web/src/main/js/apps/organizations/components/OrganizationGroupCheckbox.js
server/sonar-web/src/main/js/apps/organizations/components/OrganizationMembers.js
server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationGroupCheckbox-test.js.snap
server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationMembers-test.js.snap
server/sonar-web/src/main/js/apps/organizations/components/forms/AddMemberForm.js
server/sonar-web/src/main/js/apps/organizations/components/forms/ManageMemberGroupsForm.js
server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/AddMemberForm-test.js.snap
server/sonar-web/src/main/js/apps/users/components/UsersSelectSearch.js

index 4f6dbe6cc152e23b979d7daf96b0953bbdaa00be..0b05b0ef264e66ffedaaa7f2e0f8e289a50f8bc6 100644 (file)
@@ -223,7 +223,6 @@ public class OrganizationMembershipTest {
   }
 
   @Test
-  @Ignore("To be fixed by SONAR-8994")
   public void admin_can_manage_groups() {
     String orgKey = createOrganization();
     userRule.createUser("foo", "pwd");
index becfdb9c8a0652be1e5244c0084c0d617e119e38..de515f119b062d25cb2fbc52a225b00236a71b10 100644 (file)
 //@flow
 import { getJSON, post } from '../helpers/request';
 
-export function searchUsersGroups(query?: string, organization?: string) {
+export function searchUsersGroups(
+  data: { f?: string, organization?: string, p?: number, ps?: number, q?: string }
+) {
   const url = '/api/user_groups/search';
-  const data: { q?: string, organization?: string } = {};
-  if (query) {
-    data.q = query;
-  }
-  if (organization) {
-    data.organization = organization;
-  }
   return getJSON(url, data);
 }
 
-export function addUserToGroup(groupId: string, login: string) {
+export function addUserToGroup(
+  data: { id?: string, name?: string, login?: string, organization?: string }
+) {
   const url = '/api/user_groups/add_user';
-  return post(url, { id: groupId, login });
+  return post(url, data);
 }
 
-export function removeUserFromGroup(groupId: string, login: string) {
+export function removeUserFromGroup(
+  data: { id?: string, name?: string, login?: string, organization?: string }
+) {
   const url = '/api/user_groups/remove_user';
-  return post(url, { id: groupId, login });
+  return post(url, data);
 }
index a67d6103e3eaf29c0878fef21ec85303c2398dbc..db5727bdd36244ccf83494675eef250bd5eee2b5 100644 (file)
@@ -59,11 +59,11 @@ export const fetchOrganization = (key: string): Function =>
     );
   };
 
-export const fetchOrganizationGroups = (key: string): Function =>
+export const fetchOrganizationGroups = (organization: string): Function =>
   (dispatch: Function): Promise<*> => {
-    return searchUsersGroups('', key).then(
+    return searchUsersGroups({ organization }).then(
       response => {
-        dispatch(actions.receiveOrganizationGroups(key, response.groups));
+        dispatch(actions.receiveOrganizationGroups(organization, response.groups));
       },
       onFail(dispatch)
     );
@@ -166,24 +166,26 @@ export const removeOrganizationMember = (key: string, member: Member) =>
   };
 
 export const updateOrganizationMemberGroups = (
+  organization: Organization,
   member: Member,
   add: Array<string>,
   remove: Array<string>
 ) =>
   (dispatch: Function) => {
+    dispatch(
+      receiveUser({
+        ...member,
+        groupCount: (member.groupCount || 0) + add.length - remove.length
+      })
+    );
     const promises = [
-      ...add.map(id => addUserToGroup(id, member.login)),
-      ...remove.map(id => removeUserFromGroup(id, member.login))
+      ...add.map(name =>
+        addUserToGroup({ name, login: member.login, organization: organization.key })),
+      ...remove.map(name =>
+        removeUserFromGroup({ name, login: member.login, organization: organization.key }))
     ];
-    return Promise.all(promises).then(
-      () => {
-        dispatch(
-          receiveUser({
-            ...member,
-            groupCount: (member.groupCount || 0) + add.length - remove.length
-          })
-        );
-      },
-      onFail(dispatch)
-    );
+    return Promise.all(promises).catch(error => {
+      dispatch(receiveUser(member));
+      onFail(dispatch)(error);
+    });
   };
index ffe6fc463ce68df078468de26fd50cf172757991..3324cc4ec7714d0e10c7e4c92dc846fd3658f8f8 100644 (file)
@@ -32,11 +32,11 @@ export default class OrganizationGroupCheckbox extends React.PureComponent {
   props: Props;
 
   onCheck = (checked: boolean) => {
-    this.props.onCheck(this.props.group.id, checked);
+    this.props.onCheck(this.props.group.name, checked);
   };
 
   toggleCheck = () => {
-    this.props.onCheck(this.props.group.id, !this.props.checked);
+    this.props.onCheck(this.props.group.name, !this.props.checked);
   };
 
   render() {
index b44485481493a33b29c5788f9b9f3b9136d68491..49eac0e3fa7473fa1debccdac9d158768ab5f8fb 100644 (file)
@@ -39,6 +39,7 @@ type Props = {
   addOrganizationMember: (organizationKey: string, member: Member) => void,
   removeOrganizationMember: (organizationKey: string, member: Member) => void,
   updateOrganizationMemberGroups: (
+    organization: Organization,
     member: Member,
     add: Array<string>,
     remove: Array<string>
@@ -71,6 +72,10 @@ export default class OrganizationMembers extends React.PureComponent {
     this.props.removeOrganizationMember(this.props.organization.key, member);
   };
 
+  updateMemberGroups = (member: Member, add: Array<string>, remove: Array<string>) => {
+    this.props.updateOrganizationMemberGroups(this.props.organization, member, add, remove);
+  };
+
   render() {
     const { organization, status, members } = this.props;
     return (
@@ -89,7 +94,7 @@ export default class OrganizationMembers extends React.PureComponent {
           organizationGroups={this.props.organizationGroups}
           organization={organization}
           removeMember={this.removeMember}
-          updateMemberGroups={this.props.updateOrganizationMemberGroups}
+          updateMemberGroups={this.updateMemberGroups}
         />
         {status.total != null &&
           <ListFooter
index 2ecdb53cf0dab69b5fc8ef43411077d9df65c52c..5ee947cf614ac965df09ba1b8e43eac69f009ef8 100644 (file)
@@ -16,7 +16,7 @@ exports[`test should be able to toggle check 1`] = `
 exports[`test should be able to toggle check 2`] = `
 Array [
   Array [
-    "7",
+    "professionals",
     false,
   ],
 ]
index bcd0da072016610d79cc39e593168628d0ec3011..ccec13399d5f489117bdd8394bb5845315d43049 100644 (file)
@@ -29,7 +29,8 @@ exports[`test should not render actions for non admin 1`] = `
         "name": "Foo",
       }
     }
-    removeMember={[Function]} />
+    removeMember={[Function]}
+    updateMemberGroups={[Function]} />
   <ListFooter
     count={2}
     loadMore={[Function]}
@@ -80,7 +81,8 @@ exports[`test should render actions for admin 1`] = `
         "name": "Foo",
       }
     }
-    removeMember={[Function]} />
+    removeMember={[Function]}
+    updateMemberGroups={[Function]} />
   <ListFooter
     count={2}
     loadMore={[Function]}
index c478be943d56e508903f46608e799c56a48363b6..f1fafe1a2e0cd69d34cd136a0052f5ba1704f34d 100644 (file)
@@ -78,6 +78,7 @@ export default class AddMemberForm extends React.PureComponent {
             <div className="modal-large-field">
               <label>{translate('users.search_description')}</label>
               <UsersSelectSearch
+                autoFocus={true}
                 selectedUser={this.state.selectedMember}
                 excludedUsers={this.props.memberLogins}
                 searchUsers={searchUsers}
index 14243d37f04e58893b26179da9f6fd303fd7f142..4021f1a5ade40fcbb45a739019c638a49572a3cd 100644 (file)
@@ -60,13 +60,13 @@ export default class ManageMemberGroupsForm extends React.PureComponent {
   loadUserGroups = () => {
     this.setState({ loading: true });
     getUserGroups(this.props.member.login, this.props.organization.key).then(response => {
-      this.setState({ loading: false, userGroups: keyBy(response.groups, 'id') });
+      this.setState({ loading: false, userGroups: keyBy(response.groups, 'name') });
     });
   };
 
-  isGroupSelected = (groupId: string) => {
+  isGroupSelected = (groupName: string) => {
     if (this.state.userGroups) {
-      const group = this.state.userGroups[groupId] || {};
+      const group = this.state.userGroups[groupName] || {};
       if (group.status) {
         return group.status === 'add';
       } else {
@@ -76,17 +76,17 @@ export default class ManageMemberGroupsForm extends React.PureComponent {
     return false;
   };
 
-  onCheck = (groupId: string, checked: boolean) => {
+  onCheck = (groupName: string, checked: boolean) => {
     this.setState((prevState: State) => {
       const userGroups = prevState.userGroups || {};
-      const group = userGroups[groupId] || {};
+      const group = userGroups[groupName] || {};
       let status = '';
       if (group.selected && !checked) {
         status = 'remove';
       } else if (!group.selected && checked) {
         status = 'add';
       }
-      return { userGroups: { ...userGroups, [groupId]: { ...group, status } } };
+      return { userGroups: { ...userGroups, [groupName]: { ...group, status } } };
     });
   };
 
@@ -125,7 +125,7 @@ export default class ManageMemberGroupsForm extends React.PureComponent {
                   <OrganizationGroupCheckbox
                     key={group.id}
                     group={group}
-                    checked={this.isGroupSelected(group.id)}
+                    checked={this.isGroupSelected(group.name)}
                     onCheck={this.onCheck}
                   />
                 ))}
index 8cae5645817c77efcf9ed70dd262d6d1d3deb385..51167b86dcd9ca717b9f3061b5ee33c2d12aa5a3 100644 (file)
@@ -36,6 +36,7 @@ exports[`test should render and open the modal 2`] = `
             users.search_description
           </label>
           <UsersSelectSearch
+            autoFocus={true}
             excludedUsers={
               Array [
                 "admin",
index 4fdecd3935ea4ec5cf46b92bddfeff7dbc601be6..96cc5da6e5354e8517adf1dbeb8595555f44d8e8 100644 (file)
@@ -35,16 +35,17 @@ export type Option = {
 };
 
 type Props = {
-  selectedUser?: Option,
+  autoFocus?: boolean,
   excludedUsers: Array<string>,
+  handleValueChange: (Option) => void,
   searchUsers: (string, number) => Promise<*>,
-  handleValueChange: (Option) => void
+  selectedUser?: Option
 };
 
 type State = {
-  searchResult: Array<Option>,
   isLoading: boolean,
-  search: string
+  search: string,
+  searchResult: Array<Option>
 };
 
 const LIST_SIZE = 10;
@@ -88,6 +89,7 @@ export default class UsersSelectSearch extends React.PureComponent {
       : translate('no_results');
     return (
       <Select
+        autofocus={this.props.autoFocus}
         className="Select-big"
         options={this.state.searchResult}
         isLoading={this.state.isLoading}