]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-23064 Enhance search projects response with isAiCodeAssured flag (#11774)
authorAnita Stanisz <106669481+anita-stanisz-sonarsource@users.noreply.github.com>
Thu, 19 Sep 2024 09:43:31 +0000 (11:43 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 25 Sep 2024 20:02:53 +0000 (20:02 +0000)
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/component/ws/search_projects-example.json
sonar-ws/src/main/protobuf/ws-components.proto

index ca96953776b25793684fc5d3fab71f43f11585fc..cfa55b434e58410ac0012cd49a02ce42aa8b8c31 100644 (file)
@@ -193,6 +193,11 @@ public class SearchProjectsActionIT {
     };
   }
 
+  @DataProvider
+  public static List<Edition> editions_supporting_ai_code_assurance() {
+    return List.of(Edition.DEVELOPER, Edition.ENTERPRISE, Edition.DATACENTER);
+  }
+
   private final DbClient dbClient = db.getDbClient();
   private final DbSession dbSession = db.getSession();
 
@@ -217,7 +222,7 @@ public class SearchProjectsActionIT {
     assertThat(def.isPost()).isFalse();
     assertThat(def.responseExampleAsString()).isNotEmpty();
     assertThat(def.params().stream().map(Param::key).toList()).containsOnly("filter", "facets", "s", "asc", "ps", "p", "f");
-    assertThat(def.changelog()).hasSize(4);
+    assertThat(def.changelog()).hasSize(5);
 
     Param sort = def.param("s");
     assertThat(sort.defaultValue()).isEqualTo("name");
@@ -1386,6 +1391,47 @@ public class SearchProjectsActionIT {
         tuple(publicProject.getKey(), publicProject.isPrivate() ? "private" : "public"));
   }
 
+  @Test
+  public void ai_code_assured_is_always_false_for_community_edition() {
+    when(editionProviderMock.get()).thenReturn(Optional.of(Edition.COMMUNITY));
+    userSession.logIn();
+    ProjectDto aiAssuredProject = db.components().insertPublicProject(componentDto -> componentDto.setName("proj_A"),
+      projectDto -> projectDto.setAiCodeAssurance(true)).getProjectDto();
+    authorizationIndexerTester.allowOnlyAnyone(aiAssuredProject);
+    ProjectDto notAiAssuredProject = db.components().insertPrivateProject(componentDto -> componentDto.setName("proj_B"),
+      projectDto -> projectDto.setAiCodeAssurance(false)).getProjectDto();
+    authorizationIndexerTester.allowOnlyAnyone(notAiAssuredProject);
+    index();
+
+    SearchProjectsWsResponse result = call(request);
+
+    assertThat(result.getComponentsList()).extracting(Component::getKey, Component::getIsAiCodeAssured)
+      .containsExactly(
+        tuple(aiAssuredProject.getKey(), false),
+        tuple(notAiAssuredProject.getKey(), false));
+  }
+
+  @Test
+  @UseDataProvider("editions_supporting_ai_code_assurance")
+  public void return_ai_code_assured(Edition edition) {
+    when(editionProviderMock.get()).thenReturn(Optional.of(edition));
+    userSession.logIn();
+    ProjectDto aiAssuredProject = db.components().insertPublicProject(componentDto -> componentDto.setName("proj_A"),
+      projectDto -> projectDto.setAiCodeAssurance(true)).getProjectDto();
+    authorizationIndexerTester.allowOnlyAnyone(aiAssuredProject);
+    ProjectDto notAiAssuredProject = db.components().insertPrivateProject(componentDto -> componentDto.setName("proj_B"),
+      projectDto -> projectDto.setAiCodeAssurance(false)).getProjectDto();
+    authorizationIndexerTester.allowOnlyAnyone(notAiAssuredProject);
+    index();
+
+    SearchProjectsWsResponse result = call(request);
+
+    assertThat(result.getComponentsList()).extracting(Component::getKey, Component::getIsAiCodeAssured)
+      .containsExactly(
+        tuple(aiAssuredProject.getKey(), true),
+        tuple(notAiAssuredProject.getKey(), false));
+  }
+
   @Test
   public void does_not_return_branches() {
     ProjectDto project = db.components().insertPublicProject().getProjectDto();
index 6d4c8ccb05e7320fba88a58efce19a7410db946e..a0427f9ff86434b60a9b4f788da8f988640fcde9 100644 (file)
@@ -128,6 +128,7 @@ public class SearchProjectsAction implements ComponentsWsAction {
       .addPagingParams(DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE)
       .setInternal(true)
       .setChangelog(
+        new Change("10.7", "Add 'isAiCodeAssured' response field"),
         new Change("10.3", "Add 'creationDate' sort parameter."),
         new Change("10.2", "Field 'needIssueSync' removed from response"),
         new Change("8.3", "Add 'qualifier' filter and facet"),
@@ -367,7 +368,8 @@ public class SearchProjectsAction implements ComponentsWsAction {
   }
 
   private SearchProjectsWsResponse buildResponse(SearchProjectsRequest request, SearchResults searchResults) {
-    Function<ProjectDto, Component> dbToWsComponent = new DbToWsComponent(request, searchResults, userSession.isLoggedIn());
+    Function<ProjectDto, Component> dbToWsComponent = new DbToWsComponent(request, searchResults, userSession.isLoggedIn(),
+      editionProvider.get().orElse(null));
 
     return Stream.of(SearchProjectsWsResponse.newBuilder())
       .map(response -> response.setPaging(Common.Paging.newBuilder()
@@ -467,14 +469,16 @@ public class SearchProjectsAction implements ComponentsWsAction {
     private final boolean isUserLoggedIn;
     private final Map<String, SnapshotDto> analysisByProjectUuid;
     private final Map<String, Long> applicationsLeakPeriod;
+    private final Edition edition;
 
-    private DbToWsComponent(SearchProjectsRequest request, SearchResults searchResults, boolean isUserLoggedIn) {
+    private DbToWsComponent(SearchProjectsRequest request, SearchResults searchResults, boolean isUserLoggedIn, @Nullable Edition edition) {
       this.request = request;
       this.analysisByProjectUuid = searchResults.analysisByProjectUuid;
       this.applicationsLeakPeriod = searchResults.applicationsLeakPeriods;
       this.wsComponent = Component.newBuilder();
       this.favoriteProjectUuids = searchResults.favoriteProjectUuids;
       this.isUserLoggedIn = isUserLoggedIn;
+      this.edition = edition;
     }
 
     @Override
@@ -484,7 +488,8 @@ public class SearchProjectsAction implements ComponentsWsAction {
         .setKey(dbProject.getKey())
         .setName(dbProject.getName())
         .setQualifier(dbProject.getQualifier())
-        .setVisibility(Visibility.getLabel(dbProject.isPrivate()));
+        .setVisibility(Visibility.getLabel(dbProject.isPrivate()))
+        .setIsAiCodeAssured(isAiCodeAssured(dbProject));
       wsComponent.getTagsBuilder().addAllTags(dbProject.getTags());
 
       SnapshotDto snapshotDto = analysisByProjectUuid.get(dbProject.getUuid());
@@ -507,6 +512,14 @@ public class SearchProjectsAction implements ComponentsWsAction {
 
       return wsComponent.build();
     }
+
+    /**
+     * Make sure that for {@link Edition#COMMUNITY} we'll always get false, no matter of the value in database.
+     * This is to support correctly downgraded instances.
+     */
+    private boolean isAiCodeAssured(ProjectDto dbProject) {
+      return edition != null && !edition.equals(Edition.COMMUNITY) && dbProject.getAiCodeAssurance();
+    }
   }
 
   public static class SearchResults {
@@ -518,7 +531,8 @@ public class SearchProjectsAction implements ComponentsWsAction {
     private final ProjectMeasuresQuery query;
     private final int total;
 
-    private SearchResults(List<ProjectDto> projects, Set<String> favoriteProjectUuids, SearchIdResult<String> searchResults, Map<String, SnapshotDto> analysisByProjectUuid,
+    private SearchResults(List<ProjectDto> projects, Set<String> favoriteProjectUuids, SearchIdResult<String> searchResults, Map<String,
+      SnapshotDto> analysisByProjectUuid,
       Map<String, Long> applicationsLeakPeriods, ProjectMeasuresQuery query) {
       this.projects = projects;
       this.favoriteProjectUuids = favoriteProjectUuids;
index 44890a1a2383ca171533d11046b2654ea455decb..f888de5a7025c450db04c4b10726a680c5fc2226 100644 (file)
@@ -14,7 +14,8 @@
         "finance",
         "java"
       ],
-      "visibility": "public"
+      "visibility": "public",
+      "isAiCodeAssured": false
     },
     {
       "key": "another_project",
@@ -22,7 +23,8 @@
       "qualifier": "TRK",
       "isFavorite": false,
       "tags": [],
-      "visibility": "public"
+      "visibility": "public",
+      "isAiCodeAssured": false
     },
     {
       "key": "third_project",
@@ -34,7 +36,8 @@
         "offshore",
         "java"
       ],
-      "visibility": "public"
+      "visibility": "public",
+      "isAiCodeAssured": false
     }
   ],
   "facets": [
index d51a8a2805bec0eeed82b9b85b45d273ba9541c7..b0ec074464ace6b9e64fa78400526efe09213910 100644 (file)
@@ -114,6 +114,7 @@ message Component {
   optional string version = 19;
   optional string pullRequest = 20;
   optional bool needIssueSync = 21;
+  optional bool isAiCodeAssured = 22;
 
   message Tags {
     repeated string tags = 1;