]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22559 Add method to fetch projects' members from GitLab
authorAurelien Poscia <aurelien.poscia@sonarsource.com>
Tue, 6 Aug 2024 13:47:32 +0000 (15:47 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 19 Aug 2024 20:02:44 +0000 (20:02 +0000)
server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabApplicationClient.java
server/sonar-alm-client/src/test/java/org/sonar/alm/client/gitlab/GitlabApplicationClientTest.java
server/sonar-alm-client/src/test/resources/org/sonar/alm/client/gitlab/project-members-full-response.json [new file with mode: 0644]
server/sonar-auth-gitlab/src/main/java/org/sonar/auth/gitlab/GsonProjectMember.java [new file with mode: 0644]

index 4db9851c99da56c57aec30105a3830728e714fb4..47178442d52b48b84ab82f3ee7af36c5084da689 100644 (file)
@@ -26,7 +26,6 @@ import com.google.gson.JsonSyntaxException;
 import com.google.gson.reflect.TypeToken;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
-import java.lang.reflect.Type;
 import java.net.URLEncoder;
 import java.util.Arrays;
 import java.util.List;
@@ -45,6 +44,7 @@ import org.slf4j.LoggerFactory;
 import org.sonar.alm.client.TimeoutConfiguration;
 import org.sonar.api.server.ServerSide;
 import org.sonar.auth.gitlab.GsonGroup;
+import org.sonar.auth.gitlab.GsonProjectMember;
 import org.sonar.auth.gitlab.GsonUser;
 import org.sonarqube.ws.MediaTypes;
 import org.sonarqube.ws.client.OkHttpClientBuilder;
@@ -62,6 +62,8 @@ public class GitlabApplicationClient {
   };
   private static final TypeToken<List<GsonUser>> GITLAB_USER = new TypeToken<>() {
   };
+  private static final TypeToken<List<GsonProjectMember>> GITLAB_PROJECT_MEMBER = new TypeToken<>() {
+  };
 
   protected static final String PRIVATE_TOKEN = "Private-Token";
   private static final String GITLAB_GROUPS_MEMBERS_ENDPOINT = "/groups/%s/members";
@@ -370,9 +372,13 @@ public class GitlabApplicationClient {
     return Set.copyOf(executePaginatedQuery(gitlabUrl, token, endpoint, resp -> GSON.fromJson(resp, GITLAB_USER)));
   }
 
+  public Set<GsonProjectMember> getAllProjectMembers(String gitlabUrl, String token, long projectId) {
+    String url = format("/projects/%s/members/all", projectId);
+    return Set.copyOf(executePaginatedQuery(gitlabUrl, token, url, resp -> GSON.fromJson(resp, GITLAB_PROJECT_MEMBER)));
+  }
+
   private <E> List<E> executePaginatedQuery(String appUrl, String token, String query, Function<String, List<E>> responseDeserializer) {
     GitlabToken gitlabToken = new GitlabToken(token);
     return gitlabPaginatedHttpClient.get(appUrl, gitlabToken, query, responseDeserializer);
   }
-
 }
index 37cc5f9defbdfa05afaf484a7379cc20ebdfdecb..afa35587b88fde93d90e01786753246a1f04ff6b 100644 (file)
@@ -40,6 +40,7 @@ import org.sonar.alm.client.ConstantTimeoutConfiguration;
 import org.sonar.alm.client.TimeoutConfiguration;
 import org.sonar.api.testfixtures.log.LogTester;
 import org.sonar.auth.gitlab.GsonGroup;
+import org.sonar.auth.gitlab.GsonProjectMember;
 import org.sonar.auth.gitlab.GsonUser;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -659,6 +660,47 @@ public class GitlabApplicationClientTest {
     return gsonUser;
   }
 
