]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9000 Filter on membership in WS api/organizations/search
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Thu, 23 Nov 2017 15:51:17 +0000 (16:51 +0100)
committerTeryk Bellahsene <teryk@users.noreply.github.com>
Wed, 29 Nov 2017 16:27:53 +0000 (17:27 +0100)
server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationQuery.java
server/sonar-db-dao/src/main/resources/org/sonar/db/organization/OrganizationMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationDaoTest.java
server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchAction.java
server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchActionTest.java

index cfa220de49323d062ecba1d405ec37488432fbd4..79ded911581f58b182e2797c14461ee9d16f0d3c 100644 (file)
@@ -30,9 +30,21 @@ import static org.sonar.core.util.stream.MoreCollectors.toSet;
 public class OrganizationQuery {
   private static final OrganizationQuery NO_QUERY = newOrganizationQueryBuilder().build();
   private final Set<String> keys;
+  private final Integer userId;
 
   private OrganizationQuery(Builder builder) {
     this.keys = builder.keys;
+    this.userId = builder.member;
+  }
+
+  @CheckForNull
+  public Set<String> getKeys() {
+    return keys;
+  }
+
+  @CheckForNull
+  public Integer getMember() {
+    return userId;
   }
 
   public static OrganizationQuery returnAll() {
@@ -43,13 +55,9 @@ public class OrganizationQuery {
     return new Builder();
   }
 
-  @CheckForNull
-  public Set<String> getKeys() {
-    return keys;
-  }
-
   public static class Builder {
     private Set<String> keys;
+    private Integer member;
 
     private Builder() {
       // use static factory method
@@ -64,6 +72,11 @@ public class OrganizationQuery {
       return this;
     }
 
+    public Builder setMember(@Nullable Integer userId) {
+      this.member = userId;
+      return this;
+    }
+
     public OrganizationQuery build() {
       return new OrganizationQuery(this);
     }
index 457d2639594c169380700f761515d74fe1cc8a85..4f23af35bea600005cbfa73724924656ec82bdd2 100644 (file)
 
   <sql id="sqlSelectByQuery">
     from organizations org
+    <if test="query.member != null">
+      inner join organization_members om on org.uuid=om.organization_uuid
+        and om.user_id=#{query.member,jdbcType=INTEGER}
+    </if>
     <where>
       <if test="query.keys != null">
         org.kee in
index 127407808954b8f14b5fa264eb1feeeeca196bcb..a747b2a82bf11b58fc0aae30e53f247744355f16 100644 (file)
@@ -498,6 +498,42 @@ public class OrganizationDaoTest {
       .containsExactly("uuid1", "uuid3");
   }
 
+  @Test
+  public void selectByQuery_filter_on_a_member() {
+    OrganizationDto organization = dbTester.organizations().insert();
+    OrganizationDto anotherOrganization = dbTester.organizations().insert();
+    OrganizationDto organizationWithoutMember = dbTester.organizations().insert();
+    UserDto user = dbTester.users().insertUser();
+    dbTester.organizations().addMember(organization, user);
+    dbTester.organizations().addMember(anotherOrganization, user);
+
+    List<OrganizationDto> result = underTest.selectByQuery(dbSession, OrganizationQuery.newOrganizationQueryBuilder().setMember(user.getId()).build(), forPage(1).andSize(100));
+
+    assertThat(result).extracting(OrganizationDto::getUuid)
+      .containsExactlyInAnyOrder(organization.getUuid(), anotherOrganization.getUuid())
+      .doesNotContain(organizationWithoutMember.getUuid());
+  }
+
+  @Test
+  public void selectByQuery_filter_on_a_member_and_keys() {
+    OrganizationDto organization = dbTester.organizations().insert();
+    OrganizationDto anotherOrganization = dbTester.organizations().insert();
+    OrganizationDto organizationWithoutKeyProvided = dbTester.organizations().insert();
+    OrganizationDto organizationWithoutMember = dbTester.organizations().insert();
+    UserDto user = dbTester.users().insertUser();
+    dbTester.organizations().addMember(organization, user);
+    dbTester.organizations().addMember(anotherOrganization, user);
+    dbTester.organizations().addMember(organizationWithoutKeyProvided, user);
+
+    List<OrganizationDto> result = underTest.selectByQuery(dbSession, OrganizationQuery.newOrganizationQueryBuilder()
+      .setKeys(Arrays.asList(organization.getKey(), anotherOrganization.getKey(), organizationWithoutMember.getKey()))
+      .setMember(user.getId()).build(), forPage(1).andSize(100));
+
+    assertThat(result).extracting(OrganizationDto::getUuid)
+      .containsExactlyInAnyOrder(organization.getUuid(), anotherOrganization.getUuid())
+      .doesNotContain(organizationWithoutKeyProvided.getUuid(), organizationWithoutMember.getUuid());
+  }
+
   @Test
   public void getDefaultTemplates_returns_empty_when_table_is_empty() {
     assertThat(underTest.getDefaultTemplates(dbSession, ORGANIZATION_DTO_1.getUuid())).isEmpty();
index 957bf0d71113c08d7890af79503ec572e40503e5..edceff388b54ab957f763b29a7ce1f50b3a10432 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.organization.ws;
 
 import java.util.List;
+import javax.annotation.CheckForNull;
 import org.sonar.api.server.ws.Change;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
@@ -29,6 +30,7 @@ import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.organization.OrganizationQuery;
+import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.Organizations;
 import org.sonarqube.ws.Organizations.Organization;
 
@@ -39,16 +41,18 @@ import static org.sonar.server.ws.WsUtils.writeProtobuf;
 import static org.sonarqube.ws.Common.Paging;
 
 public class SearchAction implements OrganizationsWsAction {
-  private static final String PARAM_ORGANIZATIONS = "organizations";
-  private static final String PARAM_MEMBER = "member";
+  static final String PARAM_ORGANIZATIONS = "organizations";
+  static final String PARAM_MEMBER = "member";
   private static final String ACTION = "search";
   private static final int MAX_SIZE = 500;
 
   private final DbClient dbClient;
+  private final UserSession userSession;
   private final OrganizationsWsSupport wsSupport;
 
-  public SearchAction(DbClient dbClient, OrganizationsWsSupport wsSupport) {
+  public SearchAction(DbClient dbClient, UserSession userSession, OrganizationsWsSupport wsSupport) {
     this.dbClient = dbClient;
+    this.userSession = userSession;
     this.wsSupport = wsSupport;
   }
 
@@ -70,22 +74,30 @@ public class SearchAction implements OrganizationsWsAction {
       .setRequired(false)
       .setSince("6.3");
 
+    action.createParam(PARAM_MEMBER)
+      .setDescription("Filter organizations based on whether the authenticated user is a member. If false, no filter applies.")
+      .setSince("7.0")
+      .setDefaultValue(String.valueOf(false))
+      .setBooleanPossibleValues();
+
     action.addPagingParams(100, MAX_SIZE);
   }
 
   @Override
   public void handle(Request request, Response response) throws Exception {
     try (DbSession dbSession = dbClient.openSession(false)) {
+      Integer userId = getUserIdIfFilterMembership(request);
       List<String> organizations = getOrganizationKeys(request);
-      OrganizationQuery organizationQuery = newOrganizationQueryBuilder()
+      OrganizationQuery dbQuery = newOrganizationQueryBuilder()
         .setKeys(organizations)
+        .setMember(userId)
         .build();
 
-      int total = dbClient.organizationDao().countByQuery(dbSession, organizationQuery);
+      int total = dbClient.organizationDao().countByQuery(dbSession, dbQuery);
       Paging paging = buildWsPaging(request, total);
       List<OrganizationDto> dtos = dbClient.organizationDao().selectByQuery(
         dbSession,
-        organizationQuery,
+        dbQuery,
         forPage(paging.getPageIndex()).andSize(paging.getPageSize()));
       writeResponse(request, response, dtos, paging);
     }
@@ -107,6 +119,18 @@ public class SearchAction implements OrganizationsWsAction {
       .build();
   }
 
+  @CheckForNull
+  private Integer getUserIdIfFilterMembership(Request request) {
+    boolean filterOnAuthenticatedUser = request.mandatoryParamAsBoolean(PARAM_MEMBER);
+    if (!filterOnAuthenticatedUser) {
+      return null;
+    }
+
+    userSession.checkLoggedIn();
+    return userSession.getUserId();
+  }
+
+  @CheckForNull
   private static List<String> getOrganizationKeys(Request request) {
     List<String> organizations = request.paramAsStrings(PARAM_ORGANIZATIONS);
     if (organizations != null) {
index 62b0a59d998cdb02ac3f83a4266de278c871731b..77adf57e92ef22091e3d7d896dd63e69621951fe 100644 (file)
@@ -35,7 +35,9 @@ import org.sonar.core.util.Uuids;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.organization.OrganizationValidationImpl;
+import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestRequest;
 import org.sonar.server.ws.WsActionTester;
 import org.sonarqube.ws.Common.Paging;
@@ -47,6 +49,7 @@ import static java.lang.String.valueOf;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.sonar.server.organization.ws.SearchAction.PARAM_MEMBER;
 import static org.sonar.test.JsonAssert.assertJson;
 
 public class SearchActionTest {
@@ -64,23 +67,25 @@ public class SearchActionTest {
   private System2 system2 = mock(System2.class);
 
   @Rule
-  public DbTester dbTester = DbTester.create(system2).setDisableDefaultOrganization(true);
+  public UserSessionRule userSession = UserSessionRule.standalone();
+  @Rule
+  public DbTester db = DbTester.create(system2).setDisableDefaultOrganization(true);
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
-  private SearchAction underTest = new SearchAction(dbTester.getDbClient(), new OrganizationsWsSupport(new OrganizationValidationImpl()));
-  private WsActionTester wsTester = new WsActionTester(underTest);
+  private SearchAction underTest = new SearchAction(db.getDbClient(), userSession, new OrganizationsWsSupport(new OrganizationValidationImpl()));
+  private WsActionTester ws = new WsActionTester(underTest);
 
   @Test
-  public void verify_define() {
-    WebService.Action action = wsTester.getDef();
+  public void definition() {
+    WebService.Action action = ws.getDef();
     assertThat(action.key()).isEqualTo("search");
     assertThat(action.isPost()).isFalse();
     assertThat(action.description()).isEqualTo("Search for organizations");
     assertThat(action.isInternal()).isTrue();
     assertThat(action.since()).isEqualTo("6.2");
     assertThat(action.handler()).isEqualTo(underTest);
-    assertThat(action.params()).hasSize(3);
+    assertThat(action.params()).hasSize(4);
     assertThat(action.responseExample()).isEqualTo(getClass().getResource("search-example.json"));
 
     WebService.Param organizations = action.param("organizations");
@@ -100,6 +105,11 @@ public class SearchActionTest {
     assertThat(pageSize.defaultValue()).isEqualTo("100");
     assertThat(pageSize.maximumValue()).isEqualTo(500);
     assertThat(pageSize.description()).isEqualTo("Page size. Must be greater than 0 and less than 500");
+
+    WebService.Param member = action.param("member");
+    assertThat(member.since()).isEqualTo("7.0");
+    assertThat(member.defaultValue()).isEqualTo(String.valueOf(false));
+    assertThat(member.isRequired()).isFalse();
   }
 
   @Test
@@ -119,9 +129,12 @@ public class SearchActionTest {
       .setName("Foo Company")
       .setGuarded(true));
 
-    String response = executeJsonRequest(null, 25);
+    TestRequest request = ws.newRequest()
+      .setMediaType(MediaTypes.JSON);
+    populateRequest(request, null, 25);
+    String response = request.execute().getInput();
 
-    assertJson(response).isSimilarTo(wsTester.getDef().responseExampleAsString());
+    assertJson(response).isSimilarTo(ws.getDef().responseExampleAsString());
   }
 
   @Test
@@ -213,19 +226,19 @@ public class SearchActionTest {
     insertOrganization(ORGANIZATION_DTO.setUuid("uuid5").setKey("key-5"));
     insertOrganization(ORGANIZATION_DTO.setUuid("uuid4").setKey("key-4"));
 
-    SearchWsResponse response = executeRequest(1, 1, "key-1", "key-3", "key-5");
+    SearchWsResponse response = call(1, 1, "key-1", "key-3", "key-5");
     assertThat(response.getOrganizationsList()).extracting(Organization::getKey).containsOnly("key-5");
     assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(1, 1, 3);
 
-    response = executeRequest(1, 2, "key-1", "key-3", "key-5");
+    response = call(1, 2, "key-1", "key-3", "key-5");
     assertThat(response.getOrganizationsList()).extracting(Organization::getKey).containsOnly("key-5", "key-1");
     assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(1, 2, 3);
 
-    response = executeRequest(2, 2, "key-1", "key-3", "key-5");
+    response = call(2, 2, "key-1", "key-3", "key-5");
     assertThat(response.getOrganizationsList()).extracting(Organization::getKey).containsOnly("key-3");
     assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(2, 2, 3);
 
-    response = executeRequest(null, null);
+    response = call(null, null);
     assertThat(response.getOrganizationsList()).extracting(Organization::getKey).hasSize(5);
     assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(1, 100, 5);
   }
@@ -240,27 +253,39 @@ public class SearchActionTest {
       .containsExactly(ORGANIZATION_DTO.getKey());
   }
 
+  @Test
+  public void filter_organization_user_is_member_of() {
+    UserDto user = db.users().insertUser();
+    userSession.logIn(user);
+    OrganizationDto organization = db.organizations().insert();
+    OrganizationDto organizationWithoutMember = db.organizations().insert();
+    db.organizations().addMember(organization, user);
+
+    SearchWsResponse result = call(ws.newRequest().setParam(PARAM_MEMBER, String.valueOf(true)));
+
+    assertThat(result.getOrganizationsList()).extracting(Organization::getKey)
+      .containsExactlyInAnyOrder(organization.getKey())
+      .doesNotContain(organizationWithoutMember.getKey());
+  }
+
   private List<Organization> executeRequestAndReturnList(@Nullable Integer page, @Nullable Integer pageSize, String... keys) {
-    return executeRequest(page, pageSize, keys).getOrganizationsList();
+    return call(page, pageSize, keys).getOrganizationsList();
   }
 
-  private SearchWsResponse executeRequest(@Nullable Integer page, @Nullable Integer pageSize, String... keys) {
-    TestRequest request = wsTester.newRequest();
-    populateRequest(request, page, pageSize, keys);
+  private SearchWsResponse call(TestRequest request) {
     return request.executeProtobuf(SearchWsResponse.class);
   }
 
   private void insertOrganization(OrganizationDto dto) {
-    DbSession dbSession = dbTester.getSession();
-    dbTester.getDbClient().organizationDao().insert(dbSession, dto, false);
+    DbSession dbSession = db.getSession();
+    db.getDbClient().organizationDao().insert(dbSession, dto, false);
     dbSession.commit();
   }
 
-  private String executeJsonRequest(@Nullable Integer page, @Nullable Integer pageSize, String... keys) {
-    TestRequest request = wsTester.newRequest()
-      .setMediaType(MediaTypes.JSON);
+  private SearchWsResponse call(@Nullable Integer page, @Nullable Integer pageSize, String... keys) {
+    TestRequest request = ws.newRequest();
     populateRequest(request, page, pageSize, keys);
-    return request.execute().getInput();
+    return call(request);
   }
 
   private void populateRequest(TestRequest request, @Nullable Integer page, @Nullable Integer pageSize, String... keys) {