aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java93
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java4
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/component/ws/search_projects-example.json9
-rw-r--r--sonar-ws/src/main/protobuf/ws-components.proto11
4 files changed, 72 insertions, 45 deletions
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java
index 88885ca3f57..32634203d72 100644
--- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java
+++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java
@@ -45,6 +45,8 @@ import org.sonar.core.platform.PlatformEditionProvider;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentQualifiers;
import org.sonar.db.component.ProjectData;
@@ -55,6 +57,7 @@ import org.sonar.db.project.ProjectDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.qualitygate.QualityGateDto;
import org.sonar.server.ai.code.assurance.AiCodeAssurance;
+import org.sonar.server.ai.code.assurance.AiCodeAssuranceEntitlement;
import org.sonar.server.ai.code.assurance.AiCodeAssuranceVerifier;
import org.sonar.server.component.ws.SearchProjectsAction.RequestBuilder;
import org.sonar.server.component.ws.SearchProjectsAction.SearchProjectsRequest;
@@ -107,6 +110,10 @@ import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALIT
import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY;
import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY;
import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY;
+import static org.sonar.server.ai.code.assurance.AiCodeAssurance.AI_CODE_ASSURANCE_FAIL;
+import static org.sonar.server.ai.code.assurance.AiCodeAssurance.AI_CODE_ASSURANCE_OFF;
+import static org.sonar.server.ai.code.assurance.AiCodeAssurance.AI_CODE_ASSURANCE_ON;
+import static org.sonar.server.ai.code.assurance.AiCodeAssurance.AI_CODE_ASSURANCE_PASS;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_002;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_003;
@@ -205,20 +212,22 @@ public class SearchProjectsActionIT {
System2.INSTANCE);
private final ProjectMeasuresIndexer projectMeasuresIndexer = new ProjectMeasuresIndexer(db.getDbClient(), es.client());
- private final AiCodeAssuranceVerifier aiCodeAssuranceVerifier = mock(AiCodeAssuranceVerifier.class);
+ private final AiCodeAssuranceEntitlement aiCodeAssuranceEntitlement = mock(AiCodeAssuranceEntitlement.class);
- private final WsActionTester ws = new WsActionTester(new SearchProjectsAction(dbClient, index, userSession, editionProviderMock, aiCodeAssuranceVerifier));
+ private WsActionTester underTest;
private final RequestBuilder request = SearchProjectsRequest.builder();
@BeforeEach
void setUp() {
- when(aiCodeAssuranceVerifier.getAiCodeAssurance(any())).thenReturn(AiCodeAssurance.NONE);
+ when(aiCodeAssuranceEntitlement.isEnabled()).thenReturn(true);
+ AiCodeAssuranceVerifier aiCodeAssuranceVerifier = new AiCodeAssuranceVerifier(aiCodeAssuranceEntitlement, db.getDbClient());
+ underTest = new WsActionTester(new SearchProjectsAction(dbClient, index, userSession, editionProviderMock, aiCodeAssuranceVerifier));
}
@Test
void verify_definition() {
- WebService.Action def = ws.getDef();
+ WebService.Action def = underTest.getDef();
assertThat(def.key()).isEqualTo("search_projects");
assertThat(def.since()).isEqualTo("6.2");
@@ -226,7 +235,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(7);
+ assertThat(def.changelog()).hasSize(9);
Param sort = def.param("s");
assertThat(sort.defaultValue()).isEqualTo("name");
@@ -270,7 +279,8 @@ public class SearchProjectsActionIT {
Param facets = def.param("facets");
assertThat(facets.defaultValue()).isNull();
- assertThat(facets.possibleValues()).containsOnly("ncloc", "duplicated_lines_density", "coverage", "sqale_rating", "reliability_rating", "security_rating", "alert_status",
+ assertThat(facets.possibleValues()).containsOnly("ncloc", "duplicated_lines_density", "coverage", "sqale_rating", "reliability_rating"
+ , "security_rating", "alert_status",
"languages", "tags", "qualifier", "new_reliability_rating", "new_security_rating", "new_maintainability_rating", "new_coverage",
"new_duplicated_lines_density", "new_lines",
"security_review_rating", "security_hotspots_reviewed", "new_security_hotspots_reviewed", "new_security_review_rating",
@@ -299,15 +309,15 @@ public class SearchProjectsActionIT {
addFavourite(db.components().getProjectDtoByMainBranch(project1));
index();
- String jsonResult = ws.newRequest()
+ String jsonResult = underTest.newRequest()
.setParam(FACETS, COVERAGE)
.setParam(FIELDS, "_all")
.execute().getInput();
- assertJson(jsonResult).ignoreFields("id").isSimilarTo(ws.getDef().responseExampleAsString());
- assertJson(ws.getDef().responseExampleAsString()).ignoreFields("id").isSimilarTo(jsonResult);
+ assertJson(jsonResult).ignoreFields("id").isSimilarTo(underTest.getDef().responseExampleAsString());
+ assertJson(underTest.getDef().responseExampleAsString()).ignoreFields("id").isSimilarTo(jsonResult);
- SearchProjectsWsResponse protobufResult = ws.newRequest()
+ SearchProjectsWsResponse protobufResult = underTest.newRequest()
.setParam(FACETS, COVERAGE)
.executeProtobuf(SearchProjectsWsResponse.class);
@@ -1381,35 +1391,41 @@ public class SearchProjectsActionIT {
tuple(publicProject.getKey(), publicProject.isPrivate() ? "private" : "public"));
}
-// TODO SC-23925
-// @ParameterizedTest
-// @MethodSource("aiCodeAssuranceParams")
-// void return_ai_code_assured(boolean containsAiCode, boolean aiCodeSupportedByQg, AiCodeAssurance expected) {
-// userSession.logIn();
-//
-// ProjectDto project = db.components().insertPublicProject(componentDto -> componentDto.setName("proj_A"),
-// projectDto -> projectDto.setContainsAiCode(containsAiCode)).getProjectDto();
-// QualityGateDto qualityGate = db.qualityGates().insertQualityGate(qg -> qg.setAiCodeSupported(aiCodeSupportedByQg));
-// db.qualityGates().associateProjectToQualityGate(project, qualityGate);
-//
-// when(aiCodeAssuranceVerifier.getAiCodeAssurance(project)).thenReturn(expected);
-// authorizationIndexerTester.allowOnlyAnyone(project);
-// index();
-//
-// SearchProjectsWsResponse result = call(request);
-//
-// boolean isAiCodeAssured = AiCodeAssurance.AI_CODE_ASSURANCE_ON.equals(expected);
-// assertThat(result.getComponentsList()).extracting(Component::getKey, Component::getIsAiCodeAssured, Component::getAiCodeAssurance)
-// .containsExactly(
-// tuple(project.getKey(), isAiCodeAssured, Components.AiCodeAssurance.valueOf(expected.name())));
-// }
+ @ParameterizedTest
+ @MethodSource("aiCodeAssuranceParams")
+ void return_ai_code_assurance(boolean containsAiCode, boolean aiCodeSupportedByQg, String qualityGateStatus, AiCodeAssurance expected) {
+ userSession.logIn();
+
+ ProjectData projectData = db.components().insertPublicProject(componentDto -> componentDto.setName("proj_A"),
+ projectDto -> projectDto.setContainsAiCode(containsAiCode));
+ ProjectDto project = projectData.getProjectDto();
+ QualityGateDto qualityGate = db.qualityGates().insertQualityGate(qg -> qg.setAiCodeSupported(aiCodeSupportedByQg));
+ db.qualityGates().associateProjectToQualityGate(project, qualityGate);
+ db.measures().insertMeasure(projectData.getMainBranchDto(), m -> m.addValue(ALERT_STATUS_KEY, qualityGateStatus));
+ authorizationIndexerTester.allowOnlyAnyone(project);
+ index();
+
+ SearchProjectsWsResponse result = call(request);
+
+ assertThat(result.getComponentsList()).extracting(Component::getKey, Component::getContainsAiCode, Component::getAiCodeAssurance)
+ .containsExactly(
+ tuple(project.getKey(), containsAiCode, Components.AiCodeAssurance.valueOf(expected.name())));
+ }
private static Stream<Arguments> aiCodeAssuranceParams() {
return Stream.of(
- Arguments.of(false, false, AiCodeAssurance.NONE),
- Arguments.of(false, true, AiCodeAssurance.NONE),
- Arguments.of(true, false, AiCodeAssurance.AI_CODE_ASSURANCE_OFF),
- Arguments.of(true, true, AiCodeAssurance.AI_CODE_ASSURANCE_ON)
+ Arguments.of(false, false, "OK", AiCodeAssurance.NONE),
+ Arguments.of(false, false, "ERROR", AiCodeAssurance.NONE),
+ Arguments.of(false, false, null, AiCodeAssurance.NONE),
+ Arguments.of(false, true, "OK", AiCodeAssurance.NONE),
+ Arguments.of(false, true, "ERROR", AiCodeAssurance.NONE),
+ Arguments.of(false, true, null, AiCodeAssurance.NONE),
+ Arguments.of(true, false, "OK", AI_CODE_ASSURANCE_OFF),
+ Arguments.of(true, false, "ERROR", AI_CODE_ASSURANCE_OFF),
+ Arguments.of(true, false, null, AI_CODE_ASSURANCE_OFF),
+ Arguments.of(true, true, null, AI_CODE_ASSURANCE_ON),
+ Arguments.of(true, true, "OK", AI_CODE_ASSURANCE_PASS),
+ Arguments.of(true, true, "ERROR", AI_CODE_ASSURANCE_FAIL)
);
}
@@ -1476,7 +1492,7 @@ public class SearchProjectsActionIT {
private SearchProjectsWsResponse call(RequestBuilder requestBuilder) {
SearchProjectsRequest wsRequest = requestBuilder.build();
- TestRequest httpRequest = ws.newRequest();
+ TestRequest httpRequest = underTest.newRequest();
ofNullable(wsRequest.getFilter()).ifPresent(filter -> httpRequest.setParam(PARAM_FILTER, filter));
ofNullable(wsRequest.getSort()).ifPresent(sort -> httpRequest.setParam(SORT, sort));
ofNullable(wsRequest.getAsc()).ifPresent(asc -> httpRequest.setParam(ASCENDING, Boolean.toString(asc)));
@@ -1512,7 +1528,8 @@ public class SearchProjectsActionIT {
return insertProject(componentConsumer, defaults(), measureConsumer);
}
- private ComponentDto insertProject(Consumer<ComponentDto> componentConsumer, Consumer<ProjectDto> projectConsumer, @Nullable Consumer<MeasureDto> measureConsumer) {
+ private ComponentDto insertProject(Consumer<ComponentDto> componentConsumer, Consumer<ProjectDto> projectConsumer,
+ @Nullable Consumer<MeasureDto> measureConsumer) {
ComponentDto project = db.components().insertPublicProject(componentConsumer, projectConsumer).getMainBranchComponent();
if (measureConsumer != null) {
db.measures().insertMeasure(project, measureConsumer);
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java
index 4bff12c870b..0f20c517db0 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java
@@ -133,6 +133,8 @@ public class SearchProjectsAction implements ComponentsWsAction {
.addPagingParams(DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE)
.setInternal(true)
.setChangelog(
+ new Change("2025.1", "Field 'containsAiCode' response field has added."),
+ new Change("2025.1", "Field 'isAiCodeAssured' response field has been removed."),
new Change("10.8", "Field 'isAiCodeAssured' response field has been deprecated. Use 'aiCodeAssurance' instead."),
new Change("10.8", "Add 'aiCodeAssurance' response field"),
new Change("10.7", "Add 'isAiCodeAssured' response field"),
@@ -494,7 +496,7 @@ public class SearchProjectsAction implements ComponentsWsAction {
.setName(dbProject.getName())
.setQualifier(dbProject.getQualifier())
.setVisibility(Visibility.getLabel(dbProject.isPrivate()))
-// .setIsAiCodeAssured(AiCodeAssurance.AI_CODE_ASSURED.equals(aiCodeAssurance)) //TODO SONAR-23925 Clean it!
+ .setContainsAiCode(dbProject.getContainsAiCode())
.setAiCodeAssurance(Components.AiCodeAssurance.valueOf(aiCodeAssurance.name()))
.setIsAiCodeFixEnabled(dbProject.getAiCodeFixEnabled());
wsComponent.getTagsBuilder().addAllTags(dbProject.getTags());
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/component/ws/search_projects-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/component/ws/search_projects-example.json
index 400a79e668e..c115ebd79db 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/component/ws/search_projects-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/component/ws/search_projects-example.json
@@ -16,7 +16,8 @@
],
"visibility": "public",
"isAiCodeFixEnabled": false,
- "aiCodeAssurance": "NONE"
+ "aiCodeAssurance": "NONE",
+ "containsAiCode": false
},
{
"key": "another_project",
@@ -26,7 +27,8 @@
"tags": [],
"visibility": "public",
"isAiCodeFixEnabled": false,
- "aiCodeAssurance": "NONE"
+ "aiCodeAssurance": "NONE",
+ "containsAiCode": false
},
{
"key": "third_project",
@@ -40,7 +42,8 @@
],
"visibility": "public",
"isAiCodeFixEnabled": false,
- "aiCodeAssurance": "NONE"
+ "aiCodeAssurance": "NONE",
+ "containsAiCode": false
}
],
"facets": [
diff --git a/sonar-ws/src/main/protobuf/ws-components.proto b/sonar-ws/src/main/protobuf/ws-components.proto
index b93c95fd32e..acfbc37474c 100644
--- a/sonar-ws/src/main/protobuf/ws-components.proto
+++ b/sonar-ws/src/main/protobuf/ws-components.proto
@@ -114,9 +114,10 @@ message Component {
optional string version = 19;
optional string pullRequest = 20;
optional bool needIssueSync = 21;
- optional bool isAiCodeAssured = 22;
+ //reserved 22
optional bool isAiCodeFixEnabled = 23;
optional AiCodeAssurance aiCodeAssurance = 24;
+ optional bool containsAiCode = 25;
message Tags {
repeated string tags = 1;
@@ -125,8 +126,12 @@ message Component {
enum AiCodeAssurance {
UNKNOWN_AI_CODE_ASSURANCE = 0;
- CONTAINS_AI_CODE = 1;
- AI_CODE_ASSURED = 2;
+ //reserved 1;
+ //reserved 2;
NONE = 3;
+ AI_CODE_ASSURANCE_ON = 4;
+ AI_CODE_ASSURANCE_OFF = 5;
+ AI_CODE_ASSURANCE_PASS = 6;
+ AI_CODE_ASSURANCE_FAIL = 7;
}