+  @Test
+  public void getAllProjectMembers_whenCallIsSuccesfull_deserializesAndReturnsCorrectlyProjectsMembers() throws IOException {
+    ArgumentCaptor<Function<String, List<GsonProjectMember>>> deserializerCaptor = ArgumentCaptor.forClass(Function.class);
+
+    String token = "token-toto";
+    GitlabToken gitlabToken = new GitlabToken(token);
+    List<GsonProjectMember> expectedProjectMembers = expectedProjectMembers();
+    when(gitlabPaginatedHttpClient.get(eq(gitlabUrl), eq(gitlabToken), eq("/projects/42/members/all"), deserializerCaptor.capture())).thenReturn(expectedProjectMembers);
+
+    Set<GsonProjectMember> actualProjectMembers = underTest.getAllProjectMembers(gitlabUrl, token, 42);
+    assertThat(actualProjectMembers).containsExactlyInAnyOrderElementsOf(expectedProjectMembers);
+
+    String responseContent = getResponseContent("project-members-full-response.json");
+
+    List<GsonProjectMember> deserializedProjectMembers = deserializerCaptor.getValue().apply(responseContent);
+    assertThat(deserializedProjectMembers).isEqualTo(expectedProjectMembers);
+
+  }
+
+  @Test
+  public void getAllProjectMembers_whenCallIsInError_rethrows() {
+    String token = "token-toto";
+    GitlabToken gitlabToken = new GitlabToken(token);
+    List<GsonProjectMember> expectedProjectMembers = expectedProjectMembers();
+    when(gitlabPaginatedHttpClient.get(eq(gitlabUrl), eq(gitlabToken), eq("/projects/42/members/all"), any())).thenThrow(new IllegalStateException("exception"));
+
+    assertThatIllegalStateException()
+      .isThrownBy(() -> underTest.getAllProjectMembers(gitlabUrl, token, 42))
+      .withMessage("exception");
+  }
+
+  private static List<GsonProjectMember> expectedProjectMembers() {
+    GsonProjectMember user1 = createGsonProjectMember(12818153, 5);
+    GsonProjectMember user2 = createGsonProjectMember(22330087, 50);
+    return List.of(user1, user2);
+  }
+
+  private static GsonProjectMember createGsonProjectMember(int id, int accessLevel) {
+    return new GsonProjectMember(id, accessLevel);
+  }
+
   private static String getResponseContent(String path) throws IOException {
     return IOUtils.toString(GitlabApplicationClientTest.class.getResourceAsStream(path), StandardCharsets.UTF_8);
   }
diff --git a/server/sonar-alm-client/src/test/resources/org/sonar/alm/client/gitlab/project-members-full-response.json b/server/sonar-alm-client/src/test/resources/org/sonar/alm/client/gitlab/project-members-full-response.json
new file mode 100644 (file)
index 0000000..aed1ada
--- /dev/null
@@ -0,0 +1,37 @@
+[
+  {
+    "id": 12818153,
+    "username": "aurelien-poscia-sonarsource",
+    "name": "AurĂ©lien Poscia 2",
+    "state": "active",
+    "locked": false,
+    "avatar_url": "https://secure.gravatar.com/avatar/171fd98c95e67a87cb7bd05a42842adebd14818d7e7d6d5c838225e9240e01b0?s=80&d=identicon",
+    "web_url": "https://gitlab.com/aurelien-poscia-sonarsource",
+    "access_level": 5,
+    "created_at": "2023-06-01T08:22:56.677Z",
+    "created_by": {
+      "id": 12818153,
+      "username": "aurelien-poscia-sonarsource",
+      "name": "AurĂ©lien Poscia 2",
+      "state": "active",
+      "locked": false,
+      "avatar_url": "https://secure.gravatar.com/avatar/171fd98c95e67a87cb7bd05a42842adebd14818d7e7d6d5c838225e9240e01b0?s=80&d=identicon",
+      "web_url": "https://gitlab.com/aurelien-poscia-sonarsource"
+    },
+    "expires_at": null,
+    "membership_state": "active"
+  },
+  {
+    "id": 22330087,
+    "username": "project_46494175_bot_d518a7d5f68ba2e623d8dba785ca8b5f",
+    "name": "Guillaume Peoch",
+    "state": "active",
+    "locked": false,
+    "avatar_url": "https://secure.gravatar.com/avatar/60254275467af5e8861e2d91a3e3a09a5819fafaa00f34813b9ebef61f9bb44b?s=80&d=identicon",
+    "web_url": "https://gitlab.com/project_46494175_bot_d518a7d5f68ba2e623d8dba785ca8b5f",
+    "access_level": 50,
+    "created_at": "2024-08-05T14:38:37.647Z",
+    "expires_at": "2024-09-04",
+    "membership_state": "active"
+  }
+]
diff --git a/server/sonar-auth-gitlab/src/main/java/org/sonar/auth/gitlab/GsonProjectMember.java b/server/sonar-auth-gitlab/src/main/java/org/sonar/auth/gitlab/GsonProjectMember.java
new file mode 100644 (file)
index 0000000..14a5965
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.auth.gitlab;
+
+import com.google.gson.annotations.SerializedName;
+
+public record GsonProjectMember (
+  @SerializedName("id")
+  long id,
+  @SerializedName("access_level")
+  int accessLevel
+){
+}