]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9000 Add admin action to WS api/organizations/search
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Fri, 24 Nov 2017 09:24:41 +0000 (10:24 +0100)
committerTeryk Bellahsene <teryk@users.noreply.github.com>
Wed, 29 Nov 2017 16:27:53 +0000 (17:27 +0100)
server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java
server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchAction.java
server/sonar-server/src/main/resources/org/sonar/server/organization/ws/search-example.json
server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchActionTest.java
sonar-ws-generated/src/main/protobuf/ws-organizations.proto

index 7fff042cb82cdb70f326bf8b428ed5d0a527c926..cd4981c921bbdb70687a5742fc2fb0d127378bd7 100644 (file)
@@ -24,7 +24,7 @@ import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.server.organization.OrganizationValidation;
-import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.Organizations.Organization;
 
 import static org.sonar.core.util.Protobuf.setNullable;
 import static org.sonar.server.organization.OrganizationValidation.DESCRIPTION_MAX_LENGTH;
@@ -107,19 +107,18 @@ public class OrganizationsWsSupport {
       .setExampleValue("https://www.foo.com/foo.png");
   }
 
-  Organizations.Organization toOrganization(OrganizationDto dto) {
-    return toOrganization(Organizations.Organization.newBuilder(), dto);
+  Organization.Builder toOrganization(OrganizationDto dto) {
+    return toOrganization(Organization.newBuilder(), dto);
   }
 
-  Organizations.Organization toOrganization(Organizations.Organization.Builder builder, OrganizationDto dto) {
+  Organization.Builder toOrganization(Organization.Builder builder, OrganizationDto dto) {
     builder
-      .clear()
       .setName(dto.getName())
       .setKey(dto.getKey())
       .setGuarded(dto.isGuarded());
     setNullable(dto.getDescription(), builder::setDescription);
     setNullable(dto.getUrl(), builder::setUrl);
     setNullable(dto.getAvatarUrl(), builder::setAvatar);
-    return builder.build();
+    return builder;
   }
 }
index edceff388b54ab957f763b29a7ce1f50b3a10432..0adba4dfacd160455264eebbea2003a8739b788e 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.organization.ws;
 
 import java.util.List;
+import java.util.Set;
 import javax.annotation.CheckForNull;
 import org.sonar.api.server.ws.Change;
 import org.sonar.api.server.ws.Request;
@@ -34,9 +35,11 @@ import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.Organizations;
 import org.sonarqube.ws.Organizations.Organization;
 
+import static java.util.Collections.emptySet;
+import static org.sonar.core.util.stream.MoreCollectors.toSet;
 import static org.sonar.db.Pagination.forPage;
 import static org.sonar.db.organization.OrganizationQuery.newOrganizationQueryBuilder;
-import static org.sonar.server.ws.WsUtils.checkRequest;
+import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
 import static org.sonarqube.ws.Common.Paging;
 
@@ -70,7 +73,7 @@ public class SearchAction implements OrganizationsWsAction {
     action.createParam(PARAM_ORGANIZATIONS)
       .setDescription("Comma-separated list of organization keys")
       .setExampleValue(String.join(",", "my-org-1", "foocorp"))
-      .setMinimumLength(2)
+      .setMaxValuesAllowed(MAX_SIZE)
       .setRequired(false)
       .setSince("6.3");
 
@@ -86,29 +89,40 @@ public class SearchAction implements OrganizationsWsAction {
   @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 dbQuery = newOrganizationQueryBuilder()
-        .setKeys(organizations)
-        .setMember(userId)
-        .build();
-
+      OrganizationQuery dbQuery = buildDbQuery(request);
       int total = dbClient.organizationDao().countByQuery(dbSession, dbQuery);
       Paging paging = buildWsPaging(request, total);
-      List<OrganizationDto> dtos = dbClient.organizationDao().selectByQuery(
-        dbSession,
-        dbQuery,
-        forPage(paging.getPageIndex()).andSize(paging.getPageSize()));
-      writeResponse(request, response, dtos, paging);
+      List<OrganizationDto> organizations = dbClient.organizationDao().selectByQuery(dbSession, dbQuery, forPage(paging.getPageIndex()).andSize(paging.getPageSize()));
+      Set<String> adminOrganizationUuids = searchOrganizationWithAdminPermission(dbSession);
+      writeResponse(request, response, organizations, adminOrganizationUuids, paging);
     }
   }
 
-  private void writeResponse(Request request, Response response, List<OrganizationDto> dtos, Paging paging) {
-    Organizations.SearchWsResponse.Builder responseBuilder = Organizations.SearchWsResponse.newBuilder();
-    responseBuilder.setPaging(paging);
-    Organization.Builder organizationBuilder = Organization.newBuilder();
-    dtos.forEach(dto -> responseBuilder.addOrganizations(wsSupport.toOrganization(organizationBuilder, dto)));
-    writeProtobuf(responseBuilder.build(), request, response);
+  private OrganizationQuery buildDbQuery(Request request) {
+    return newOrganizationQueryBuilder()
+      .setKeys(request.paramAsStrings(PARAM_ORGANIZATIONS))
+      .setMember(getUserIdIfFilterOnMembership(request))
+      .build();
+  }
+
+  private Set<String> searchOrganizationWithAdminPermission(DbSession dbSession) {
+    Integer userId = userSession.getUserId();
+    return userId == null ? emptySet()
+      : dbClient.organizationDao().selectByPermission(dbSession, userId, ADMINISTER.getKey()).stream().map(OrganizationDto::getUuid).collect(toSet());
+  }
+
+  private void writeResponse(Request httpRequest, Response httpResponse, List<OrganizationDto> organizations, Set<String> adminOrganizationUuids, Paging paging) {
+    Organizations.SearchWsResponse.Builder response = Organizations.SearchWsResponse.newBuilder();
+    response.setPaging(paging);
+    Organization.Builder wsOrganization = Organization.newBuilder();
+    organizations
+      .forEach(o -> {
+        boolean isAdmin = adminOrganizationUuids.contains(o.getUuid());
+        wsOrganization.clear();
+        wsOrganization.setIsAdmin(isAdmin);
+        response.addOrganizations(wsSupport.toOrganization(wsOrganization, o));
+      });
+    writeProtobuf(response.build(), httpRequest, httpResponse);
   }
 
   private static Paging buildWsPaging(Request request, int total) {
@@ -120,23 +134,8 @@ public class SearchAction implements OrganizationsWsAction {
   }
 
   @CheckForNull
-  private Integer getUserIdIfFilterMembership(Request request) {
+  private Integer getUserIdIfFilterOnMembership(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) {
-      checkRequest(organizations.size() <= MAX_SIZE, "Size of '%s' (%d) must be less than %d", PARAM_ORGANIZATIONS, organizations.size(), MAX_SIZE);
-    }
-    return organizations;
+    return (userSession.isLoggedIn() && filterOnAuthenticatedUser) ? userSession.getUserId() : null;
   }
-
 }
index 84a79a7e84a8a3d337748055e580aa658e031191..481ae34e9ef10b209aa2265a699857d874cd17cc 100644 (file)
@@ -8,7 +8,8 @@
     {
       "key": "foo-company",
       "name": "Foo Company",
-      "guarded": true
+      "guarded": true,
+      "isAdmin": false
     },
     {
       "key": "bar-company",
@@ -16,7 +17,8 @@
       "description": "The Bar company produces quality software too.",
       "url": "https://www.bar.com",
       "avatar": "https://www.bar.com/logo.png",
-      "guarded": false
+      "guarded": false,
+      "isAdmin": true
     }
   ]
 }
