aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java2
-rw-r--r--server/sonar-server-common/src/it/java/org/sonar/server/issue/index/IssueIteratorFactoryIT.java4
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java12
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueDoc.java10
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIndexDefinition.java2
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java9
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java4
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java8
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java11
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java3
-rw-r--r--server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java17
-rw-r--r--server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java14
-rw-r--r--server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java5
-rw-r--r--server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryTest.java2
-rw-r--r--server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/ShowActionIT.java18
-rw-r--r--server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java28
-rw-r--r--server/sonar-webserver-webapi/src/it/resources/org/sonar/server/issue/ws/SearchActionIT/search_by_variants_with_facets.json89
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/ShowAction.java2
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java15
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java1
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/hotspot/ws/show-example.json6
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/search-example.json6
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java1
-rw-r--r--sonar-ws/src/main/protobuf/ws-hotspots.proto1
-rw-r--r--sonar-ws/src/main/protobuf/ws-issues.proto2
26 files changed, 252 insertions, 21 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java
index d36c839314b..81104ebd175 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java
@@ -50,7 +50,7 @@ public final class IssueDto implements Serializable {
public static final int AUTHOR_MAX_SIZE = 255;
private static final char STRING_LIST_SEPARATOR = ',';
private static final Joiner STRING_LIST_JOINER = Joiner.on(STRING_LIST_SEPARATOR).skipNulls();
- private static final Splitter STRING_LIST_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
+ private static final Splitter STRING_LIST_SPLITTER = Splitter.on(STRING_LIST_SEPARATOR).trimResults().omitEmptyStrings();
private int type;
private String kee;
diff --git a/server/sonar-server-common/src/it/java/org/sonar/server/issue/index/IssueIteratorFactoryIT.java b/server/sonar-server-common/src/it/java/org/sonar/server/issue/index/IssueIteratorFactoryIT.java
index cb16c2e28f3..7f4e61bad92 100644
--- a/server/sonar-server-common/src/it/java/org/sonar/server/issue/index/IssueIteratorFactoryIT.java
+++ b/server/sonar-server-common/src/it/java/org/sonar/server/issue/index/IssueIteratorFactoryIT.java
@@ -68,7 +68,8 @@ public class IssueIteratorFactoryIT {
.setIssueCreationDate(new Date(1115848800000L))
.setIssueUpdateDate(new Date(1356994800000L))
.setIssueCloseDate(null)
- .setType(2));
+ .setType(2)
+ .setCodeVariants(List.of("variant1", "variant2")));
Map<String, IssueDoc> issuesByKey = issuesByKey();
@@ -90,6 +91,7 @@ public class IssueIteratorFactoryIT {
assertThat(issue.getTags()).containsOnly("tag1", "tag2", "tag3");
assertThat(issue.effort().toMinutes()).isPositive();
assertThat(issue.type().getDbConstant()).isEqualTo(2);
+ assertThat(issue.getCodeVariants()).containsOnly("variant1", "variant2");
}
@Test
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java
index 40c9182facc..c4a0e3e59d6 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java
@@ -69,8 +69,8 @@ public class SearchRequest {
private List<String> sonarsourceSecurity;
private List<String> cwe;
private String timeZone;
-
private Integer owaspAsvsLevel;
+ private List<String> codeVariants;
public SearchRequest() {
// nothing to do here
@@ -502,4 +502,14 @@ public class SearchRequest {
this.owaspAsvsLevel = owaspAsvsLevel;
return this;
}
+
+ @CheckForNull
+ public List<String> getCodeVariants() {
+ return codeVariants;
+ }
+
+ public SearchRequest setCodeVariants(@Nullable List<String> codeVariants) {
+ this.codeVariants = codeVariants;
+ return this;
+ }
}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueDoc.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueDoc.java
index a490dc62a44..c02ff1f7782 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueDoc.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueDoc.java
@@ -366,4 +366,14 @@ public class IssueDoc extends BaseDoc {
setField(IssueIndexDefinition.FIELD_ISSUE_NEW_CODE_REFERENCE, b);
return this;
}
+
+ @CheckForNull
+ public Collection<String> getCodeVariants() {
+ return getNullableField(IssueIndexDefinition.FIELD_ISSUE_CODE_VARIANTS);
+ }
+
+ public IssueDoc setCodeVariants(@Nullable Collection<String> codeVariants) {
+ setField(IssueIndexDefinition.FIELD_ISSUE_CODE_VARIANTS, codeVariants);
+ return this;
+ }
}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIndexDefinition.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIndexDefinition.java
index 65368181f95..9cfb992b2a2 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIndexDefinition.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIndexDefinition.java
@@ -105,6 +105,7 @@ public class IssueIndexDefinition implements IndexDefinition {
public static final String FIELD_ISSUE_CWE = "cwe";
public static final String FIELD_ISSUE_SQ_SECURITY_CATEGORY = "sonarsourceSecurity";
public static final String FIELD_ISSUE_VULNERABILITY_PROBABILITY = "vulnerabilityProbability";
+ public static final String FIELD_ISSUE_CODE_VARIANTS = "codeVariants";
/**
* Whether issue is new code for a branch using the reference branch new code definition.
@@ -177,5 +178,6 @@ public class IssueIndexDefinition implements IndexDefinition {
mapping.keywordFieldBuilder(FIELD_ISSUE_SQ_SECURITY_CATEGORY).disableNorms().build();
mapping.keywordFieldBuilder(FIELD_ISSUE_VULNERABILITY_PROBABILITY).disableNorms().build();
mapping.createBooleanField(FIELD_ISSUE_NEW_CODE_REFERENCE);
+ mapping.keywordFieldBuilder(FIELD_ISSUE_CODE_VARIANTS).disableNorms().build();
}
}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java
index 907d1d55465..213a55599e1 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java
@@ -82,7 +82,8 @@ class IssueIteratorForSingleChunk implements IssueIterator {
"i.issue_type",
"r.security_standards",
"c.qualifier",
- "n.uuid"
+ "n.uuid",
+ "i.code_variants"
};
private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from issues i " +
@@ -96,7 +97,7 @@ class IssueIteratorForSingleChunk implements IssueIterator {
private static final String ISSUE_KEY_FILTER_PREFIX = " and i.kee in (";
private static final String ISSUE_KEY_FILTER_SUFFIX = ") ";
- static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
+ static final Splitter STRING_LIST_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
private final DbSession session;
@@ -222,7 +223,7 @@ class IssueIteratorForSingleChunk implements IssueIterator {
doc.setIsMainBranch(isMainBranch);
doc.setProjectUuid(projectUuid);
String tags = rs.getString(20);
- doc.setTags(IssueIteratorForSingleChunk.TAGS_SPLITTER.splitToList(tags == null ? "" : tags));
+ doc.setTags(STRING_LIST_SPLITTER.splitToList(tags == null ? "" : tags));
doc.setType(RuleType.valueOf(rs.getInt(21)));
SecurityStandards securityStandards = fromSecurityStandards(deserializeSecurityStandardsString(rs.getString(22)));
@@ -239,6 +240,8 @@ class IssueIteratorForSingleChunk implements IssueIterator {
doc.setScope(Qualifiers.UNIT_TEST_FILE.equals(rs.getString(23)) ? IssueScope.TEST : IssueScope.MAIN);
doc.setIsNewCodeReference(!isNullOrEmpty(rs.getString(24)));
+ String codeVariants = rs.getString(25);
+ doc.setCodeVariants(STRING_LIST_SPLITTER.splitToList(codeVariants == null ? "" : codeVariants));
return doc;
}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java
index a5550ce1bb8..04ef1b63488 100644
--- a/server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java
@@ -53,7 +53,8 @@ public class SearchRequestTest {
.setOwaspAsvs40(asList("1.1.1", "4.2.2"))
.setOwaspAsvsLevel(2)
.setPciDss32(asList("1", "4"))
- .setPciDss40(asList("3", "5"));
+ .setPciDss40(asList("3", "5"))
+ .setCodeVariants(asList("variant1", "variant2"));
assertThat(underTest.getIssues()).containsOnlyOnce("anIssueKey");
assertThat(underTest.getSeverities()).containsExactly("MAJOR", "MINOR");
@@ -79,6 +80,7 @@ public class SearchRequestTest {
assertThat(underTest.getOwaspAsvsLevel()).isEqualTo(2);
assertThat(underTest.getPciDss32()).containsExactly("1", "4");
assertThat(underTest.getPciDss40()).containsExactly("3", "5");
+ assertThat(underTest.getCodeVariants()).containsExactly("variant1", "variant2");
}
@Test
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
index 1cc371ca21b..7b38a2ccb11 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
@@ -117,6 +117,7 @@ import static org.sonar.server.es.searchrequest.TopAggregationHelper.NO_OTHER_SU
import static org.sonar.server.issue.index.IssueIndex.Facet.ASSIGNED_TO_ME;
import static org.sonar.server.issue.index.IssueIndex.Facet.ASSIGNEES;
import static org.sonar.server.issue.index.IssueIndex.Facet.AUTHOR;
+import static org.sonar.server.issue.index.IssueIndex.Facet.CODE_VARIANTS;
import static org.sonar.server.issue.index.IssueIndex.Facet.CREATED_AT;
import static org.sonar.server.issue.index.IssueIndex.Facet.CWE;
import static org.sonar.server.issue.index.IssueIndex.Facet.DIRECTORIES;
@@ -140,6 +141,7 @@ import static org.sonar.server.issue.index.IssueIndex.Facet.TYPES;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID;
+import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_CODE_VARIANTS;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_CWE;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH;
@@ -179,6 +181,7 @@ import static org.sonar.server.view.index.ViewIndexDefinition.TYPE_VIEW;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.FACET_MODE_EFFORT;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNEES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_AUTHOR;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CODE_VARIANTS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AT;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CWE;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_DIRECTORIES;
@@ -259,7 +262,8 @@ public class IssueIndex {
SANS_TOP_25(PARAM_SANS_TOP_25, FIELD_ISSUE_SANS_TOP_25, STICKY, DEFAULT_FACET_SIZE),
CWE(PARAM_CWE, FIELD_ISSUE_CWE, STICKY, DEFAULT_FACET_SIZE),
CREATED_AT(PARAM_CREATED_AT, FIELD_ISSUE_FUNC_CREATED_AT, NON_STICKY),
- SONARSOURCE_SECURITY(PARAM_SONARSOURCE_SECURITY, FIELD_ISSUE_SQ_SECURITY_CATEGORY, STICKY, DEFAULT_FACET_SIZE);
+ SONARSOURCE_SECURITY(PARAM_SONARSOURCE_SECURITY, FIELD_ISSUE_SQ_SECURITY_CATEGORY, STICKY, DEFAULT_FACET_SIZE),
+ CODE_VARIANTS(PARAM_CODE_VARIANTS, FIELD_ISSUE_CODE_VARIANTS, STICKY, MAX_FACET_SIZE);
private final String name;
private final SimpleFieldTopAggregationDefinition topAggregation;
@@ -452,6 +456,7 @@ public class IssueIndex {
FIELD_ISSUE_RULE_UUID,
query.ruleUuids()));
filters.addFilter(FIELD_ISSUE_STATUS, STATUSES.getFilterScope(), createTermsFilter(FIELD_ISSUE_STATUS, query.statuses()));
+ filters.addFilter(FIELD_ISSUE_CODE_VARIANTS, CODE_VARIANTS.getFilterScope(), createTermsFilter(FIELD_ISSUE_CODE_VARIANTS, query.codeVariants()));
// security category
addSecurityCategoryPrefixFilter(FIELD_ISSUE_PCI_DSS_32, PCI_DSS_32, query.pciDss32(), filters);
@@ -784,6 +789,7 @@ public class IssueIndex {
addFacetIfNeeded(options, aggregationHelper, esRequest, AUTHOR, query.authors().toArray());
addFacetIfNeeded(options, aggregationHelper, esRequest, TAGS, query.tags().toArray());
addFacetIfNeeded(options, aggregationHelper, esRequest, TYPES, query.types().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, CODE_VARIANTS, query.codeVariants().toArray());
addSecurityCategoryFacetIfNeeded(PARAM_PCI_DSS_32, PCI_DSS_32, options, aggregationHelper, esRequest, query.pciDss32().toArray());
addSecurityCategoryFacetIfNeeded(PARAM_PCI_DSS_40, PCI_DSS_40, options, aggregationHelper, esRequest, query.pciDss40().toArray());
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java
index fd73568089f..58174b3b5f2 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java
@@ -98,6 +98,7 @@ public class IssueQuery {
private final ZoneId timeZone;
private final Boolean newCodeOnReference;
private final Collection<String> newCodeOnReferenceByProjectUuids;
+ private final Collection<String> codeVariants;
private IssueQuery(Builder builder) {
this.issueKeys = defaultCollection(builder.issueKeys);
@@ -141,6 +142,7 @@ public class IssueQuery {
this.timeZone = builder.timeZone;
this.newCodeOnReference = builder.newCodeOnReference;
this.newCodeOnReferenceByProjectUuids = defaultCollection(builder.newCodeOnReferenceByProjectUuids);
+ this.codeVariants = defaultCollection(builder.codeVariants);
}
public Collection<String> issueKeys() {
@@ -328,6 +330,9 @@ public class IssueQuery {
return newCodeOnReferenceByProjectUuids;
}
+ public Collection<String> codeVariants() {
+ return codeVariants;
+ }
public static class Builder {
private Collection<String> issueKeys;
@@ -371,6 +376,7 @@ public class IssueQuery {
private ZoneId timeZone;
private Boolean newCodeOnReference = null;
private Collection<String> newCodeOnReferenceByProjectUuids;
+ private Collection<String> codeVariants;
private Builder() {
@@ -607,6 +613,11 @@ public class IssueQuery {
this.newCodeOnReferenceByProjectUuids = newCodeOnReferenceByProjectUuids;
return this;
}
+
+ public Builder codeVariants(@Nullable Collection<String> codeVariants) {
+ this.codeVariants = codeVariants;
+ return this;
+ }
}
private static <T> Collection<T> defaultCollection(@Nullable Collection<T> c) {
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
index b2645b4d6dc..7455abdbee1 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
@@ -151,7 +151,8 @@ public class IssueQueryFactory {
.createdAt(parseStartingDateOrDateTime(request.getCreatedAt(), timeZone))
.createdBefore(parseEndingDateOrDateTime(request.getCreatedBefore(), timeZone))
.facetMode(request.getFacetMode())
- .timeZone(timeZone);
+ .timeZone(timeZone)
+ .codeVariants(request.getCodeVariants());
List<ComponentDto> allComponents = new ArrayList<>();
boolean effectiveOnComponentOnly = mergeDeprecatedComponentParameters(dbSession, request, allComponents);
diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java
index ad2a99e4568..00046aa6012 100644
--- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java
+++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java
@@ -644,6 +644,23 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
assertThat(createdAt).isNull();
}
+ @Test
+ public void search_shouldReturnCodeVariantsFacet() {
+ ComponentDto project = newPrivateProjectDto();
+ ComponentDto file = newFileDto(project);
+
+ indexIssues(
+ newDoc("I1", project.uuid(), file).setCodeVariants(asList("variant1", "variant2")),
+ newDoc("I2", project.uuid(), file).setCodeVariants(singletonList("variant2")),
+ newDoc("I3", project.uuid(), file).setCodeVariants(singletonList("variant3")),
+ newDoc("I4", project.uuid(), file));
+
+ assertThatFacetHasOnly(IssueQuery.builder(), "codeVariants",
+ entry("variant1", 1L),
+ entry("variant2", 2L),
+ entry("variant3", 1L));
+ }
+
private SearchOptions fixtureForCreatedAtFacet() {
ComponentDto project = newPrivateProjectDto();
ComponentDto file = newFileDto(project);
diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java
index fed30d63c18..90731021766 100644
--- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java
+++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java
@@ -838,6 +838,20 @@ public class IssueIndexFiltersTest extends IssueIndexTestCommon {
assertThatSearchReturnsOnly(IssueQuery.builder().sonarsourceSecurity(singletonList("buffer-overflow")), "I1");
}
+ @Test
+ public void search_whenFilteringByCodeVariants_shouldReturnRelevantIssues() {
+ ComponentDto project = newPrivateProjectDto();
+ ComponentDto file = newFileDto(project);
+
+ indexIssues(
+ newDoc("I1", project.uuid(), file).setCodeVariants(asList("variant1", "variant2")),
+ newDoc("I2", project.uuid(), file).setCodeVariants(singletonList("variant2")),
+ newDoc("I3", project.uuid(), file).setCodeVariants(singletonList("variant3")),
+ newDoc("I4", project.uuid(), file));
+
+ assertThatSearchReturnsOnly(IssueQuery.builder().codeVariants(singletonList("variant2")), "I1", "I2");
+ }
+
private void indexView(String viewUuid, List<String> projects) {
viewIndexer.index(new ViewDoc().setUuid(viewUuid).setProjects(projects));
}
diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java
index 219f54b7c1f..ececa35a00c 100644
--- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java
+++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java
@@ -104,7 +104,8 @@ public class IssueQueryFactoryTest {
.setCreatedBefore("2013-04-17T09:08:24+0200")
.setRules(asList(rule1.getKey().toString(), rule2.getKey().toString()))
.setSort("CREATION_DATE")
- .setAsc(true);
+ .setAsc(true)
+ .setCodeVariants(asList("variant1", "variant2"));
IssueQuery query = underTest.create(request);
@@ -129,7 +130,7 @@ public class IssueQueryFactoryTest {
assertThat(query.createdBefore()).isEqualTo(parseDateTime("2013-04-17T09:08:24+0200"));
assertThat(query.sort()).isEqualTo(IssueQuery.SORT_BY_CREATION_DATE);
assertThat(query.asc()).isTrue();
-
+ assertThat(query.codeVariants()).containsOnly("variant1", "variant2");
}
@Test
diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryTest.java
index 4c22e2d473d..d80c28a596c 100644
--- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryTest.java
+++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryTest.java
@@ -63,6 +63,7 @@ public class IssueQueryTest {
.newCodeOnReferenceByProjectUuids(List.of("PROJECT"))
.sort(IssueQuery.SORT_BY_CREATION_DATE)
.asc(true)
+ .codeVariants(List.of("codeVariant1", "codeVariant2"))
.build();
assertThat(query.issueKeys()).containsOnly("ABCDE");
assertThat(query.severities()).containsOnly(Severity.BLOCKER);
@@ -88,6 +89,7 @@ public class IssueQueryTest {
assertThat(query.newCodeOnReferenceByProjectUuids()).containsOnly("PROJECT");
assertThat(query.sort()).isEqualTo(IssueQuery.SORT_BY_CREATION_DATE);
assertThat(query.asc()).isTrue();
+ assertThat(query.codeVariants()).containsOnly("codeVariant1", "codeVariant2");
}
@Test
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/ShowActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/ShowActionIT.java
index e3784bc6339..88a33c88b68 100644
--- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/ShowActionIT.java
+++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/ShowActionIT.java
@@ -1070,6 +1070,21 @@ public class ShowActionIT {
}
@Test
+ public void response_shouldContainCodeVariants() {
+ ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent();
+ userSessionRule.registerComponents(project);
+ ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
+ RuleDto rule = newRule(SECURITY_HOTSPOT);
+ IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file, t -> t.setCodeVariants(List.of("variant1", "variant2")));
+ mockChangelogAndCommentsFormattingContext();
+
+ Hotspots.ShowWsResponse response = newRequest(hotspot)
+ .executeProtobuf(Hotspots.ShowWsResponse.class);
+
+ assertThat(response.getCodeVariantsList()).containsOnly("variant1", "variant2");
+ }
+
+ @Test
public void verify_response_example() {
ComponentDto project = dbTester.components().insertPublicProject(componentDto -> componentDto
.setName("test-project")
@@ -1103,7 +1118,8 @@ public class ShowActionIT {
.setIssueUpdateTime(time)
.setAuthorLogin(author.getLogin())
.setAssigneeUuid(author.getUuid())
- .setKee("AW9mgJw6eFC3pGl94Wrf"));
+ .setKee("AW9mgJw6eFC3pGl94Wrf")
+ .setCodeVariants(List.of("windows", "linux")));
List<Common.Changelog> changelog = IntStream.range(0, 3)
.mapToObj(i -> Common.Changelog.newBuilder()
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java
index feb7555d0a0..591fc7a23cf 100644
--- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java
+++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java
@@ -116,6 +116,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.ACTION_ASSIGN;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.ACTION_SET_TAGS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ADDITIONAL_FIELDS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNEES;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CODE_VARIANTS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_KEYS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AFTER;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_HIDE_COMMENTS;
@@ -178,7 +179,8 @@ public class SearchActionIT {
.setAssigneeUuid(simon.getUuid())
.setTags(asList("bug", "owasp"))
.setIssueCreationDate(parseDate("2014-09-03"))
- .setIssueUpdateDate(parseDate("2017-12-04")));
+ .setIssueUpdateDate(parseDate("2017-12-04"))
+ .setCodeVariants(List.of("variant1", "variant2")));
indexPermissionsAndIssues();
SearchWsResponse response = ws.newRequest()
@@ -188,12 +190,12 @@ public class SearchActionIT {
.extracting(
Issue::getKey, Issue::getRule, Issue::getSeverity, Issue::getComponent, Issue::getResolution, Issue::getStatus, Issue::getMessage, Issue::getMessageFormattingsList,
Issue::getEffort, Issue::getAssignee, Issue::getAuthor, Issue::getLine, Issue::getHash, Issue::getTagsList, Issue::getCreationDate, Issue::getUpdateDate,
- Issue::getQuickFixAvailable)
+ Issue::getQuickFixAvailable, Issue::getCodeVariantsList)
.containsExactlyInAnyOrder(
tuple(issue.getKey(), rule.getKey().toString(), Severity.MAJOR, file.getKey(), RESOLUTION_FIXED, STATUS_RESOLVED, "the message",
MessageFormattingUtils.dbMessageFormattingListToWs(List.of(MESSAGE_FORMATTING)), "10min",
simon.getLogin(), "John", 42, "a227e508d6646b55a086ee11d63b21e9", asList("bug", "owasp"), formatDateTime(issue.getIssueCreationDate()),
- formatDateTime(issue.getIssueUpdateDate()), false));
+ formatDateTime(issue.getIssueUpdateDate()), false, List.of("variant1", "variant2")));
}
@Test
@@ -551,6 +553,24 @@ public class SearchActionIT {
}
@Test
+ public void search_by_variants_with_facets() {
+ RuleDto rule = newIssueRule();
+ ComponentDto project = db.components().insertPublicProject("PROJECT_ID", c -> c.setKey("PROJECT_KEY").setLanguage("java")).getMainBranchComponent();
+ ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setKey("FILE_KEY").setLanguage("java"));
+ db.issues().insertIssue(rule, project, file, i -> i.setCodeVariants(List.of("variant1")));
+ db.issues().insertIssue(rule, project, file, i -> i.setCodeVariants(List.of("variant2")));
+ db.issues().insertIssue(rule, project, file, i -> i.setCodeVariants(List.of("variant1", "variant2")));
+ db.issues().insertIssue(rule, project, file, i -> i.setCodeVariants(List.of("variant2", "variant3")));
+ indexPermissionsAndIssues();
+
+ ws.newRequest()
+ .setParam(PARAM_CODE_VARIANTS, "variant2,variant3")
+ .setParam(FACETS, PARAM_CODE_VARIANTS)
+ .execute()
+ .assertJson(this.getClass(), "search_by_variants_with_facets.json");
+ }
+
+ @Test
public void issue_on_removed_file() {
RuleDto rule = newIssueRule();
ComponentDto project = db.components().insertPublicProject("PROJECT_ID", c -> c.setKey("PROJECT_KEY").setKey("PROJECT_KEY")).getMainBranchComponent();
@@ -1754,7 +1774,7 @@ public class SearchActionIT {
"createdBefore", "createdInLast", "directories", "facets", "files", "issues", "scopes", "languages", "onComponentOnly",
"p", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "statuses", "tags", "types", "pciDss-3.2", "pciDss-4.0", "owaspAsvs-4.0",
"owaspAsvsLevel", "owaspTop10",
- "owaspTop10-2021", "sansTop25", "cwe", "sonarsourceSecurity", "timeZone", "inNewCodePeriod");
+ "owaspTop10-2021", "sansTop25", "cwe", "sonarsourceSecurity", "timeZone", "inNewCodePeriod", "codeVariants");
WebService.Param branch = def.param(PARAM_BRANCH);
assertThat(branch.isInternal()).isFalse();
diff --git a/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/issue/ws/SearchActionIT/search_by_variants_with_facets.json b/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/issue/ws/SearchActionIT/search_by_variants_with_facets.json
new file mode 100644
index 00000000000..3798206a378
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/issue/ws/SearchActionIT/search_by_variants_with_facets.json
@@ -0,0 +1,89 @@
+{
+ "total": 3,
+ "p": 1,
+ "ps": 100,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 3
+ },
+ "issues": [
+ {
+ "rule": "xoo:x1",
+ "component": "FILE_KEY",
+ "project": "PROJECT_KEY",
+ "flows": [],
+ "status": "OPEN",
+ "scope": "MAIN",
+ "quickFixAvailable": false,
+ "messageFormattings": [],
+ "codeVariants": [
+ "variant2",
+ "variant3"
+ ]
+ },
+ {
+ "rule": "xoo:x1",
+ "component": "FILE_KEY",
+ "project": "PROJECT_KEY",
+ "flows": [],
+ "status": "OPEN",
+ "scope": "MAIN",
+ "quickFixAvailable": false,
+ "messageFormattings": [],
+ "codeVariants": [
+ "variant2"
+ ]
+ },
+ {
+ "rule": "xoo:x1",
+ "component": "FILE_KEY",
+ "project": "PROJECT_KEY",
+ "flows": [],
+ "status": "OPEN",
+ "scope": "MAIN",
+ "quickFixAvailable": false,
+ "messageFormattings": [],
+ "codeVariants": [
+ "variant1",
+ "variant2"
+ ]
+ }
+ ],
+ "components": [
+ {
+ "key": "FILE_KEY",
+ "enabled": true,
+ "qualifier": "FIL",
+ "name": "NAME_FILE_ID",
+ "longName": "null/NAME_FILE_ID",
+ "path": "null/NAME_FILE_ID"
+ },
+ {
+ "key": "PROJECT_KEY",
+ "enabled": true,
+ "qualifier": "TRK",
+ "name": "NAME_PROJECT_ID",
+ "longName": "LONG_NAME_PROJECT_ID"
+ }
+ ],
+ "facets": [
+ {
+ "property": "codeVariants",
+ "values": [
+ {
+ "val": "variant2",
+ "count": 3
+ },
+ {
+ "val": "variant1",
+ "count": 2
+ },
+ {
+ "val": "variant3",
+ "count": 1
+ }
+ ]
+ }
+ ]
+}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/ShowAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/ShowAction.java
index 2740b9cb490..f5c82402406 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/ShowAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/ShowAction.java
@@ -108,6 +108,7 @@ public class ShowAction implements HotspotsWsAction {
.setDescription("Provides the details of a Security Hotspot.")
.setSince("8.1")
.setChangelog(
+ new Change("10.1", "Add the 'codeVariants' response field"),
new Change("9.5", "The fields rule.riskDescription, rule.fixRecommendations, rule.vulnerabilityDescription of the response are deprecated."
+ " /api/rules/show endpoint should be used to fetch rule descriptions."),
new Change("9.7", "Hotspot flows in the response may contain a description and a type"),
@@ -171,6 +172,7 @@ public class ShowAction implements HotspotsWsAction {
builder.setUpdateDate(formatDateTime(hotspot.getIssueUpdateDate()));
users.getAssignee().map(UserDto::getLogin).ifPresent(builder::setAssignee);
Optional.ofNullable(hotspot.getAuthorLogin()).ifPresent(builder::setAuthor);
+ builder.addAllCodeVariants(hotspot.getCodeVariants());
}
private void formatComponents(Components components, ShowWsResponse.Builder responseBuilder) {
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
index b5e9b981091..ed55f7438d3 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
@@ -96,6 +96,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNED;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNEES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_AUTHOR;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_BRANCH;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CODE_VARIANTS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_KEYS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AFTER;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AT;
@@ -156,7 +157,8 @@ public class SearchAction implements IssuesWsAction {
PARAM_SANS_TOP_25,
PARAM_CWE,
PARAM_CREATED_AT,
- PARAM_SONARSOURCE_SECURITY
+ PARAM_SONARSOURCE_SECURITY,
+ PARAM_CODE_VARIANTS
);
private static final String INTERNAL_PARAMETER_DISCLAIMER = "This parameter is mostly used by the Issues page, please prefer usage of the componentKeys parameter. ";
@@ -193,6 +195,7 @@ public class SearchAction implements IssuesWsAction {
+ "<br/>When issue indexation is in progress returns 503 service unavailable HTTP code.")
.setSince("3.6")
.setChangelog(
+ new Change("10.1", "Add the 'codeVariants' parameter, facet and response field"),
new Change("10.0", "Parameter 'sansTop25' is deprecated"),
new Change("10.0", "The value 'sansTop25' for the parameter 'facets' has been deprecated"),
new Change("10.0", format("Deprecated value 'ASSIGNEE' in parameter '%s' is dropped", Param.SORT)),
@@ -278,7 +281,7 @@ public class SearchAction implements IssuesWsAction {
action.createParam(PARAM_OWASP_ASVS_LEVEL)
.setDescription("Level of OWASP ASVS categories.")
.setSince("9.7")
- .setPossibleValues(1,2,3);
+ .setPossibleValues(1, 2, 3);
action.createParam(PARAM_PCI_DSS_32)
.setDescription("Comma-separated list of PCI DSS v3.2 categories.")
.setSince("9.6")
@@ -356,6 +359,10 @@ public class SearchAction implements IssuesWsAction {
.setRequired(false)
.setExampleValue("'Europe/Paris', 'Z' or '+02:00'")
.setSince("8.6");
+ action.createParam(PARAM_CODE_VARIANTS)
+ .setDescription("Comma-separated list of code variants.")
+ .setExampleValue("windows,linux")
+ .setSince("10.1");
}
private static void addComponentRelatedParams(WebService.NewAction action) {
@@ -500,6 +507,7 @@ public class SearchAction implements IssuesWsAction {
addMandatoryValuesToFacet(facets, PARAM_SANS_TOP_25, request.getSansTop25());
addMandatoryValuesToFacet(facets, PARAM_CWE, request.getCwe());
addMandatoryValuesToFacet(facets, PARAM_SONARSOURCE_SECURITY, request.getSonarsourceSecurity());
+ addMandatoryValuesToFacet(facets, PARAM_CODE_VARIANTS, request.getCodeVariants());
}
private static void setTypesFacet(Facets facets) {
@@ -577,7 +585,8 @@ public class SearchAction implements IssuesWsAction {
.setSansTop25(request.paramAsStrings(PARAM_SANS_TOP_25))
.setCwe(request.paramAsStrings(PARAM_CWE))
.setSonarsourceSecurity(request.paramAsStrings(PARAM_SONARSOURCE_SECURITY))
- .setTimeZone(request.param(PARAM_TIMEZONE));
+ .setTimeZone(request.param(PARAM_TIMEZONE))
+ .setCodeVariants(request.paramAsStrings(PARAM_CODE_VARIANTS));
}
private void checkIfNeedIssueSync(DbSession dbSession, SearchRequest searchRequest) {
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
index e22262d6428..75233dde0b2 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
@@ -181,6 +181,7 @@ public class SearchResponseFormat {
issueBuilder.setMessage(nullToEmpty(dto.getMessage()));
issueBuilder.addAllMessageFormattings(MessageFormattingUtils.dbMessageFormattingToWs(dto.parseMessageFormattings()));
issueBuilder.addAllTags(dto.getTags());
+ issueBuilder.addAllCodeVariants(dto.getCodeVariants());
Long effort = dto.getEffort();
if (effort != null) {
String effortValue = durations.encode(Duration.create(effort));
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/hotspot/ws/show-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/hotspot/ws/show-example.json
index feb46663bd0..575ccea5231 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/hotspot/ws/show-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/hotspot/ws/show-example.json
@@ -108,5 +108,9 @@
"active": true
}
],
- "canChangeStatus": true
+ "canChangeStatus": true,
+ "codeVariants": [
+ "windows",
+ "linux"
+ ]
}
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/search-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/search-example.json
index 3e90745a062..24d91b68761 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/search-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/search-example.json
@@ -93,7 +93,11 @@
}
],
"quickFixAvailable": false,
- "ruleDescriptionContextKey": "spring"
+ "ruleDescriptionContextKey": "spring",
+ "codeVariants": [
+ "windows",
+ "linux"
+ ]
}
],
"components": [
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java
index 6f68df0a595..d6687e73f6b 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java
@@ -139,6 +139,7 @@ public class SearchResponseFormatFormatOperationTest {
assertThat(issue.getCloseDate()).isEqualTo(formatDateTime(issueDto.getIssueCloseDate()));
assertThat(issue.getQuickFixAvailable()).isEqualTo(issueDto.isQuickFixAvailable());
assertThat(issue.getRuleDescriptionContextKey()).isEqualTo(issueDto.getOptionalRuleDescriptionContextKey().orElse(null));
+ assertThat(new ArrayList<>(issue.getCodeVariantsList())).containsExactlyInAnyOrderElementsOf(issueDto.getCodeVariants());
}
@Test
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java
index b9f417bc9b8..8791003f93f 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java
@@ -102,6 +102,7 @@ public class IssuesWsParameters {
public static final String PARAM_ASC = "asc";
public static final String PARAM_ADDITIONAL_FIELDS = "additionalFields";
public static final String PARAM_TIMEZONE = "timeZone";
+ public static final String PARAM_CODE_VARIANTS = "codeVariants";
public static final String FACET_MODE_EFFORT = "effort";
diff --git a/sonar-ws/src/main/protobuf/ws-hotspots.proto b/sonar-ws/src/main/protobuf/ws-hotspots.proto
index c846f04ad9e..0e0599e42de 100644
--- a/sonar-ws/src/main/protobuf/ws-hotspots.proto
+++ b/sonar-ws/src/main/protobuf/ws-hotspots.proto
@@ -75,6 +75,7 @@ message ShowWsResponse {
optional bool canChangeStatus = 17;
repeated sonarqube.ws.commons.Flow flows = 19;
repeated sonarqube.ws.commons.MessageFormatting messageFormattings = 20;
+ repeated string codeVariants = 21;
}
message Component {
diff --git a/sonar-ws/src/main/protobuf/ws-issues.proto b/sonar-ws/src/main/protobuf/ws-issues.proto
index b03e72200b1..1956a10558a 100644
--- a/sonar-ws/src/main/protobuf/ws-issues.proto
+++ b/sonar-ws/src/main/protobuf/ws-issues.proto
@@ -161,6 +161,8 @@ message Issue {
optional string ruleDescriptionContextKey = 37;
repeated sonarqube.ws.commons.MessageFormatting messageFormattings = 38;
+
+ repeated string codeVariants = 39;
}
message Transitions {