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;
.setIssueCreationDate(new Date(1115848800000L))
.setIssueUpdateDate(new Date(1356994800000L))
.setIssueCloseDate(null)
- .setType(2));
+ .setType(2)
+ .setCodeVariants(List.of("variant1", "variant2")));
Map<String, IssueDoc> issuesByKey = issuesByKey();
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
private List<String> sonarsourceSecurity;
private List<String> cwe;
private String timeZone;
-
private Integer owaspAsvsLevel;
+ private List<String> codeVariants;
public SearchRequest() {
// nothing to do here
this.owaspAsvsLevel = owaspAsvsLevel;
return this;
}
+
+ @CheckForNull
+ public List<String> getCodeVariants() {
+ return codeVariants;
+ }
+
+ public SearchRequest setCodeVariants(@Nullable List<String> codeVariants) {
+ this.codeVariants = codeVariants;
+ return this;
+ }
}
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;
+ }
}
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.
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();
}
}
"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 " +
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;
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)));
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;
}
.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");
assertThat(underTest.getOwaspAsvsLevel()).isEqualTo(2);
assertThat(underTest.getPciDss32()).containsExactly("1", "4");
assertThat(underTest.getPciDss40()).containsExactly("3", "5");
+ assertThat(underTest.getCodeVariants()).containsExactly("variant1", "variant2");
}
@Test
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;
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;
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;
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;
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);
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());
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);
this.timeZone = builder.timeZone;
this.newCodeOnReference = builder.newCodeOnReference;
this.newCodeOnReferenceByProjectUuids = defaultCollection(builder.newCodeOnReferenceByProjectUuids);
+ this.codeVariants = defaultCollection(builder.codeVariants);
}
public Collection<String> issueKeys() {
return newCodeOnReferenceByProjectUuids;
}
+ public Collection<String> codeVariants() {
+ return codeVariants;
+ }
public static class Builder {
private Collection<String> issueKeys;
private ZoneId timeZone;
private Boolean newCodeOnReference = null;
private Collection<String> newCodeOnReferenceByProjectUuids;
+ private Collection<String> codeVariants;
private Builder() {
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) {
.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);
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);
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));
}
.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);
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
.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);
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
.containsOnly(tuple(author.getLogin(), author.getName(), author.isActive()));
}
+ @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
.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()
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;
.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()
.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
execute.assertJson(this.getClass(), "no_issue.json");
}
+ @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();
"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();
--- /dev/null
+{
+ "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
+ }
+ ]
+ }
+ ]
+}
.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"),
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) {
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;
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. ";
+ "<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)),
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")
.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) {
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) {
.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) {
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));
"active": true
}
],
- "canChangeStatus": true
+ "canChangeStatus": true,
+ "codeVariants": [
+ "windows",
+ "linux"
+ ]
}
}
],
"quickFixAvailable": false,
- "ruleDescriptionContextKey": "spring"
+ "ruleDescriptionContextKey": "spring",
+ "codeVariants": [
+ "windows",
+ "linux"
+ ]
}
],
"components": [
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
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";
optional bool canChangeStatus = 17;
repeated sonarqube.ws.commons.Flow flows = 19;
repeated sonarqube.ws.commons.MessageFormatting messageFormattings = 20;
+ repeated string codeVariants = 21;
}
message Component {
optional string ruleDescriptionContextKey = 37;
repeated sonarqube.ws.commons.MessageFormatting messageFormattings = 38;
+
+ repeated string codeVariants = 39;
}
message Transitions {