index 77adf57e92ef22091e3d7d896dd63e69621951fe..8eedbae42ecab9e46f9dc88af10057e0cf072f8c 100644 (file)
@@ -32,9 +32,9 @@ import org.junit.rules.ExpectedException;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.System2;
 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.GroupDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.organization.OrganizationValidationImpl;
 import org.sonar.server.tester.UserSessionRule;
@@ -47,8 +47,10 @@ import org.sonarqube.ws.Organizations.SearchWsResponse;
 
 import static java.lang.String.valueOf;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
 import static org.sonar.server.organization.ws.SearchAction.PARAM_MEMBER;
 import static org.sonar.test.JsonAssert.assertJson;
 
@@ -94,6 +96,7 @@ public class SearchActionTest {
     assertThat(organizations.description()).isEqualTo("Comma-separated list of organization keys");
     assertThat(organizations.exampleValue()).isEqualTo("my-org-1,foocorp");
     assertThat(organizations.since()).isEqualTo("6.3");
+    assertThat(organizations.maxValuesAllowed()).isEqualTo(500);
 
     WebService.Param page = action.param("p");
     assertThat(page.isRequired()).isFalse();
@@ -113,9 +116,9 @@ public class SearchActionTest {
   }
 
   @Test
-  public void verify_response_example() throws URISyntaxException, IOException {
+  public void json_example() throws URISyntaxException, IOException {
     when(system2.now()).thenReturn(SOME_DATE, SOME_DATE + 1000);
-    insertOrganization(new OrganizationDto()
+    OrganizationDto barOrganization = db.organizations().insert(new OrganizationDto()
       .setUuid(Uuids.UUID_EXAMPLE_02)
       .setKey("bar-company")
       .setName("Bar Company")
@@ -123,18 +126,44 @@ public class SearchActionTest {
       .setUrl("https://www.bar.com")
       .setAvatarUrl("https://www.bar.com/logo.png")
       .setGuarded(false));
-    insertOrganization(new OrganizationDto()
+    OrganizationDto fooOrganization = db.organizations().insert(new OrganizationDto()
       .setUuid(Uuids.UUID_EXAMPLE_01)
       .setKey("foo-company")
       .setName("Foo Company")
       .setGuarded(true));
+    UserDto user = db.users().insertUser();
+    db.organizations().addMember(barOrganization, user);
+    db.organizations().addMember(fooOrganization, user);
+    db.users().insertPermissionOnUser(barOrganization, user, ADMINISTER);
+    userSession.logIn(user).addPermission(ADMINISTER, barOrganization);
 
     TestRequest request = ws.newRequest()
       .setMediaType(MediaTypes.JSON);
     populateRequest(request, null, 25);
-    String response = request.execute().getInput();
+    String result = request.execute().getInput();
 
-    assertJson(response).isSimilarTo(ws.getDef().responseExampleAsString());
+    assertJson(ws.getDef().responseExampleAsString()).isSimilarTo(result);
+    assertJson(result).isSimilarTo(ws.getDef().responseExampleAsString());
+  }
+
+  @Test
+  public void is_admin_available_for_each_organization() {
+    OrganizationDto userAdminOrganization = db.organizations().insert();
+    OrganizationDto groupAdminOrganization = db.organizations().insert();
+    OrganizationDto browseOrganization = db.organizations().insert();
+    UserDto user = db.users().insertUser();
+    GroupDto group = db.users().insertGroup(groupAdminOrganization);
+    db.users().insertMember(group, user);
+    userSession.logIn(user).addPermission(ADMINISTER, userAdminOrganization);
+    db.users().insertPermissionOnUser(userAdminOrganization, user, ADMINISTER);
+    db.users().insertPermissionOnGroup(group, ADMINISTER);
+
+    SearchWsResponse result = call(ws.newRequest());
+
+    assertThat(result.getOrganizationsList()).extracting(Organization::getKey, Organization::getIsAdmin).containsExactlyInAnyOrder(
+      tuple(userAdminOrganization.getKey(), true),
+      tuple(browseOrganization.getKey(), false),
+      tuple(groupAdminOrganization.getKey(), true));
   }
 
   @Test
@@ -150,7 +179,7 @@ public class SearchActionTest {
   @Test
   public void request_returns_empty_on_table_with_single_row_when_not_requesting_the_first_page() {
     when(system2.now()).thenReturn(SOME_DATE);
-    insertOrganization(ORGANIZATION_DTO);
+    db.organizations().insert(ORGANIZATION_DTO);
 
     assertThat(executeRequestAndReturnList(2, null)).isEmpty();
     assertThat(executeRequestAndReturnList(2, 1)).isEmpty();
@@ -162,11 +191,11 @@ public class SearchActionTest {
   @Test
   public void request_returns_rows_ordered_by_createdAt_descending_applying_requested_paging() {
     when(system2.now()).thenReturn(SOME_DATE, SOME_DATE + 1_000, SOME_DATE + 2_000, SOME_DATE + 3_000, SOME_DATE + 5_000);
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid3").setKey("key-3"));
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid1").setKey("key-1"));
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid2").setKey("key-2"));
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid5").setKey("key-5"));
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid4").setKey("key-4"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid3").setKey("key-3"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid1").setKey("key-1"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid2").setKey("key-2"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid5").setKey("key-5"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid4").setKey("key-4"));
 
     assertThat(executeRequestAndReturnList(1, 1))
       .extracting(Organization::getKey)
@@ -202,11 +231,11 @@ public class SearchActionTest {
   @Test
   public void request_returns_only_specified_keys_ordered_by_createdAt_when_filtering_keys() {
     when(system2.now()).thenReturn(SOME_DATE, SOME_DATE + 1_000, SOME_DATE + 2_000, SOME_DATE + 3_000, SOME_DATE + 5_000);
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid3").setKey("key-3"));
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid1").setKey("key-1"));
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid2").setKey("key-2"));
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid5").setKey("key-5"));
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid4").setKey("key-4"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid3").setKey("key-3"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid1").setKey("key-1"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid2").setKey("key-2"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid5").setKey("key-5"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid4").setKey("key-4"));
 
     assertThat(executeRequestAndReturnList(1, 10, "key-3", "key-1", "key-5"))
       .extracting(Organization::getKey)
@@ -220,11 +249,11 @@ public class SearchActionTest {
   @Test
   public void result_is_paginated() {
     when(system2.now()).thenReturn(SOME_DATE, SOME_DATE + 1_000, SOME_DATE + 2_000, SOME_DATE + 3_000, SOME_DATE + 5_000);
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid3").setKey("key-3"));
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid1").setKey("key-1"));
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid2").setKey("key-2"));
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid5").setKey("key-5"));
-    insertOrganization(ORGANIZATION_DTO.setUuid("uuid4").setKey("key-4"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid3").setKey("key-3"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid1").setKey("key-1"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid2").setKey("key-2"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid5").setKey("key-5"));
+    db.organizations().insert(ORGANIZATION_DTO.setUuid("uuid4").setKey("key-4"));
 
     SearchWsResponse response = call(1, 1, "key-1", "key-3", "key-5");
     assertThat(response.getOrganizationsList()).extracting(Organization::getKey).containsOnly("key-5");
@@ -246,7 +275,7 @@ public class SearchActionTest {
   @Test
   public void request_returns_empty_when_filtering_on_non_existing_key() {
     when(system2.now()).thenReturn(SOME_DATE);
-    insertOrganization(ORGANIZATION_DTO);
+    db.organizations().insert(ORGANIZATION_DTO);
 
     assertThat(executeRequestAndReturnList(1, 10, ORGANIZATION_DTO.getKey()))
       .extracting(Organization::getKey)
@@ -276,12 +305,6 @@ public class SearchActionTest {
     return request.executeProtobuf(SearchWsResponse.class);
   }
 
-  private void insertOrganization(OrganizationDto dto) {
-    DbSession dbSession = db.getSession();
-    db.getDbClient().organizationDao().insert(dbSession, dto, false);
-    dbSession.commit();
-  }
-
   private SearchWsResponse call(@Nullable Integer page, @Nullable Integer pageSize, String... keys) {
     TestRequest request = ws.newRequest();
     populateRequest(request, page, pageSize, keys);
index f6f2733c7720ae5715f188ad09413efb7b20a436..172bf31d85c966393c2352ab070f053dff76d245 100644 (file)
@@ -60,6 +60,7 @@ message Organization {
   optional string url = 4;
   optional string avatar = 5;
   optional bool guarded = 6;
+  optional bool isAdmin = 7;
 }
 
 message User {