- Refactor issue indexer to use MyBatis mapping with cursortags/10.2.0.77647
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.Iterator; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Optional; | import java.util.Optional; | ||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.stream.IntStream; | import java.util.stream.IntStream; | ||||
import java.util.stream.Stream; | import java.util.stream.Stream; | ||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.apache.ibatis.cursor.Cursor; | |||||
import org.junit.Before; | import org.junit.Before; | ||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
tuple(Severity.LOW, SoftwareQuality.SECURITY)); | tuple(Severity.LOW, SoftwareQuality.SECURITY)); | ||||
} | } | ||||
@Test | |||||
public void scrollIndexationIssues_shouldReturnDto() { | |||||
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); | |||||
RuleDto rule = db.rules().insert(r -> r.setRepositoryKey("java").setLanguage("java") | |||||
.addDefaultImpact(new ImpactDto() | |||||
.setUuid(UuidFactoryFast.getInstance().create()) | |||||
.setSoftwareQuality(SoftwareQuality.RELIABILITY) | |||||
.setSeverity(Severity.MEDIUM))); | |||||
ComponentDto branchA = db.components().insertProjectBranch(project, b -> b.setKey("branchA")); | |||||
ComponentDto fileA = db.components().insertComponent(newFileDto(branchA)); | |||||
IntStream.range(0, 100).forEach(i -> insertBranchIssue(branchA, fileA, rule, "A" + i, STATUS_OPEN, 1_340_000_000_000L)); | |||||
Cursor<IndexedIssueDto> issues = underTest.scrollIssuesForIndexation(db.getSession(), null, null); | |||||
Iterator<IndexedIssueDto> iterator = issues.iterator(); | |||||
int issueCount = 0; | |||||
while (iterator.hasNext()) { | |||||
IndexedIssueDto next = iterator.next(); | |||||
assertThat(next.getRuleDefaultImpacts()).hasSize(2) | |||||
.extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity) | |||||
.containsExactlyInAnyOrder( | |||||
tuple(SoftwareQuality.RELIABILITY, Severity.MEDIUM), | |||||
tuple(SoftwareQuality.MAINTAINABILITY, Severity.HIGH)); | |||||
assertThat(next.getImpacts()) | |||||
.extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity) | |||||
.containsExactlyInAnyOrder( | |||||
tuple(SoftwareQuality.MAINTAINABILITY, Severity.HIGH)); | |||||
issueCount++; | |||||
} | |||||
assertThat(issueCount).isEqualTo(100); | |||||
} | |||||
@Test | @Test | ||||
public void selectIssueKeysByComponentUuid() { | public void selectIssueKeysByComponentUuid() { | ||||
// contains I1 and I2 | // contains I1 and I2 |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 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.db.issue; | |||||
import java.util.Collections; | |||||
import java.util.EnumMap; | |||||
import java.util.HashSet; | |||||
import java.util.Map; | |||||
import java.util.Set; | |||||
import org.sonar.api.issue.impact.Severity; | |||||
import org.sonar.api.issue.impact.SoftwareQuality; | |||||
public final class IndexedIssueDto { | |||||
private String issueKey = null; | |||||
private String assignee = null; | |||||
private Integer line = null; | |||||
private String resolution = null; | |||||
private String cleanCodeAttribute = null; | |||||
private String severity = null; | |||||
private String status = null; | |||||
private Long effort = null; | |||||
private String authorLogin = null; | |||||
private Long issueCloseDate = null; | |||||
private Long issueCreationDate = null; | |||||
private Long issueUpdateDate = null; | |||||
private String ruleUuid = null; | |||||
private String language = null; | |||||
private String componentUuid = null; | |||||
private String path = null; | |||||
private String scope = null; | |||||
private String branchUuid = null; | |||||
private boolean isMain = false; | |||||
private String projectUuid = null; | |||||
private String tags = null; | |||||
private Integer issueType = null; | |||||
private String securityStandards = null; | |||||
private String qualifier = null; | |||||
private boolean isNewCodeReferenceIssue = false; | |||||
private String codeVariants = null; | |||||
private Set<ImpactDto> impacts = new HashSet<>(); | |||||
private Set<ImpactDto> ruleDefaultImpacts = new HashSet<>(); | |||||
public IndexedIssueDto() { | |||||
// empty constructor | |||||
} | |||||
public String getIssueKey() { | |||||
return issueKey; | |||||
} | |||||
public IndexedIssueDto setIssueKey(String issueKey) { | |||||
this.issueKey = issueKey; | |||||
return this; | |||||
} | |||||
public String getAssignee() { | |||||
return assignee; | |||||
} | |||||
public IndexedIssueDto setAssignee(String assignee) { | |||||
this.assignee = assignee; | |||||
return this; | |||||
} | |||||
public Integer getLine() { | |||||
return line; | |||||
} | |||||
public IndexedIssueDto setLine(Integer line) { | |||||
this.line = line; | |||||
return this; | |||||
} | |||||
public String getResolution() { | |||||
return resolution; | |||||
} | |||||
public IndexedIssueDto setResolution(String resolution) { | |||||
this.resolution = resolution; | |||||
return this; | |||||
} | |||||
public String getSeverity() { | |||||
return severity; | |||||
} | |||||
public IndexedIssueDto setSeverity(String severity) { | |||||
this.severity = severity; | |||||
return this; | |||||
} | |||||
public String getStatus() { | |||||
return status; | |||||
} | |||||
public IndexedIssueDto setStatus(String status) { | |||||
this.status = status; | |||||
return this; | |||||
} | |||||
public Long getEffort() { | |||||
return effort; | |||||
} | |||||
public IndexedIssueDto setEffort(Long effort) { | |||||
this.effort = effort; | |||||
return this; | |||||
} | |||||
public String getAuthorLogin() { | |||||
return authorLogin; | |||||
} | |||||
public IndexedIssueDto setAuthorLogin(String authorLogin) { | |||||
this.authorLogin = authorLogin; | |||||
return this; | |||||
} | |||||
public Long getIssueCloseDate() { | |||||
return issueCloseDate; | |||||
} | |||||
public IndexedIssueDto setIssueCloseDate(Long issueCloseDate) { | |||||
this.issueCloseDate = issueCloseDate; | |||||
return this; | |||||
} | |||||
public Long getIssueCreationDate() { | |||||
return issueCreationDate; | |||||
} | |||||
public IndexedIssueDto setIssueCreationDate(Long issueCreationDate) { | |||||
this.issueCreationDate = issueCreationDate; | |||||
return this; | |||||
} | |||||
public Long getIssueUpdateDate() { | |||||
return issueUpdateDate; | |||||
} | |||||
public IndexedIssueDto setIssueUpdateDate(Long issueUpdateDate) { | |||||
this.issueUpdateDate = issueUpdateDate; | |||||
return this; | |||||
} | |||||
public String getRuleUuid() { | |||||
return ruleUuid; | |||||
} | |||||
public IndexedIssueDto setRuleUuid(String ruleUuid) { | |||||
this.ruleUuid = ruleUuid; | |||||
return this; | |||||
} | |||||
public String getLanguage() { | |||||
return language; | |||||
} | |||||
public IndexedIssueDto setLanguage(String language) { | |||||
this.language = language; | |||||
return this; | |||||
} | |||||
public String getComponentUuid() { | |||||
return componentUuid; | |||||
} | |||||
public IndexedIssueDto setComponentUuid(String componentUuid) { | |||||
this.componentUuid = componentUuid; | |||||
return this; | |||||
} | |||||
public String getPath() { | |||||
return path; | |||||
} | |||||
public IndexedIssueDto setPath(String path) { | |||||
this.path = path; | |||||
return this; | |||||
} | |||||
public String getScope() { | |||||
return scope; | |||||
} | |||||
public IndexedIssueDto setScope(String scope) { | |||||
this.scope = scope; | |||||
return this; | |||||
} | |||||
public String getBranchUuid() { | |||||
return branchUuid; | |||||
} | |||||
public IndexedIssueDto setBranchUuid(String branchUuid) { | |||||
this.branchUuid = branchUuid; | |||||
return this; | |||||
} | |||||
public boolean isMain() { | |||||
return isMain; | |||||
} | |||||
public IndexedIssueDto setIsMain(boolean isMain) { | |||||
this.isMain = isMain; | |||||
return this; | |||||
} | |||||
public String getProjectUuid() { | |||||
return projectUuid; | |||||
} | |||||
public IndexedIssueDto setProjectUuid(String projectUuid) { | |||||
this.projectUuid = projectUuid; | |||||
return this; | |||||
} | |||||
public String getTags() { | |||||
return tags; | |||||
} | |||||
public IndexedIssueDto setTags(String tags) { | |||||
this.tags = tags; | |||||
return this; | |||||
} | |||||
@Deprecated | |||||
public Integer getIssueType() { | |||||
return issueType; | |||||
} | |||||
@Deprecated | |||||
public IndexedIssueDto setIssueType(Integer issueType) { | |||||
this.issueType = issueType; | |||||
return this; | |||||
} | |||||
public String getSecurityStandards() { | |||||
return securityStandards; | |||||
} | |||||
public IndexedIssueDto setSecurityStandards(String securityStandards) { | |||||
this.securityStandards = securityStandards; | |||||
return this; | |||||
} | |||||
public String getQualifier() { | |||||
return qualifier; | |||||
} | |||||
public IndexedIssueDto setQualifier(String qualifier) { | |||||
this.qualifier = qualifier; | |||||
return this; | |||||
} | |||||
public boolean isNewCodeReferenceIssue() { | |||||
return isNewCodeReferenceIssue; | |||||
} | |||||
public IndexedIssueDto setNewCodeReferenceIssue(boolean newCodeReferenceIssue) { | |||||
isNewCodeReferenceIssue = newCodeReferenceIssue; | |||||
return this; | |||||
} | |||||
public String getCodeVariants() { | |||||
return codeVariants; | |||||
} | |||||
public IndexedIssueDto setCodeVariants(String codeVariants) { | |||||
this.codeVariants = codeVariants; | |||||
return this; | |||||
} | |||||
public Set<ImpactDto> getImpacts() { | |||||
return impacts; | |||||
} | |||||
public Set<ImpactDto> getRuleDefaultImpacts() { | |||||
return ruleDefaultImpacts; | |||||
} | |||||
public Map<SoftwareQuality, Severity> getEffectiveImpacts() { | |||||
EnumMap<SoftwareQuality, Severity> effectiveImpacts = new EnumMap<>(SoftwareQuality.class); | |||||
ruleDefaultImpacts.forEach(impact -> effectiveImpacts.put(impact.getSoftwareQuality(), impact.getSeverity())); | |||||
impacts.forEach(impact -> effectiveImpacts.put(impact.getSoftwareQuality(), impact.getSeverity())); | |||||
return Collections.unmodifiableMap(effectiveImpacts); | |||||
} | |||||
public String getCleanCodeAttribute() { | |||||
return cleanCodeAttribute; | |||||
} | |||||
public IndexedIssueDto setCleanCodeAttribute(String cleanCodeAttribute) { | |||||
this.cleanCodeAttribute = cleanCodeAttribute; | |||||
return this; | |||||
} | |||||
} |
import java.util.List; | import java.util.List; | ||||
import java.util.Optional; | import java.util.Optional; | ||||
import java.util.Set; | import java.util.Set; | ||||
import javax.annotation.Nullable; | |||||
import org.apache.ibatis.annotations.Param; | |||||
import org.apache.ibatis.cursor.Cursor; | |||||
import org.sonar.db.Dao; | import org.sonar.db.Dao; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.Pagination; | import org.sonar.db.Pagination; | ||||
return mapper(dbSession).selectIssueGroupsByComponent(component, leakPeriodBeginningDate); | return mapper(dbSession).selectIssueGroupsByComponent(component, leakPeriodBeginningDate); | ||||
} | } | ||||
public Cursor<IndexedIssueDto> scrollIssuesForIndexation(DbSession dbSession, @Nullable @Param("branchUuid") String branchUuid, | |||||
@Nullable @Param("issueKeys") Collection<String> issueKeys) { | |||||
return mapper(dbSession).scrollIssuesForIndexation(branchUuid, issueKeys); | |||||
} | |||||
public void insert(DbSession session, IssueDto dto) { | public void insert(DbSession session, IssueDto dto) { | ||||
mapper(session).insert(dto); | mapper(session).insert(dto); | ||||
updateIssueImpacts(dto, mapper(session)); | updateIssueImpacts(dto, mapper(session)); |
import java.util.Set; | import java.util.Set; | ||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.apache.ibatis.annotations.Param; | import org.apache.ibatis.annotations.Param; | ||||
import org.apache.ibatis.cursor.Cursor; | |||||
import org.apache.ibatis.session.ResultHandler; | import org.apache.ibatis.session.ResultHandler; | ||||
import org.sonar.db.Pagination; | import org.sonar.db.Pagination; | ||||
import org.sonar.db.component.ComponentDto; | import org.sonar.db.component.ComponentDto; | ||||
void scrollClosedByComponentUuid(@Param("componentUuid") String componentUuid, @Param("closeDateAfter") long closeDateAfter, ResultHandler<IssueDto> handler); | void scrollClosedByComponentUuid(@Param("componentUuid") String componentUuid, @Param("closeDateAfter") long closeDateAfter, ResultHandler<IssueDto> handler); | ||||
Cursor<IndexedIssueDto> scrollIssuesForIndexation(@Nullable @Param("branchUuid") String branchUuid, @Nullable @Param("issueKeys") Collection<String> issueKeys); | |||||
Collection<IssueGroupDto> selectIssueGroupsByComponent(@Param("component") ComponentDto component, @Param("leakPeriodBeginningDate") long leakPeriodBeginningDate); | Collection<IssueGroupDto> selectIssueGroupsByComponent(@Param("component") ComponentDto component, @Param("leakPeriodBeginningDate") long leakPeriodBeginningDate); | ||||
List<IssueDto> selectByBranch(@Param("keys") Set<String> keys, @Nullable @Param("changedSince") Long changedSince); | List<IssueDto> selectByBranch(@Param("keys") Set<String> keys, @Nullable @Param("changedSince") Long changedSince); |
i.kee, ic.issue_change_creation_date desc | i.kee, ic.issue_change_creation_date desc | ||||
</select> | </select> | ||||
<resultMap id="indexedIssueResultMap" type="org.sonar.db.issue.IndexedIssueDto" autoMapping="true"> | |||||
<id property="issueKey" column="issueKey"/> | |||||
<collection property="impacts" column="ii_uuid" notNullColumn="ii_uuid" | |||||
javaType="java.util.Set" ofType="Impact"> | |||||
<id property="uuid" column="ii_uuid"/> | |||||
<result property="softwareQuality" column="ii_softwareQuality"/> | |||||
<result property="severity" column="ii_severity"/> | |||||
</collection> | |||||
<collection property="ruleDefaultImpacts" column="rdi_uuid" notNullColumn="rdi_uuid" | |||||
javaType="java.util.Set" ofType="Impact"> | |||||
<id property="uuid" column="rdi_uuid"/> | |||||
<result property="softwareQuality" column="rdi_softwareQuality"/> | |||||
<result property="severity" column="rdi_severity"/> | |||||
</collection> | |||||
</resultMap> | |||||
<select id="scrollIssuesForIndexation" parameterType="map" resultMap="indexedIssueResultMap" fetchSize="${_scrollFetchSize}" | |||||
resultSetType="FORWARD_ONLY" resultOrdered="true"> | |||||
select | |||||
i.kee as issueKey, | |||||
i.assignee, | |||||
i.line, | |||||
i.resolution, | |||||
i.severity, | |||||
i.status, | |||||
i.effort, | |||||
i.author_login as authorLogin, | |||||
i.issue_close_date as issueCloseDate, | |||||
i.issue_creation_date as issueCreationDate, | |||||
i.issue_update_date as issueUpdateDate, | |||||
r.uuid as ruleUuid, | |||||
r.language as language, | |||||
r.clean_code_attribute as cleanCodeAttribute, | |||||
c.uuid as componentUuid, | |||||
c.path, | |||||
c.scope, | |||||
c.branch_uuid as branchUuid, | |||||
pb.is_main as isMain, | |||||
pb.project_uuid as projectUuid, | |||||
i.tags, | |||||
i.issue_type as issueType, | |||||
r.security_standards as securityStandards, | |||||
c.qualifier, | |||||
i.code_variants as codeVariants, | |||||
<include refid="issueImpactsColumns"/> | |||||
<include refid="ruleDefaultImpactsColumns"/> | |||||
<include refid="isNewCodeReferenceIssue"/> | |||||
from issues i | |||||
inner join rules r on r.uuid = i.rule_uuid | |||||
inner join components c on c.uuid = i.component_uuid | |||||
inner join project_branches pb on c.branch_uuid = pb.uuid | |||||
left join new_code_reference_issues n on n.issue_key = i.kee | |||||
left outer join issues_impacts ii on i.kee = ii.issue_key | |||||
left outer join rules_default_impacts rdi on r.uuid = rdi.rule_uuid | |||||
<where> | |||||
<if test="branchUuid != null"> | |||||
and c.branch_uuid = #{branchUuid,jdbcType=VARCHAR} and i.project_uuid = #{branchUuid,jdbcType=VARCHAR} | |||||
</if> | |||||
<if test="issueKeys != null"> | |||||
and i.kee in | |||||
<foreach collection="issueKeys" open="(" close=")" item="key" separator=","> | |||||
#{key,jdbcType=VARCHAR} | |||||
</foreach> | |||||
</if> | |||||
</where> | |||||
order by i.kee | |||||
</select> | |||||
<select id="selectComponentUuidsOfOpenIssuesForProjectUuid" parameterType="string" resultType="string"> | <select id="selectComponentUuidsOfOpenIssuesForProjectUuid" parameterType="string" resultType="string"> | ||||
select distinct(i.component_uuid) | select distinct(i.component_uuid) | ||||
from issues i | from issues i |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 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.db.issue; | |||||
import org.junit.Test; | |||||
import org.sonar.api.issue.impact.Severity; | |||||
import org.sonar.api.issue.impact.SoftwareQuality; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.assertj.core.groups.Tuple.tuple; | |||||
public class IndexedIssueDtoTest { | |||||
@Test | |||||
public void settersGetters_shouldSetAndGetValues() { | |||||
IndexedIssueDto indexedIssueDto = new IndexedIssueDto() | |||||
.setIssueKey("issueKey") | |||||
.setAssignee("assignee") | |||||
.setAuthorLogin("authorLogin") | |||||
.setStatus("status") | |||||
.setNewCodeReferenceIssue(true) | |||||
.setCleanCodeAttribute("cleanCodeAttribute") | |||||
.setCodeVariants("codeVariants") | |||||
.setSecurityStandards("securityStandards") | |||||
.setComponentUuid("componentUuid") | |||||
.setIssueCloseDate(1L) | |||||
.setIssueCreationDate(2L) | |||||
.setIssueUpdateDate(3L) | |||||
.setEffort(4L) | |||||
.setIsMain(true) | |||||
.setLanguage("language") | |||||
.setLine(5) | |||||
.setPath("path") | |||||
.setProjectUuid("projectUuid") | |||||
.setQualifier("qualifier") | |||||
.setResolution("resolution") | |||||
.setRuleUuid("ruleUuid") | |||||
.setScope("scope") | |||||
.setSeverity("severity") | |||||
.setTags("tags") | |||||
.setIssueType(6) | |||||
.setBranchUuid("branchUuid"); | |||||
indexedIssueDto.getImpacts().add(new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH)); | |||||
indexedIssueDto.getRuleDefaultImpacts().add(new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.MEDIUM)); | |||||
assertThat(indexedIssueDto) | |||||
.extracting(IndexedIssueDto::getIssueKey, IndexedIssueDto::getAssignee, IndexedIssueDto::getAuthorLogin, IndexedIssueDto::getStatus, | |||||
IndexedIssueDto::isNewCodeReferenceIssue, IndexedIssueDto::getCleanCodeAttribute, IndexedIssueDto::getCodeVariants, | |||||
IndexedIssueDto::getSecurityStandards, IndexedIssueDto::getComponentUuid, IndexedIssueDto::getIssueCloseDate, IndexedIssueDto::getIssueCreationDate, | |||||
IndexedIssueDto::getIssueUpdateDate, IndexedIssueDto::getEffort, IndexedIssueDto::isMain, IndexedIssueDto::getLanguage, IndexedIssueDto::getLine, | |||||
IndexedIssueDto::getPath, IndexedIssueDto::getProjectUuid, IndexedIssueDto::getQualifier, IndexedIssueDto::getResolution, | |||||
IndexedIssueDto::getRuleUuid, IndexedIssueDto::getScope, IndexedIssueDto::getSeverity, IndexedIssueDto::getTags, IndexedIssueDto::getIssueType, | |||||
IndexedIssueDto::getBranchUuid) | |||||
.containsExactly("issueKey", "assignee", "authorLogin", "status", true, "cleanCodeAttribute", "codeVariants", "securityStandards", | |||||
"componentUuid", 1L, 2L, 3L, 4L, true, "language", 5, "path", "projectUuid", "qualifier", "resolution", "ruleUuid", | |||||
"scope", "severity", "tags", 6, "branchUuid"); | |||||
assertThat(indexedIssueDto.getImpacts()) | |||||
.extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity) | |||||
.containsExactly(tuple(SoftwareQuality.SECURITY, Severity.HIGH)); | |||||
assertThat(indexedIssueDto.getRuleDefaultImpacts()) | |||||
.extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity) | |||||
.containsExactly(tuple(SoftwareQuality.MAINTAINABILITY, Severity.MEDIUM)); | |||||
assertThat(indexedIssueDto.getEffectiveImpacts()) | |||||
.containsEntry(SoftwareQuality.MAINTAINABILITY, Severity.MEDIUM) | |||||
.containsEntry(SoftwareQuality.SECURITY, Severity.HIGH); | |||||
} | |||||
} |
import java.util.Date; | import java.util.Date; | ||||
import java.util.HashSet; | import java.util.HashSet; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | |||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.function.Predicate; | import java.util.function.Predicate; | ||||
import org.assertj.core.api.Assertions; | import org.assertj.core.api.Assertions; | ||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.slf4j.event.Level; | import org.slf4j.event.Level; | ||||
import org.sonar.api.issue.impact.Severity; | |||||
import org.sonar.api.issue.impact.SoftwareQuality; | |||||
import org.sonar.api.resources.Qualifiers; | import org.sonar.api.resources.Qualifiers; | ||||
import org.sonar.api.testfixtures.log.LogTester; | import org.sonar.api.testfixtures.log.LogTester; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import static org.sonar.server.es.Indexers.EntityEvent.PROJECT_KEY_UPDATE; | import static org.sonar.server.es.Indexers.EntityEvent.PROJECT_KEY_UPDATE; | ||||
import static org.sonar.server.es.Indexers.EntityEvent.PROJECT_TAGS_UPDATE; | import static org.sonar.server.es.Indexers.EntityEvent.PROJECT_TAGS_UPDATE; | ||||
import static org.sonar.server.issue.IssueDocTesting.newDoc; | import static org.sonar.server.issue.IssueDocTesting.newDoc; | ||||
import static org.sonar.server.issue.index.IssueIndexDefinition.SUB_FIELD_SOFTWARE_QUALITY; | |||||
import static org.sonar.server.issue.index.IssueIndexDefinition.SUB_FIELD_SEVERITY; | |||||
import static org.sonar.server.issue.index.IssueIndexDefinition.TYPE_ISSUE; | import static org.sonar.server.issue.index.IssueIndexDefinition.TYPE_ISSUE; | ||||
import static org.sonar.server.permission.index.IndexAuthorizationConstants.TYPE_AUTHORIZATION; | import static org.sonar.server.permission.index.IndexAuthorizationConstants.TYPE_AUTHORIZATION; | ||||
import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_POROUS_DEFENSES; | import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_POROUS_DEFENSES; | ||||
assertThat(doc.getSansTop25()).isEmpty(); | assertThat(doc.getSansTop25()).isEmpty(); | ||||
assertThat(doc.getSonarSourceSecurityCategory()).isEqualTo(SQCategory.OTHERS); | assertThat(doc.getSonarSourceSecurityCategory()).isEqualTo(SQCategory.OTHERS); | ||||
assertThat(doc.getVulnerabilityProbability()).isEqualTo(VulnerabilityProbability.LOW); | assertThat(doc.getVulnerabilityProbability()).isEqualTo(VulnerabilityProbability.LOW); | ||||
assertThat(doc.impacts()) | |||||
.containsExactlyInAnyOrder(Map.of( | |||||
SUB_FIELD_SOFTWARE_QUALITY, SoftwareQuality.MAINTAINABILITY.name(), | |||||
SUB_FIELD_SEVERITY, Severity.HIGH.name())); | |||||
} | } | ||||
@Test | @Test |
assertThat(issue.effort().toMinutes()).isPositive(); | assertThat(issue.effort().toMinutes()).isPositive(); | ||||
assertThat(issue.type().getDbConstant()).isEqualTo(2); | assertThat(issue.type().getDbConstant()).isEqualTo(2); | ||||
assertThat(issue.getCodeVariants()).containsOnly("variant1", "variant2"); | assertThat(issue.getCodeVariants()).containsOnly("variant1", "variant2"); | ||||
assertThat(issue.cleanCodeAttributeCategory()).isEqualTo("INTENTIONAL"); | |||||
} | } | ||||
@Test | @Test |
import com.google.common.collect.Maps; | import com.google.common.collect.Maps; | ||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.List; | |||||
import java.util.Map; | import java.util.Map; | ||||
import javax.annotation.CheckForNull; | import javax.annotation.CheckForNull; | ||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.sonar.api.issue.impact.SoftwareQuality; | |||||
import org.sonar.api.rule.Severity; | import org.sonar.api.rule.Severity; | ||||
import org.sonar.api.rules.RuleType; | import org.sonar.api.rules.RuleType; | ||||
import org.sonar.api.utils.Duration; | import org.sonar.api.utils.Duration; | ||||
import org.sonar.server.security.SecurityStandards; | import org.sonar.server.security.SecurityStandards; | ||||
import org.sonar.server.security.SecurityStandards.VulnerabilityProbability; | import org.sonar.server.security.SecurityStandards.VulnerabilityProbability; | ||||
import static org.sonar.server.issue.index.IssueIndexDefinition.SUB_FIELD_SOFTWARE_QUALITY; | |||||
import static org.sonar.server.issue.index.IssueIndexDefinition.SUB_FIELD_SEVERITY; | |||||
import static org.sonar.server.issue.index.IssueIndexDefinition.TYPE_ISSUE; | import static org.sonar.server.issue.index.IssueIndexDefinition.TYPE_ISSUE; | ||||
public class IssueDoc extends BaseDoc { | public class IssueDoc extends BaseDoc { | ||||
return getField(IssueIndexDefinition.FIELD_ISSUE_SEVERITY); | return getField(IssueIndexDefinition.FIELD_ISSUE_SEVERITY); | ||||
} | } | ||||
public String cleanCodeAttributeCategory() { | |||||
return getField(IssueIndexDefinition.FIELD_ISSUE_CLEAN_CODE_ATTRIBUTE_CATEGORY); | |||||
} | |||||
public Collection<Map<String, String>> impacts() { | |||||
return getField(IssueIndexDefinition.FIELD_ISSUE_IMPACTS); | |||||
} | |||||
@CheckForNull | @CheckForNull | ||||
public Integer line() { | public Integer line() { | ||||
return getNullableField(IssueIndexDefinition.FIELD_ISSUE_LINE); | return getNullableField(IssueIndexDefinition.FIELD_ISSUE_LINE); | ||||
return getNullableField(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN); | return getNullableField(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN); | ||||
} | } | ||||
@Deprecated | |||||
public RuleType type() { | public RuleType type() { | ||||
return RuleType.valueOf(getField(IssueIndexDefinition.FIELD_ISSUE_TYPE)); | return RuleType.valueOf(getField(IssueIndexDefinition.FIELD_ISSUE_TYPE)); | ||||
} | } | ||||
return this; | return this; | ||||
} | } | ||||
@Deprecated | |||||
public IssueDoc setSeverity(@Nullable String s) { | public IssueDoc setSeverity(@Nullable String s) { | ||||
setField(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, s); | setField(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, s); | ||||
setField(IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE, Severity.ALL.indexOf(s)); | setField(IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE, Severity.ALL.indexOf(s)); | ||||
return this; | return this; | ||||
} | } | ||||
public IssueDoc setCleanCodeAttributeCategory(@Nullable String s) { | |||||
setField(IssueIndexDefinition.FIELD_ISSUE_CLEAN_CODE_ATTRIBUTE_CATEGORY, s); | |||||
return this; | |||||
} | |||||
public IssueDoc setLine(@Nullable Integer i) { | public IssueDoc setLine(@Nullable Integer i) { | ||||
setField(IssueIndexDefinition.FIELD_ISSUE_LINE, i); | setField(IssueIndexDefinition.FIELD_ISSUE_LINE, i); | ||||
return this; | return this; | ||||
return this; | return this; | ||||
} | } | ||||
@Deprecated | |||||
public IssueDoc setType(RuleType type) { | public IssueDoc setType(RuleType type) { | ||||
setField(IssueIndexDefinition.FIELD_ISSUE_TYPE, type.toString()); | setField(IssueIndexDefinition.FIELD_ISSUE_TYPE, type.toString()); | ||||
return this; | return this; | ||||
} | } | ||||
public IssueDoc setImpacts(Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> softwareQualities) { | |||||
List<Map<String, String>> convertedMap = softwareQualities | |||||
.entrySet() | |||||
.stream() | |||||
.map(entry -> Map.of( | |||||
SUB_FIELD_SOFTWARE_QUALITY, entry.getKey().name(), | |||||
SUB_FIELD_SEVERITY, entry.getValue().name())) | |||||
.toList(); | |||||
setField(IssueIndexDefinition.FIELD_ISSUE_IMPACTS, convertedMap); | |||||
return this; | |||||
} | |||||
@CheckForNull | @CheckForNull | ||||
public Collection<String> getPciDss32() { | public Collection<String> getPciDss32() { | ||||
return getNullableField(IssueIndexDefinition.FIELD_ISSUE_PCI_DSS_32); | return getNullableField(IssueIndexDefinition.FIELD_ISSUE_PCI_DSS_32); |
* Whether issue is new code for a branch using the reference branch new code definition. | * Whether issue is new code for a branch using the reference branch new code definition. | ||||
*/ | */ | ||||
public static final String FIELD_ISSUE_NEW_CODE_REFERENCE = "isNewCodeReference"; | public static final String FIELD_ISSUE_NEW_CODE_REFERENCE = "isNewCodeReference"; | ||||
public static final String FIELD_ISSUE_CLEAN_CODE_ATTRIBUTE_CATEGORY = "cleanCodeAttributeCategory"; | |||||
public static final String FIELD_ISSUE_IMPACTS = "impacts"; | |||||
public static final String SUB_FIELD_SOFTWARE_QUALITY = "softwareQuality"; | |||||
public static final String SUB_FIELD_SEVERITY = "severity"; | |||||
public static final String FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY = FIELD_ISSUE_IMPACTS + "." + SUB_FIELD_SOFTWARE_QUALITY; | |||||
public static final String FIELD_ISSUE_IMPACT_SEVERITY = FIELD_ISSUE_IMPACTS + "." + SUB_FIELD_SEVERITY; | |||||
private final Configuration config; | private final Configuration config; | ||||
private final boolean enableSource; | private final boolean enableSource; | ||||
mapping.keywordFieldBuilder(FIELD_ISSUE_RULE_UUID).disableNorms().build(); | mapping.keywordFieldBuilder(FIELD_ISSUE_RULE_UUID).disableNorms().build(); | ||||
mapping.keywordFieldBuilder(FIELD_ISSUE_SEVERITY).disableNorms().build(); | mapping.keywordFieldBuilder(FIELD_ISSUE_SEVERITY).disableNorms().build(); | ||||
mapping.createByteField(FIELD_ISSUE_SEVERITY_VALUE); | mapping.createByteField(FIELD_ISSUE_SEVERITY_VALUE); | ||||
mapping.keywordFieldBuilder(FIELD_ISSUE_CLEAN_CODE_ATTRIBUTE_CATEGORY).disableNorms().build(); | |||||
mapping.nestedFieldBuilder(FIELD_ISSUE_IMPACTS) | |||||
.addKeywordField(SUB_FIELD_SOFTWARE_QUALITY) | |||||
.addKeywordField(SUB_FIELD_SEVERITY) | |||||
.build(); | |||||
mapping.keywordFieldBuilder(FIELD_ISSUE_STATUS).disableNorms().addSubFields(SORTABLE_ANALYZER).build(); | mapping.keywordFieldBuilder(FIELD_ISSUE_STATUS).disableNorms().addSubFields(SORTABLE_ANALYZER).build(); | ||||
mapping.keywordFieldBuilder(FIELD_ISSUE_TAGS).disableNorms().build(); | mapping.keywordFieldBuilder(FIELD_ISSUE_TAGS).disableNorms().build(); | ||||
mapping.keywordFieldBuilder(FIELD_ISSUE_TYPE).disableNorms().build(); | mapping.keywordFieldBuilder(FIELD_ISSUE_TYPE).disableNorms().build(); |
import com.google.common.base.CharMatcher; | import com.google.common.base.CharMatcher; | ||||
import com.google.common.base.Splitter; | import com.google.common.base.Splitter; | ||||
import java.sql.PreparedStatement; | |||||
import java.sql.ResultSet; | |||||
import java.sql.SQLException; | |||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.stream.Collectors; | |||||
import java.util.stream.IntStream; | |||||
import java.util.Iterator; | |||||
import java.util.Optional; | |||||
import javax.annotation.CheckForNull; | import javax.annotation.CheckForNull; | ||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.apache.commons.lang.StringUtils; | |||||
import org.apache.ibatis.cursor.Cursor; | |||||
import org.sonar.api.resources.Qualifiers; | import org.sonar.api.resources.Qualifiers; | ||||
import org.sonar.api.resources.Scopes; | import org.sonar.api.resources.Scopes; | ||||
import org.sonar.api.rules.CleanCodeAttribute; | |||||
import org.sonar.api.rules.RuleType; | import org.sonar.api.rules.RuleType; | ||||
import org.sonar.db.DatabaseUtils; | import org.sonar.db.DatabaseUtils; | ||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.ResultSetIterator; | |||||
import org.sonar.db.issue.IndexedIssueDto; | |||||
import org.sonar.server.security.SecurityStandards; | import org.sonar.server.security.SecurityStandards; | ||||
import static com.google.common.base.Preconditions.checkArgument; | import static com.google.common.base.Preconditions.checkArgument; | ||||
import static org.elasticsearch.common.Strings.isNullOrEmpty; | |||||
import static org.sonar.api.utils.DateUtils.longToDate; | import static org.sonar.api.utils.DateUtils.longToDate; | ||||
import static org.sonar.db.DatabaseUtils.getLong; | |||||
import static org.sonar.db.rule.RuleDto.deserializeSecurityStandardsString; | import static org.sonar.db.rule.RuleDto.deserializeSecurityStandardsString; | ||||
import static org.sonar.server.security.SecurityStandards.fromSecurityStandards; | import static org.sonar.server.security.SecurityStandards.fromSecurityStandards; | ||||
*/ | */ | ||||
class IssueIteratorForSingleChunk implements IssueIterator { | class IssueIteratorForSingleChunk implements IssueIterator { | ||||
private static final String[] FIELDS = { | |||||
"i.kee", | |||||
"i.assignee", | |||||
"i.line", | |||||
"i.resolution", | |||||
"i.severity", | |||||
"i.status", | |||||
"i.effort", | |||||
"i.author_login", | |||||
"i.issue_close_date", | |||||
"i.issue_creation_date", | |||||
"i.issue_update_date", | |||||
"r.uuid", | |||||
"r.language", | |||||
"c.uuid", | |||||
"c.path", | |||||
"c.scope", | |||||
"c.branch_uuid", | |||||
"pb.is_main", | |||||
"pb.project_uuid", | |||||
"i.tags", | |||||
"i.issue_type", | |||||
"r.security_standards", | |||||
"c.qualifier", | |||||
"n.uuid", | |||||
"i.code_variants" | |||||
}; | |||||
private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from issues i " + | |||||
"inner join rules r on r.uuid = i.rule_uuid " + | |||||
"inner join components c on c.uuid = i.component_uuid " + | |||||
"inner join project_branches pb on c.branch_uuid = pb.uuid "; | |||||
private static final String SQL_NEW_CODE_JOIN = "left join new_code_reference_issues n on n.issue_key = i.kee "; | |||||
private static final String BRANCH_FILTER = " and c.branch_uuid = ? and i.project_uuid = ? "; | |||||
private static final String ISSUE_KEY_FILTER_PREFIX = " and i.kee in ("; | |||||
private static final String ISSUE_KEY_FILTER_SUFFIX = ") "; | |||||
static final Splitter STRING_LIST_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); | static final Splitter STRING_LIST_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); | ||||
private final DbSession session; | private final DbSession session; | ||||
@CheckForNull | |||||
private final String branchUuid; | |||||
@CheckForNull | |||||
private final Collection<String> issueKeys; | |||||
private final PreparedStatement stmt; | |||||
private final ResultSetIterator<IssueDoc> iterator; | |||||
private final Iterator<IndexedIssueDto> iterator; | |||||
IssueIteratorForSingleChunk(DbClient dbClient, @Nullable String branchUuid, @Nullable Collection<String> issueKeys) { | IssueIteratorForSingleChunk(DbClient dbClient, @Nullable String branchUuid, @Nullable Collection<String> issueKeys) { | ||||
checkArgument(issueKeys == null || issueKeys.size() <= DatabaseUtils.PARTITION_SIZE_FOR_ORACLE, | checkArgument(issueKeys == null || issueKeys.size() <= DatabaseUtils.PARTITION_SIZE_FOR_ORACLE, | ||||
"Cannot search for more than " + DatabaseUtils.PARTITION_SIZE_FOR_ORACLE + " issue keys at once. Please provide the keys in smaller chunks."); | "Cannot search for more than " + DatabaseUtils.PARTITION_SIZE_FOR_ORACLE + " issue keys at once. Please provide the keys in smaller chunks."); | ||||
this.branchUuid = branchUuid; | |||||
this.issueKeys = issueKeys; | |||||
this.session = dbClient.openSession(false); | this.session = dbClient.openSession(false); | ||||
try { | try { | ||||
String sql = createSql(); | |||||
stmt = dbClient.getMyBatis().newScrollingSelectStatement(session, sql); | |||||
iterator = createIterator(); | |||||
Cursor<IndexedIssueDto> indexCursor = dbClient.issueDao().scrollIssuesForIndexation(session, branchUuid, issueKeys); | |||||
iterator = indexCursor.iterator(); | |||||
} catch (Exception e) { | } catch (Exception e) { | ||||
session.close(); | session.close(); | ||||
throw new IllegalStateException("Fail to prepare SQL request to select all issues", e); | throw new IllegalStateException("Fail to prepare SQL request to select all issues", e); | ||||
} | } | ||||
} | } | ||||
private IssueIteratorInternal createIterator() { | |||||
try { | |||||
setParameters(stmt); | |||||
return new IssueIteratorInternal(stmt); | |||||
} catch (SQLException e) { | |||||
DatabaseUtils.closeQuietly(stmt); | |||||
throw new IllegalStateException("Fail to prepare SQL request to select all issues", e); | |||||
} | |||||
} | |||||
@Override | @Override | ||||
public boolean hasNext() { | public boolean hasNext() { | ||||
return iterator.hasNext(); | return iterator.hasNext(); | ||||
@Override | @Override | ||||
public IssueDoc next() { | public IssueDoc next() { | ||||
return iterator.next(); | |||||
return toIssueDoc(iterator.next()); | |||||
} | } | ||||
private String createSql() { | |||||
String sql = SQL_ALL; | |||||
sql += branchUuid == null ? "" : BRANCH_FILTER; | |||||
if (issueKeys != null && !issueKeys.isEmpty()) { | |||||
sql += ISSUE_KEY_FILTER_PREFIX; | |||||
sql += IntStream.range(0, issueKeys.size()).mapToObj(i -> "?").collect(Collectors.joining(",")); | |||||
sql += ISSUE_KEY_FILTER_SUFFIX; | |||||
} | |||||
sql += SQL_NEW_CODE_JOIN; | |||||
return sql; | |||||
private static IssueDoc toIssueDoc(IndexedIssueDto indexedIssueDto) { | |||||
IssueDoc doc = new IssueDoc(new HashMap<>(30)); | |||||
String key = indexedIssueDto.getIssueKey(); | |||||
// all the fields must be present, even if value is null | |||||
doc.setKey(key); | |||||
doc.setAssigneeUuid(indexedIssueDto.getAssignee()); | |||||
doc.setLine(indexedIssueDto.getLine()); | |||||
doc.setResolution(indexedIssueDto.getResolution()); | |||||
doc.setSeverity(indexedIssueDto.getSeverity()); | |||||
String cleanCodeAttributeCategory = Optional.ofNullable(indexedIssueDto.getCleanCodeAttribute()) | |||||
.map(CleanCodeAttribute::valueOf) | |||||
.map(cleanCodeAttribute -> cleanCodeAttribute.getAttributeCategory().name()) | |||||
.orElse(null); | |||||
//TODO:: uncomment once clean code attribute is set to not-null | |||||
//.orElseThrow(() -> new IllegalStateException("Clean Code Attribute is missing for issue " + key)); | |||||
doc.setCleanCodeAttributeCategory(cleanCodeAttributeCategory); | |||||
doc.setStatus(indexedIssueDto.getStatus()); | |||||
doc.setEffort(indexedIssueDto.getEffort()); | |||||
doc.setAuthorLogin(indexedIssueDto.getAuthorLogin()); | |||||
doc.setFuncCloseDate(longToDate(indexedIssueDto.getIssueCloseDate())); | |||||
doc.setFuncCreationDate(longToDate(indexedIssueDto.getIssueCreationDate())); | |||||
doc.setFuncUpdateDate(longToDate(indexedIssueDto.getIssueUpdateDate())); | |||||
doc.setRuleUuid(indexedIssueDto.getRuleUuid()); | |||||
doc.setLanguage(indexedIssueDto.getLanguage()); | |||||
doc.setComponentUuid(indexedIssueDto.getComponentUuid()); | |||||
String scope = indexedIssueDto.getScope(); | |||||
String filePath = extractFilePath(indexedIssueDto.getPath(), scope); | |||||
doc.setFilePath(filePath); | |||||
doc.setDirectoryPath(extractDirPath(doc.filePath(), scope)); | |||||
String branchUuid = indexedIssueDto.getBranchUuid(); | |||||
boolean isMainBranch = indexedIssueDto.isMain(); | |||||
String projectUuid = indexedIssueDto.getProjectUuid(); | |||||
doc.setBranchUuid(branchUuid); | |||||
doc.setIsMainBranch(isMainBranch); | |||||
doc.setProjectUuid(projectUuid); | |||||
String tags = indexedIssueDto.getTags(); | |||||
doc.setTags(STRING_LIST_SPLITTER.splitToList(tags == null ? "" : tags)); | |||||
doc.setType(RuleType.valueOf(indexedIssueDto.getIssueType())); | |||||
doc.setImpacts(indexedIssueDto.getEffectiveImpacts()); | |||||
SecurityStandards securityStandards = fromSecurityStandards(deserializeSecurityStandardsString(indexedIssueDto.getSecurityStandards())); | |||||
SecurityStandards.SQCategory sqCategory = securityStandards.getSqCategory(); | |||||
doc.setOwaspTop10(securityStandards.getOwaspTop10()); | |||||
doc.setOwaspTop10For2021(securityStandards.getOwaspTop10For2021()); | |||||
doc.setPciDss32(securityStandards.getPciDss32()); | |||||
doc.setPciDss40(securityStandards.getPciDss40()); | |||||
doc.setOwaspAsvs40(securityStandards.getOwaspAsvs40()); | |||||
doc.setCwe(securityStandards.getCwe()); | |||||
doc.setSansTop25(securityStandards.getSansTop25()); | |||||
doc.setSonarSourceSecurityCategory(sqCategory); | |||||
doc.setVulnerabilityProbability(sqCategory.getVulnerability()); | |||||
doc.setScope(Qualifiers.UNIT_TEST_FILE.equals(indexedIssueDto.getQualifier()) ? IssueScope.TEST : IssueScope.MAIN); | |||||
doc.setIsNewCodeReference(indexedIssueDto.isNewCodeReferenceIssue()); | |||||
String codeVariants = indexedIssueDto.getCodeVariants(); | |||||
doc.setCodeVariants(STRING_LIST_SPLITTER.splitToList(codeVariants == null ? "" : codeVariants)); | |||||
return doc; | |||||
} | } | ||||
private void setParameters(PreparedStatement stmt) throws SQLException { | |||||
int index = 1; | |||||
if (branchUuid != null) { | |||||
stmt.setString(index, branchUuid); | |||||
index++; | |||||
stmt.setString(index, branchUuid); | |||||
index++; | |||||
} | |||||
if (issueKeys != null) { | |||||
for (String key : issueKeys) { | |||||
stmt.setString(index, key); | |||||
index++; | |||||
@CheckForNull | |||||
private static String extractDirPath(@Nullable String filePath, String scope) { | |||||
if (filePath != null) { | |||||
if (Scopes.DIRECTORY.equals(scope)) { | |||||
return filePath; | |||||
} | |||||
int lastSlashIndex = CharMatcher.anyOf("/").lastIndexIn(filePath); | |||||
if (lastSlashIndex > 0) { | |||||
return filePath.substring(0, lastSlashIndex); | |||||
} | } | ||||
return "/"; | |||||
} | } | ||||
return null; | |||||
} | } | ||||
@Override | |||||
public void close() { | |||||
try { | |||||
iterator.close(); | |||||
} finally { | |||||
DatabaseUtils.closeQuietly(stmt); | |||||
session.close(); | |||||
@CheckForNull | |||||
private static String extractFilePath(@Nullable String filePath, String scope) { | |||||
// On modules, the path contains the relative path of the module starting from its parent, and in E/S we're only interested in the | |||||
// path | |||||
// of files and directories. | |||||
// That's why the file path should be null on modules and projects. | |||||
if (filePath != null && !Scopes.PROJECT.equals(scope)) { | |||||
return filePath; | |||||
} | } | ||||
return null; | |||||
} | } | ||||
private static final class IssueIteratorInternal extends ResultSetIterator<IssueDoc> { | |||||
public IssueIteratorInternal(PreparedStatement stmt) throws SQLException { | |||||
super(stmt); | |||||
} | |||||
@Override | |||||
protected IssueDoc read(ResultSet rs) throws SQLException { | |||||
IssueDoc doc = new IssueDoc(new HashMap<>(30)); | |||||
String key = rs.getString(1); | |||||
// all the fields must be present, even if value is null | |||||
doc.setKey(key); | |||||
doc.setAssigneeUuid(rs.getString(2)); | |||||
doc.setLine(DatabaseUtils.getInt(rs, 3)); | |||||
doc.setResolution(rs.getString(4)); | |||||
doc.setSeverity(rs.getString(5)); | |||||
doc.setStatus(rs.getString(6)); | |||||
doc.setEffort(getLong(rs, 7)); | |||||
doc.setAuthorLogin(rs.getString(8)); | |||||
doc.setFuncCloseDate(longToDate(getLong(rs, 9))); | |||||
doc.setFuncCreationDate(longToDate(getLong(rs, 10))); | |||||
doc.setFuncUpdateDate(longToDate(getLong(rs, 11))); | |||||
doc.setRuleUuid(rs.getString(12)); | |||||
doc.setLanguage(rs.getString(13)); | |||||
doc.setComponentUuid(rs.getString(14)); | |||||
String scope = rs.getString(16); | |||||
String filePath = extractFilePath(rs.getString(15), scope); | |||||
doc.setFilePath(filePath); | |||||
doc.setDirectoryPath(extractDirPath(doc.filePath(), scope)); | |||||
String branchUuid = rs.getString(17); | |||||
boolean isMainBranch = rs.getBoolean( 18); | |||||
String projectUuid = rs.getString(19); | |||||
doc.setBranchUuid(branchUuid); | |||||
doc.setIsMainBranch(isMainBranch); | |||||
doc.setProjectUuid(projectUuid); | |||||
String tags = rs.getString(20); | |||||
doc.setTags(STRING_LIST_SPLITTER.splitToList(tags == null ? "" : tags)); | |||||
doc.setType(RuleType.valueOf(rs.getInt(21))); | |||||
SecurityStandards securityStandards = fromSecurityStandards(deserializeSecurityStandardsString(rs.getString(22))); | |||||
SecurityStandards.SQCategory sqCategory = securityStandards.getSqCategory(); | |||||
doc.setOwaspTop10(securityStandards.getOwaspTop10()); | |||||
doc.setOwaspTop10For2021(securityStandards.getOwaspTop10For2021()); | |||||
doc.setPciDss32(securityStandards.getPciDss32()); | |||||
doc.setPciDss40(securityStandards.getPciDss40()); | |||||
doc.setOwaspAsvs40(securityStandards.getOwaspAsvs40()); | |||||
doc.setCwe(securityStandards.getCwe()); | |||||
doc.setSansTop25(securityStandards.getSansTop25()); | |||||
doc.setSonarSourceSecurityCategory(sqCategory); | |||||
doc.setVulnerabilityProbability(sqCategory.getVulnerability()); | |||||
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; | |||||
} | |||||
@CheckForNull | |||||
private static String extractDirPath(@Nullable String filePath, String scope) { | |||||
if (filePath != null) { | |||||
if (Scopes.DIRECTORY.equals(scope)) { | |||||
return filePath; | |||||
} | |||||
int lastSlashIndex = CharMatcher.anyOf("/").lastIndexIn(filePath); | |||||
if (lastSlashIndex > 0) { | |||||
return filePath.substring(0, lastSlashIndex); | |||||
} | |||||
return "/"; | |||||
} | |||||
return null; | |||||
} | |||||
@CheckForNull | |||||
private static String extractFilePath(@Nullable String filePath, String scope) { | |||||
// On modules, the path contains the relative path of the module starting from its parent, and in E/S we're only interested in the | |||||
// path | |||||
// of files and directories. | |||||
// That's why the file path should be null on modules and projects. | |||||
if (filePath != null && !Scopes.PROJECT.equals(scope)) { | |||||
return filePath; | |||||
} | |||||
return null; | |||||
} | |||||
@Override | |||||
public void close() { | |||||
session.close(); | |||||
} | } | ||||
} | } |
*/ | */ | ||||
package org.sonar.server.measure.index; | package org.sonar.server.measure.index; | ||||
import com.google.common.collect.ImmutableMap; | |||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.HashMap; | import java.util.HashMap; |
import javax.annotation.CheckForNull; | import javax.annotation.CheckForNull; | ||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.apache.commons.lang.StringUtils; | import org.apache.commons.lang.StringUtils; | ||||
import org.apache.lucene.search.join.ScoreMode; | |||||
import org.elasticsearch.action.search.SearchRequest; | import org.elasticsearch.action.search.SearchRequest; | ||||
import org.elasticsearch.action.search.SearchResponse; | import org.elasticsearch.action.search.SearchResponse; | ||||
import org.elasticsearch.index.query.BoolQueryBuilder; | import org.elasticsearch.index.query.BoolQueryBuilder; | ||||
import org.elasticsearch.search.aggregations.BucketOrder; | import org.elasticsearch.search.aggregations.BucketOrder; | ||||
import org.elasticsearch.search.aggregations.HasAggregations; | import org.elasticsearch.search.aggregations.HasAggregations; | ||||
import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; | import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; | ||||
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregator; | |||||
import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter; | import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter; | ||||
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval; | import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval; | ||||
import org.elasticsearch.search.aggregations.bucket.histogram.LongBounds; | import org.elasticsearch.search.aggregations.bucket.histogram.LongBounds; | ||||
import org.elasticsearch.search.sort.FieldSortBuilder; | import org.elasticsearch.search.sort.FieldSortBuilder; | ||||
import org.joda.time.Duration; | import org.joda.time.Duration; | ||||
import org.sonar.api.issue.Issue; | import org.sonar.api.issue.Issue; | ||||
import org.sonar.api.issue.impact.SoftwareQuality; | |||||
import org.sonar.api.rule.Severity; | import org.sonar.api.rule.Severity; | ||||
import org.sonar.api.rules.CleanCodeAttributeCategory; | |||||
import org.sonar.api.rules.RuleType; | import org.sonar.api.rules.RuleType; | ||||
import org.sonar.api.server.rule.RulesDefinition; | import org.sonar.api.server.rule.RulesDefinition; | ||||
import org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version; | import org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version; | ||||
import static org.elasticsearch.index.query.QueryBuilders.boolQuery; | import static org.elasticsearch.index.query.QueryBuilders.boolQuery; | ||||
import static org.elasticsearch.index.query.QueryBuilders.existsQuery; | import static org.elasticsearch.index.query.QueryBuilders.existsQuery; | ||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; | import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; | ||||
import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; | |||||
import static org.elasticsearch.index.query.QueryBuilders.prefixQuery; | import static org.elasticsearch.index.query.QueryBuilders.prefixQuery; | ||||
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery; | import static org.elasticsearch.index.query.QueryBuilders.rangeQuery; | ||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery; | import static org.elasticsearch.index.query.QueryBuilders.termQuery; | ||||
import static org.elasticsearch.index.query.QueryBuilders.termsQuery; | import static org.elasticsearch.index.query.QueryBuilders.termsQuery; | ||||
import static org.elasticsearch.search.aggregations.AggregationBuilders.filters; | |||||
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; | import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; | ||||
import static org.sonar.api.rules.RuleType.VULNERABILITY; | import static org.sonar.api.rules.RuleType.VULNERABILITY; | ||||
import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars; | import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars; | ||||
import static org.sonar.server.issue.index.IssueIndex.Facet.ASSIGNED_TO_ME; | 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.ASSIGNEES; | ||||
import static org.sonar.server.issue.index.IssueIndex.Facet.AUTHOR; | import static org.sonar.server.issue.index.IssueIndex.Facet.AUTHOR; | ||||
import static org.sonar.server.issue.index.IssueIndex.Facet.CLEAN_CODE_ATTRIBUTE_CATEGORY; | |||||
import static org.sonar.server.issue.index.IssueIndex.Facet.CODE_VARIANTS; | 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.CREATED_AT; | ||||
import static org.sonar.server.issue.index.IssueIndex.Facet.CWE; | import static org.sonar.server.issue.index.IssueIndex.Facet.CWE; | ||||
import static org.sonar.server.issue.index.IssueIndex.Facet.SANS_TOP_25; | import static org.sonar.server.issue.index.IssueIndex.Facet.SANS_TOP_25; | ||||
import static org.sonar.server.issue.index.IssueIndex.Facet.SCOPES; | import static org.sonar.server.issue.index.IssueIndex.Facet.SCOPES; | ||||
import static org.sonar.server.issue.index.IssueIndex.Facet.SEVERITIES; | import static org.sonar.server.issue.index.IssueIndex.Facet.SEVERITIES; | ||||
import static org.sonar.server.issue.index.IssueIndex.Facet.IMPACT_SOFTWARE_QUALITY; | |||||
import static org.sonar.server.issue.index.IssueIndex.Facet.IMPACT_SEVERITY; | |||||
import static org.sonar.server.issue.index.IssueIndex.Facet.SONARSOURCE_SECURITY; | import static org.sonar.server.issue.index.IssueIndex.Facet.SONARSOURCE_SECURITY; | ||||
import static org.sonar.server.issue.index.IssueIndex.Facet.STATUSES; | import static org.sonar.server.issue.index.IssueIndex.Facet.STATUSES; | ||||
import static org.sonar.server.issue.index.IssueIndex.Facet.TAGS; | import static org.sonar.server.issue.index.IssueIndex.Facet.TAGS; | ||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID; | 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_AUTHOR_LOGIN; | ||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID; | import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID; | ||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_CLEAN_CODE_ATTRIBUTE_CATEGORY; | |||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_CODE_VARIANTS; | 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_COMPONENT_UUID; | ||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_CWE; | import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_CWE; | ||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SCOPE; | import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SCOPE; | ||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SEVERITY; | import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SEVERITY; | ||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE; | import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE; | ||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_IMPACTS; | |||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY; | |||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_IMPACT_SEVERITY; | |||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SQ_SECURITY_CATEGORY; | import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SQ_SECURITY_CATEGORY; | ||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_STATUS; | import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_STATUS; | ||||
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_TAGS; | import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_TAGS; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.FACET_MODE_EFFORT; | 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_ASSIGNEES; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_AUTHOR; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_AUTHOR; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES; | |||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CODE_VARIANTS; | 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_CREATED_AT; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CWE; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CWE; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SANS_TOP_25; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SANS_TOP_25; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SCOPES; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SCOPES; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SEVERITIES; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SEVERITIES; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SOFTWARE_QUALITIES; | |||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SOFTWARE_QUALITIES_SEVERTIIES; | |||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SONARSOURCE_SECURITY; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SONARSOURCE_SECURITY; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TAGS; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TAGS; | ||||
public enum Facet { | public enum Facet { | ||||
SEVERITIES(PARAM_SEVERITIES, FIELD_ISSUE_SEVERITY, STICKY, Severity.ALL.size()), | SEVERITIES(PARAM_SEVERITIES, FIELD_ISSUE_SEVERITY, STICKY, Severity.ALL.size()), | ||||
IMPACT_SOFTWARE_QUALITY(PARAM_SOFTWARE_QUALITIES, FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY, STICKY, SoftwareQuality.values().length), | |||||
IMPACT_SEVERITY(PARAM_SOFTWARE_QUALITIES_SEVERTIIES, FIELD_ISSUE_IMPACT_SEVERITY, STICKY, | |||||
org.sonar.api.issue.impact.Severity.values().length), | |||||
CLEAN_CODE_ATTRIBUTE_CATEGORY(PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES, FIELD_ISSUE_CLEAN_CODE_ATTRIBUTE_CATEGORY, STICKY, CleanCodeAttributeCategory.values().length), | |||||
STATUSES(PARAM_STATUSES, FIELD_ISSUE_STATUS, STICKY, Issue.STATUSES.size()), | STATUSES(PARAM_STATUSES, FIELD_ISSUE_STATUS, STICKY, Issue.STATUSES.size()), | ||||
// Resolutions facet returns one more element than the number of resolutions to take into account unresolved issues | // Resolutions facet returns one more element than the number of resolutions to take into account unresolved issues | ||||
RESOLUTIONS(PARAM_RESOLUTIONS, FIELD_ISSUE_RESOLUTION, STICKY, Issue.RESOLUTIONS.size() + 1), | RESOLUTIONS(PARAM_RESOLUTIONS, FIELD_ISSUE_RESOLUTION, STICKY, Issue.RESOLUTIONS.size() + 1), | ||||
filters.addFilter(FIELD_ISSUE_LANGUAGE, LANGUAGES.getFilterScope(), createTermsFilter(FIELD_ISSUE_LANGUAGE, query.languages())); | filters.addFilter(FIELD_ISSUE_LANGUAGE, LANGUAGES.getFilterScope(), createTermsFilter(FIELD_ISSUE_LANGUAGE, query.languages())); | ||||
filters.addFilter(FIELD_ISSUE_TAGS, TAGS.getFilterScope(), createTermsFilter(FIELD_ISSUE_TAGS, query.tags())); | filters.addFilter(FIELD_ISSUE_TAGS, TAGS.getFilterScope(), createTermsFilter(FIELD_ISSUE_TAGS, query.tags())); | ||||
filters.addFilter(FIELD_ISSUE_TYPE, TYPES.getFilterScope(), createTermsFilter(FIELD_ISSUE_TYPE, query.types())); | filters.addFilter(FIELD_ISSUE_TYPE, TYPES.getFilterScope(), createTermsFilter(FIELD_ISSUE_TYPE, query.types())); | ||||
filters.addFilter( | |||||
FIELD_ISSUE_CLEAN_CODE_ATTRIBUTE_CATEGORY, | |||||
CLEAN_CODE_ATTRIBUTE_CATEGORY.getFilterScope(), | |||||
createTermsFilter(FIELD_ISSUE_CLEAN_CODE_ATTRIBUTE_CATEGORY, query.cleanCodeAttributesCategories())); | |||||
filters.addFilter( | filters.addFilter( | ||||
FIELD_ISSUE_RESOLUTION, RESOLUTIONS.getFilterScope(), | FIELD_ISSUE_RESOLUTION, RESOLUTIONS.getFilterScope(), | ||||
createTermsFilter(FIELD_ISSUE_RESOLUTION, query.resolutions())); | createTermsFilter(FIELD_ISSUE_RESOLUTION, query.resolutions())); | ||||
addSecurityCategoryFilter(FIELD_ISSUE_SQ_SECURITY_CATEGORY, SONARSOURCE_SECURITY, query.sonarsourceSecurity(), filters); | addSecurityCategoryFilter(FIELD_ISSUE_SQ_SECURITY_CATEGORY, SONARSOURCE_SECURITY, query.sonarsourceSecurity(), filters); | ||||
addSeverityFilter(query, filters); | addSeverityFilter(query, filters); | ||||
addImpactFilters(query, filters); | |||||
addComponentRelatedFilters(query, filters); | addComponentRelatedFilters(query, filters); | ||||
addDatesFilter(filters, query); | addDatesFilter(filters, query); | ||||
addCreatedAfterByProjectsFilter(filters, query); | addCreatedAfterByProjectsFilter(filters, query); | ||||
} | } | ||||
} | } | ||||
private static Set<String> calculateRequirementsForOwaspAsvs40Params(IssueQuery query) { | private static Set<String> calculateRequirementsForOwaspAsvs40Params(IssueQuery query) { | ||||
int level = query.getOwaspAsvsLevel().orElse(3); | int level = query.getOwaspAsvsLevel().orElse(3); | ||||
List<String> levelRequirements = OWASP_ASVS_40_REQUIREMENTS_BY_LEVEL.get(level); | List<String> levelRequirements = OWASP_ASVS_40_REQUIREMENTS_BY_LEVEL.get(level); | ||||
} | } | ||||
} | } | ||||
private static void addImpactFilters(IssueQuery query, AllFilters allFilters) { | |||||
if (query.impactSoftwareQualities().isEmpty() && query.impactSeverities().isEmpty()) { | |||||
return; | |||||
} | |||||
if (!query.impactSoftwareQualities().isEmpty()) { | |||||
allFilters.addFilter( | |||||
FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY, | |||||
IMPACT_SOFTWARE_QUALITY.getFilterScope(), | |||||
nestedQuery( | |||||
FIELD_ISSUE_IMPACTS, | |||||
termsQuery(FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY, query.impactSoftwareQualities()), | |||||
ScoreMode.Avg)); | |||||
} | |||||
if (!query.impactSeverities().isEmpty()) { | |||||
allFilters.addFilter( | |||||
FIELD_ISSUE_IMPACT_SEVERITY, | |||||
IMPACT_SEVERITY.getFilterScope(), | |||||
nestedQuery( | |||||
FIELD_ISSUE_IMPACTS, | |||||
termsQuery(FIELD_ISSUE_IMPACT_SEVERITY, query.impactSeverities()), | |||||
ScoreMode.Avg)); | |||||
} | |||||
} | |||||
private static void addComponentRelatedFilters(IssueQuery query, AllFilters filters) { | private static void addComponentRelatedFilters(IssueQuery query, AllFilters filters) { | ||||
addCommonComponentRelatedFilters(query, filters); | addCommonComponentRelatedFilters(query, filters); | ||||
if (query.viewUuids().isEmpty()) { | if (query.viewUuids().isEmpty()) { | ||||
private static RequestFiltersComputer newFilterComputer(SearchOptions options, AllFilters allFilters) { | private static RequestFiltersComputer newFilterComputer(SearchOptions options, AllFilters allFilters) { | ||||
Collection<String> facetNames = options.getFacets(); | Collection<String> facetNames = options.getFacets(); | ||||
Set<TopAggregationDefinition<?>> facets = Stream.concat( | Set<TopAggregationDefinition<?>> facets = Stream.concat( | ||||
Stream.of(EFFORT_TOP_AGGREGATION), | |||||
facetNames.stream() | |||||
.map(FACETS_BY_NAME::get) | |||||
.filter(Objects::nonNull) | |||||
.map(Facet::getTopAggregationDef)) | |||||
Stream.of(EFFORT_TOP_AGGREGATION), | |||||
facetNames.stream() | |||||
.map(FACETS_BY_NAME::get) | |||||
.filter(Objects::nonNull) | |||||
.map(Facet::getTopAggregationDef)) | |||||
.collect(Collectors.toSet()); | .collect(Collectors.toSet()); | ||||
return new RequestFiltersComputer(allFilters, facets); | return new RequestFiltersComputer(allFilters, facets); | ||||
addFacetIfNeeded(options, aggregationHelper, esRequest, TAGS, query.tags().toArray()); | addFacetIfNeeded(options, aggregationHelper, esRequest, TAGS, query.tags().toArray()); | ||||
addFacetIfNeeded(options, aggregationHelper, esRequest, TYPES, query.types().toArray()); | addFacetIfNeeded(options, aggregationHelper, esRequest, TYPES, query.types().toArray()); | ||||
addFacetIfNeeded(options, aggregationHelper, esRequest, CODE_VARIANTS, query.codeVariants().toArray()); | addFacetIfNeeded(options, aggregationHelper, esRequest, CODE_VARIANTS, query.codeVariants().toArray()); | ||||
addFacetIfNeeded(options, aggregationHelper, esRequest, CLEAN_CODE_ATTRIBUTE_CATEGORY, query.cleanCodeAttributesCategories().toArray()); | |||||
addSecurityCategoryFacetIfNeeded(PARAM_PCI_DSS_32, PCI_DSS_32, options, aggregationHelper, esRequest, query.pciDss32().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()); | addSecurityCategoryFacetIfNeeded(PARAM_PCI_DSS_40, PCI_DSS_40, options, aggregationHelper, esRequest, query.pciDss40().toArray()); | ||||
addSecurityCategoryFacetIfNeeded(PARAM_SONARSOURCE_SECURITY, SONARSOURCE_SECURITY, options, aggregationHelper, esRequest, query.sonarsourceSecurity().toArray()); | addSecurityCategoryFacetIfNeeded(PARAM_SONARSOURCE_SECURITY, SONARSOURCE_SECURITY, options, aggregationHelper, esRequest, query.sonarsourceSecurity().toArray()); | ||||
addSeverityFacetIfNeeded(options, aggregationHelper, esRequest); | addSeverityFacetIfNeeded(options, aggregationHelper, esRequest); | ||||
addImpactSoftwareQualityFacetIfNeeded(options, query, aggregationHelper, esRequest); | |||||
addImpactSeverityFacetIfNeeded(options, query, aggregationHelper, esRequest); | |||||
addResolutionFacetIfNeeded(options, query, aggregationHelper, esRequest); | addResolutionFacetIfNeeded(options, query, aggregationHelper, esRequest); | ||||
addAssigneesFacetIfNeeded(options, query, aggregationHelper, esRequest); | addAssigneesFacetIfNeeded(options, query, aggregationHelper, esRequest); | ||||
addCreatedAtFacetIfNeeded(options, query, aggregationHelper, queryFilters, esRequest); | addCreatedAtFacetIfNeeded(options, query, aggregationHelper, queryFilters, esRequest); | ||||
esRequest.aggregation(aggregation); | esRequest.aggregation(aggregation); | ||||
} | } | ||||
private static void addImpactSoftwareQualityFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest) { | |||||
if (!options.getFacets().contains(PARAM_SOFTWARE_QUALITIES)) { | |||||
return; | |||||
} | |||||
Function<SoftwareQuality, BoolQueryBuilder> mainQuery = softwareQuality -> boolQuery() | |||||
.filter(termQuery(FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY, softwareQuality.name())); | |||||
FiltersAggregator.KeyedFilter[] keyedFilters = Arrays.stream(SoftwareQuality.values()) | |||||
.map(softwareQuality -> new FiltersAggregator.KeyedFilter(softwareQuality.name(), | |||||
query.impactSeverities().isEmpty() ? mainQuery.apply(softwareQuality) | |||||
: mainQuery.apply(softwareQuality) | |||||
.filter(termsQuery(FIELD_ISSUE_IMPACT_SEVERITY, query.impactSeverities())))) | |||||
.toArray(FiltersAggregator.KeyedFilter[]::new); | |||||
AggregationBuilder aggregation = aggregationHelper.buildTopAggregation( | |||||
IMPACT_SOFTWARE_QUALITY.getName(), IMPACT_SOFTWARE_QUALITY.getTopAggregationDef(), | |||||
NO_EXTRA_FILTER, | |||||
t -> t.subAggregation(AggregationBuilders.nested("nested_" + IMPACT_SOFTWARE_QUALITY.getName(), FIELD_ISSUE_IMPACTS) | |||||
.subAggregation(filters(IMPACT_SOFTWARE_QUALITY.getName(), keyedFilters)))); | |||||
esRequest.aggregation(aggregation); | |||||
} | |||||
private static void addImpactSeverityFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest) { | |||||
if (!options.getFacets().contains(PARAM_SOFTWARE_QUALITIES_SEVERTIIES)) { | |||||
return; | |||||
} | |||||
Function<org.sonar.api.issue.impact.Severity, BoolQueryBuilder> mainQuery = softwareQuality -> boolQuery() | |||||
.filter(termQuery(FIELD_ISSUE_IMPACT_SEVERITY, softwareQuality.name())); | |||||
FiltersAggregator.KeyedFilter[] keyedFilters = Arrays.stream(org.sonar.api.issue.impact.Severity.values()) | |||||
.map(severity -> new FiltersAggregator.KeyedFilter(severity.name(), | |||||
query.impactSoftwareQualities().isEmpty() ? mainQuery.apply(severity) | |||||
: mainQuery.apply(severity) | |||||
.filter(termsQuery(FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY, query.impactSoftwareQualities())))) | |||||
.toArray(FiltersAggregator.KeyedFilter[]::new); | |||||
AggregationBuilder aggregation = aggregationHelper.buildTopAggregation( | |||||
IMPACT_SEVERITY.getName(), IMPACT_SEVERITY.getTopAggregationDef(), | |||||
NO_EXTRA_FILTER, | |||||
t -> t.subAggregation(AggregationBuilders.nested("nested_" + IMPACT_SEVERITY.getName(), FIELD_ISSUE_IMPACTS) | |||||
.subAggregation(filters(IMPACT_SEVERITY.getName(), | |||||
keyedFilters)))); | |||||
esRequest.aggregation(aggregation); | |||||
} | |||||
private static void addResolutionFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest) { | private static void addResolutionFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest) { | ||||
if (!options.getFacets().contains(PARAM_RESOLUTIONS)) { | if (!options.getFacets().contains(PARAM_RESOLUTIONS)) { | ||||
return; | return; | ||||
RESOLUTIONS.getName(), RESOLUTIONS.getTopAggregationDef(), RESOLUTIONS.getNumberOfTerms(), | RESOLUTIONS.getName(), RESOLUTIONS.getTopAggregationDef(), RESOLUTIONS.getNumberOfTerms(), | ||||
NO_EXTRA_FILTER, | NO_EXTRA_FILTER, | ||||
t -> | t -> | ||||
// add aggregation of type "missing" to return count of unresolved issues in the facet | |||||
t.subAggregation( | |||||
addEffortAggregationIfNeeded(query, AggregationBuilders | |||||
.missing(RESOLUTIONS.getName() + FACET_SUFFIX_MISSING) | |||||
.field(RESOLUTIONS.getFieldName())))); | |||||
// add aggregation of type "missing" to return count of unresolved issues in the facet | |||||
t.subAggregation( | |||||
addEffortAggregationIfNeeded(query, AggregationBuilders | |||||
.missing(RESOLUTIONS.getName() + FACET_SUFFIX_MISSING) | |||||
.field(RESOLUTIONS.getFieldName())))); | |||||
esRequest.aggregation(aggregation); | esRequest.aggregation(aggregation); | ||||
} | } | ||||
ASSIGNED_TO_ME.getTopAggregationDef(), | ASSIGNED_TO_ME.getTopAggregationDef(), | ||||
NO_EXTRA_FILTER, | NO_EXTRA_FILTER, | ||||
t -> | t -> | ||||
// add sub-aggregation to return issue count for current user | |||||
aggregationHelper.getSubAggregationHelper() | |||||
.buildSelectedItemsAggregation(ASSIGNED_TO_ME.getName(), ASSIGNED_TO_ME.getTopAggregationDef(), new String[]{uuid}) | |||||
.ifPresent(t::subAggregation)); | |||||
// add sub-aggregation to return issue count for current user | |||||
aggregationHelper.getSubAggregationHelper() | |||||
.buildSelectedItemsAggregation(ASSIGNED_TO_ME.getName(), ASSIGNED_TO_ME.getTopAggregationDef(), new String[] {uuid}) | |||||
.ifPresent(t::subAggregation)); | |||||
esRequest.aggregation(aggregation); | esRequest.aggregation(aggregation); | ||||
} | } | ||||
} | } |
private final Collection<String> issueKeys; | private final Collection<String> issueKeys; | ||||
private final Collection<String> severities; | private final Collection<String> severities; | ||||
private final Collection<String> impactSeverities; | |||||
private final Collection<String> impactSoftwareQualities; | |||||
private final Collection<String> statuses; | private final Collection<String> statuses; | ||||
private final Collection<String> resolutions; | private final Collection<String> resolutions; | ||||
private final Collection<String> components; | private final Collection<String> components; | ||||
private final Boolean newCodeOnReference; | private final Boolean newCodeOnReference; | ||||
private final Collection<String> newCodeOnReferenceByProjectUuids; | private final Collection<String> newCodeOnReferenceByProjectUuids; | ||||
private final Collection<String> codeVariants; | private final Collection<String> codeVariants; | ||||
private Collection<String> cleanCodeAttributesCategories; | |||||
private IssueQuery(Builder builder) { | private IssueQuery(Builder builder) { | ||||
this.issueKeys = defaultCollection(builder.issueKeys); | this.issueKeys = defaultCollection(builder.issueKeys); | ||||
this.severities = defaultCollection(builder.severities); | this.severities = defaultCollection(builder.severities); | ||||
this.impactSeverities = defaultCollection(builder.impactSeverities); | |||||
this.impactSoftwareQualities = defaultCollection(builder.impactSoftwareQualities); | |||||
this.statuses = defaultCollection(builder.statuses); | this.statuses = defaultCollection(builder.statuses); | ||||
this.resolutions = defaultCollection(builder.resolutions); | this.resolutions = defaultCollection(builder.resolutions); | ||||
this.components = defaultCollection(builder.components); | this.components = defaultCollection(builder.components); | ||||
this.newCodeOnReference = builder.newCodeOnReference; | this.newCodeOnReference = builder.newCodeOnReference; | ||||
this.newCodeOnReferenceByProjectUuids = defaultCollection(builder.newCodeOnReferenceByProjectUuids); | this.newCodeOnReferenceByProjectUuids = defaultCollection(builder.newCodeOnReferenceByProjectUuids); | ||||
this.codeVariants = defaultCollection(builder.codeVariants); | this.codeVariants = defaultCollection(builder.codeVariants); | ||||
this.cleanCodeAttributesCategories = defaultCollection(builder.cleanCodeAttributesCategories); | |||||
} | } | ||||
public Collection<String> issueKeys() { | public Collection<String> issueKeys() { | ||||
return severities; | return severities; | ||||
} | } | ||||
public Collection<String> impactSeverities() { | |||||
return impactSeverities; | |||||
} | |||||
public Collection<String> impactSoftwareQualities() { | |||||
return impactSoftwareQualities; | |||||
} | |||||
public Collection<String> statuses() { | public Collection<String> statuses() { | ||||
return statuses; | return statuses; | ||||
} | } | ||||
return codeVariants; | return codeVariants; | ||||
} | } | ||||
public Collection<String> cleanCodeAttributesCategories() { | |||||
return cleanCodeAttributesCategories; | |||||
} | |||||
public static class Builder { | public static class Builder { | ||||
private Collection<String> issueKeys; | private Collection<String> issueKeys; | ||||
private Collection<String> severities; | private Collection<String> severities; | ||||
private Collection<String> impactSeverities; | |||||
private Collection<String> impactSoftwareQualities; | |||||
private Collection<String> statuses; | private Collection<String> statuses; | ||||
private Collection<String> resolutions; | private Collection<String> resolutions; | ||||
private Collection<String> components; | private Collection<String> components; | ||||
private Boolean newCodeOnReference = null; | private Boolean newCodeOnReference = null; | ||||
private Collection<String> newCodeOnReferenceByProjectUuids; | private Collection<String> newCodeOnReferenceByProjectUuids; | ||||
private Collection<String> codeVariants; | private Collection<String> codeVariants; | ||||
private Collection<String> cleanCodeAttributesCategories; | |||||
private Builder() { | private Builder() { | ||||
return this; | return this; | ||||
} | } | ||||
public Builder impactSeverities(@Nullable Collection<String> l) { | |||||
this.impactSeverities = l; | |||||
return this; | |||||
} | |||||
public Builder impactSoftwareQualities(@Nullable Collection<String> l) { | |||||
this.impactSoftwareQualities = l; | |||||
return this; | |||||
} | |||||
public Builder files(@Nullable Collection<String> l) { | public Builder files(@Nullable Collection<String> l) { | ||||
this.files = l; | this.files = l; | ||||
return this; | return this; | ||||
this.codeVariants = codeVariants; | this.codeVariants = codeVariants; | ||||
return this; | return this; | ||||
} | } | ||||
public Builder cleanCodeAttributesCategories(@Nullable Collection<String> cleanCodeAttributesCategories) { | |||||
this.cleanCodeAttributesCategories = cleanCodeAttributesCategories; | |||||
return this; | |||||
} | |||||
} | } | ||||
private static <T> Collection<T> defaultCollection(@Nullable Collection<T> c) { | private static <T> Collection<T> defaultCollection(@Nullable Collection<T> c) { |
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | |||||
import org.elasticsearch.action.search.SearchResponse; | import org.elasticsearch.action.search.SearchResponse; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.sonar.api.issue.impact.Severity; | |||||
import org.sonar.api.rules.RuleType; | import org.sonar.api.rules.RuleType; | ||||
import org.sonar.api.server.rule.RulesDefinition.OwaspAsvsVersion; | import org.sonar.api.server.rule.RulesDefinition.OwaspAsvsVersion; | ||||
import org.sonar.db.component.ComponentDto; | import org.sonar.db.component.ComponentDto; | ||||
import static org.sonar.api.issue.Issue.STATUS_OPEN; | import static org.sonar.api.issue.Issue.STATUS_OPEN; | ||||
import static org.sonar.api.issue.Issue.STATUS_REOPENED; | import static org.sonar.api.issue.Issue.STATUS_REOPENED; | ||||
import static org.sonar.api.issue.Issue.STATUS_RESOLVED; | import static org.sonar.api.issue.Issue.STATUS_RESOLVED; | ||||
import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY; | |||||
import static org.sonar.api.issue.impact.SoftwareQuality.RELIABILITY; | |||||
import static org.sonar.api.rule.Severity.BLOCKER; | import static org.sonar.api.rule.Severity.BLOCKER; | ||||
import static org.sonar.api.rule.Severity.CRITICAL; | import static org.sonar.api.rule.Severity.CRITICAL; | ||||
import static org.sonar.api.rule.Severity.INFO; | import static org.sonar.api.rule.Severity.INFO; | ||||
import static org.sonar.api.rule.Severity.MAJOR; | import static org.sonar.api.rule.Severity.MAJOR; | ||||
import static org.sonar.api.rule.Severity.MINOR; | import static org.sonar.api.rule.Severity.MINOR; | ||||
import static org.sonar.api.rules.CleanCodeAttributeCategory.ADAPTABLE; | |||||
import static org.sonar.api.rules.CleanCodeAttributeCategory.CONSISTENT; | |||||
import static org.sonar.api.rules.CleanCodeAttributeCategory.INTENTIONAL; | |||||
import static org.sonar.api.rules.CleanCodeAttributeCategory.RESPONSIBLE; | |||||
import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2017; | import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2017; | ||||
import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2021; | import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2021; | ||||
import static org.sonar.api.server.rule.RulesDefinition.PciDssVersion.V3_2; | import static org.sonar.api.server.rule.RulesDefinition.PciDssVersion.V3_2; | ||||
@Test | @Test | ||||
public void facet_on_directories_return_100_entries_plus_selected_values() { | public void facet_on_directories_return_100_entries_plus_selected_values() { | ||||
ComponentDto project = newPrivateProjectDto(); | ComponentDto project = newPrivateProjectDto(); | ||||
indexIssues(rangeClosed(1, 110).mapToObj(i -> newDoc(newFileDto(project, newDirectory(project, "dir" + i)), project.uuid()).setDirectoryPath("a" + i)).toArray(IssueDoc[]::new)); | |||||
indexIssues( | |||||
rangeClosed(1, 110).mapToObj(i -> newDoc(newFileDto(project, newDirectory(project, "dir" + i)), project.uuid()).setDirectoryPath("a" + i)).toArray(IssueDoc[]::new)); | |||||
IssueDoc issue1 = newDoc(newFileDto(project, newDirectory(project, "path1")), project.uuid()).setDirectoryPath("directory1"); | IssueDoc issue1 = newDoc(newFileDto(project, newDirectory(project, "path1")), project.uuid()).setDirectoryPath("directory1"); | ||||
IssueDoc issue2 = newDoc(newFileDto(project, newDirectory(project, "path2")), project.uuid()).setDirectoryPath("directory2"); | IssueDoc issue2 = newDoc(newFileDto(project, newDirectory(project, "path2")), project.uuid()).setDirectoryPath("directory2"); | ||||
indexIssues(issue1, issue2); | indexIssues(issue1, issue2); | ||||
SearchOptions options = fixtureForCreatedAtFacet(); | SearchOptions options = fixtureForCreatedAtFacet(); | ||||
SearchResponse result = underTest.search(IssueQuery.builder() | SearchResponse result = underTest.search(IssueQuery.builder() | ||||
.createdAfter(parseDateTime("2014-09-01T00:00:00+0100")) | |||||
.createdBefore(parseDateTime("2014-09-21T00:00:00+0100")).build(), | |||||
.createdAfter(parseDateTime("2014-09-01T00:00:00+0100")) | |||||
.createdBefore(parseDateTime("2014-09-21T00:00:00+0100")).build(), | |||||
options); | options); | ||||
Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt"); | Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt"); | ||||
assertThat(createdAt).containsOnly( | assertThat(createdAt).containsOnly( | ||||
SearchOptions options = fixtureForCreatedAtFacet(); | SearchOptions options = fixtureForCreatedAtFacet(); | ||||
SearchResponse result = underTest.search(IssueQuery.builder() | SearchResponse result = underTest.search(IssueQuery.builder() | ||||
.createdAfter(parseDateTime("2014-09-01T00:00:00+0100")) | |||||
.createdBefore(parseDateTime("2015-01-19T00:00:00+0100")).build(), | |||||
.createdAfter(parseDateTime("2014-09-01T00:00:00+0100")) | |||||
.createdBefore(parseDateTime("2015-01-19T00:00:00+0100")).build(), | |||||
options); | options); | ||||
Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt"); | Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt"); | ||||
assertThat(createdAt).containsOnly( | assertThat(createdAt).containsOnly( | ||||
SearchOptions options = fixtureForCreatedAtFacet(); | SearchOptions options = fixtureForCreatedAtFacet(); | ||||
SearchResponse result = underTest.search(IssueQuery.builder() | SearchResponse result = underTest.search(IssueQuery.builder() | ||||
.createdAfter(parseDateTime("2011-01-01T00:00:00+0100")) | |||||
.createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(), | |||||
.createdAfter(parseDateTime("2011-01-01T00:00:00+0100")) | |||||
.createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(), | |||||
options); | options); | ||||
Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt"); | Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt"); | ||||
assertThat(createdAt).containsOnly( | assertThat(createdAt).containsOnly( | ||||
SearchOptions options = fixtureForCreatedAtFacet(); | SearchOptions options = fixtureForCreatedAtFacet(); | ||||
SearchResponse result = underTest.search(IssueQuery.builder() | SearchResponse result = underTest.search(IssueQuery.builder() | ||||
.createdAfter(parseDateTime("2014-09-01T00:00:00-0100")) | |||||
.createdBefore(parseDateTime("2014-09-02T00:00:00-0100")).build(), | |||||
.createdAfter(parseDateTime("2014-09-01T00:00:00-0100")) | |||||
.createdBefore(parseDateTime("2014-09-02T00:00:00-0100")).build(), | |||||
options); | options); | ||||
Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt"); | Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt"); | ||||
assertThat(createdAt).containsOnly( | assertThat(createdAt).containsOnly( | ||||
SearchOptions searchOptions = fixtureForCreatedAtFacet(); | SearchOptions searchOptions = fixtureForCreatedAtFacet(); | ||||
SearchResponse result = underTest.search(IssueQuery.builder() | SearchResponse result = underTest.search(IssueQuery.builder() | ||||
.createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(), | |||||
.createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(), | |||||
searchOptions); | searchOptions); | ||||
Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt"); | Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt"); | ||||
assertThat(createdAt).containsOnly( | assertThat(createdAt).containsOnly( | ||||
entry("variant3", 1L)); | entry("variant3", 1L)); | ||||
} | } | ||||
@Test | |||||
public void search_shouldReturnSoftwareQualityFacet() { | |||||
ComponentDto project = newPrivateProjectDto(); | |||||
ComponentDto file = newFileDto(project); | |||||
indexIssues( | |||||
newDoc("I1", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH, | |||||
RELIABILITY, org.sonar.api.issue.impact.Severity.MEDIUM)), | |||||
newDoc("I2", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)), | |||||
newDoc("I3", project.uuid(), file).setImpacts(Map.of( | |||||
RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH)), | |||||
newDoc("I4", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW))); | |||||
assertThatFacetHasOnly(IssueQuery.builder(), "softwareQualities", | |||||
entry("MAINTAINABILITY", 3L), | |||||
entry("RELIABILITY", 2L), | |||||
entry("SECURITY", 0L)); | |||||
} | |||||
@Test | |||||
public void search_whenFilteredOnSeverity_shouldReturnSoftwareQualityFacet() { | |||||
ComponentDto project = newPrivateProjectDto(); | |||||
ComponentDto file = newFileDto(project); | |||||
indexIssues( | |||||
newDoc("I1", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH, | |||||
RELIABILITY, org.sonar.api.issue.impact.Severity.MEDIUM)) | |||||
.setTags(singletonList("my-tag")), | |||||
newDoc("I2", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)), | |||||
newDoc("I3", project.uuid(), file).setImpacts(Map.of( | |||||
RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH)), | |||||
newDoc("I4", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW))); | |||||
assertThatFacetHasOnly(IssueQuery.builder().impactSoftwareQualities(Set.of(MAINTAINABILITY.name())).impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.LOW.name())), | |||||
"softwareQualities", | |||||
entry("MAINTAINABILITY", 2L), | |||||
entry("RELIABILITY", 0L), | |||||
entry("SECURITY", 0L)); | |||||
assertThatFacetHasOnly(IssueQuery.builder().impactSeverities(Set.of(Severity.MEDIUM.name())), "softwareQualities", | |||||
entry("MAINTAINABILITY", 0L), | |||||
entry("RELIABILITY", 1L), | |||||
entry("SECURITY", 0L)); | |||||
assertThatFacetHasOnly(IssueQuery.builder().impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.HIGH.name())), "softwareQualities", | |||||
entry("MAINTAINABILITY", 1L), | |||||
entry("RELIABILITY", 1L), | |||||
entry("SECURITY", 0L)); | |||||
assertThatFacetHasOnly(IssueQuery.builder() | |||||
.tags(singletonList("my-tag")) | |||||
.impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.HIGH.name())), "softwareQualities", | |||||
entry("MAINTAINABILITY", 1L), | |||||
entry("RELIABILITY", 0L), | |||||
entry("SECURITY", 0L)); | |||||
} | |||||
@Test | |||||
public void search_shouldReturnSoftwareQualitySeverityFacet() { | |||||
ComponentDto project = newPrivateProjectDto(); | |||||
ComponentDto file = newFileDto(project); | |||||
indexIssues( | |||||
newDoc("I1", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH, | |||||
RELIABILITY, org.sonar.api.issue.impact.Severity.MEDIUM)), | |||||
newDoc("I2", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)), | |||||
newDoc("I3", project.uuid(), file).setImpacts(Map.of( | |||||
RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH)), | |||||
newDoc("I4", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW))); | |||||
assertThatFacetHasOnly(IssueQuery.builder(), "softwareQualitiesSeverities", | |||||
entry("HIGH", 2L), | |||||
entry("MEDIUM", 1L), | |||||
entry("LOW", 2L)); | |||||
} | |||||
@Test | |||||
public void search_whenFilteredOnSoftwareQuality_shouldReturnSoftwareQualitySeverityFacet() { | |||||
ComponentDto project = newPrivateProjectDto(); | |||||
ComponentDto file = newFileDto(project); | |||||
indexIssues( | |||||
newDoc("I1", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH, | |||||
RELIABILITY, org.sonar.api.issue.impact.Severity.MEDIUM)), | |||||
newDoc("I2", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)), | |||||
newDoc("I3", project.uuid(), file).setImpacts(Map.of( | |||||
RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH)), | |||||
newDoc("I4", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW))); | |||||
assertThatFacetHasOnly(IssueQuery.builder().impactSoftwareQualities(Set.of(MAINTAINABILITY.name())), "softwareQualitiesSeverities", | |||||
entry("HIGH", 1L), | |||||
entry("MEDIUM", 0L), | |||||
entry("LOW", 2L)); | |||||
} | |||||
@Test | |||||
public void search_shouldReturnCleanCodeAttributeCategoryFacet() { | |||||
ComponentDto project = newPrivateProjectDto(); | |||||
ComponentDto file = newFileDto(project); | |||||
indexIssues( | |||||
newDoc("I1", project.uuid(), file).setCleanCodeAttributeCategory(ADAPTABLE.name()), | |||||
newDoc("I2", project.uuid(), file).setCleanCodeAttributeCategory(ADAPTABLE.name()), | |||||
newDoc("I3", project.uuid(), file).setCleanCodeAttributeCategory(CONSISTENT.name()), | |||||
newDoc("I4", project.uuid(), file).setCleanCodeAttributeCategory(INTENTIONAL.name()), | |||||
newDoc("I5", project.uuid(), file).setCleanCodeAttributeCategory(INTENTIONAL.name()), | |||||
newDoc("I6", project.uuid(), file).setCleanCodeAttributeCategory(INTENTIONAL.name()), | |||||
newDoc("I7", project.uuid(), file).setCleanCodeAttributeCategory(INTENTIONAL.name()), | |||||
newDoc("I8", project.uuid(), file).setCleanCodeAttributeCategory(RESPONSIBLE.name())); | |||||
assertThatFacetHasOnly(IssueQuery.builder(), "cleanCodeAttributeCategories", | |||||
entry("INTENTIONAL", 4L), | |||||
entry("ADAPTABLE", 2L), | |||||
entry("CONSISTENT", 1L), | |||||
entry("RESPONSIBLE", 1L)); | |||||
} | |||||
@Test | |||||
public void search_whenFilteredByTags_shouldReturnCleanCodeAttributeCategoryFacet() { | |||||
ComponentDto project = newPrivateProjectDto(); | |||||
ComponentDto file = newFileDto(project); | |||||
indexIssues( | |||||
newDoc("I1", project.uuid(), file).setCleanCodeAttributeCategory(ADAPTABLE.name()).setTags(singletonList("tag-1")), | |||||
newDoc("I2", project.uuid(), file).setCleanCodeAttributeCategory(ADAPTABLE.name()).setTags(singletonList("tag-1")), | |||||
newDoc("I3", project.uuid(), file).setCleanCodeAttributeCategory(CONSISTENT.name()), | |||||
newDoc("I4", project.uuid(), file).setCleanCodeAttributeCategory(INTENTIONAL.name()).setTags(singletonList("tag-1")), | |||||
newDoc("I5", project.uuid(), file).setCleanCodeAttributeCategory(INTENTIONAL.name()), | |||||
newDoc("I6", project.uuid(), file).setCleanCodeAttributeCategory(INTENTIONAL.name()), | |||||
newDoc("I7", project.uuid(), file).setCleanCodeAttributeCategory(INTENTIONAL.name()), | |||||
newDoc("I8", project.uuid(), file).setCleanCodeAttributeCategory(RESPONSIBLE.name()).setTags(singletonList("tag-3"))); | |||||
assertThatFacetHasOnly(IssueQuery.builder().tags(singletonList("tag-1")), "cleanCodeAttributeCategories", | |||||
entry("INTENTIONAL", 1L), | |||||
entry("ADAPTABLE", 2L)); | |||||
} | |||||
private SearchOptions fixtureForCreatedAtFacet() { | private SearchOptions fixtureForCreatedAtFacet() { | ||||
ComponentDto project = newPrivateProjectDto(); | ComponentDto project = newPrivateProjectDto(); | ||||
ComponentDto file = newFileDto(project); | ComponentDto file = newFileDto(project); |
*/ | */ | ||||
package org.sonar.server.issue.index; | package org.sonar.server.issue.index; | ||||
import com.google.common.collect.ImmutableMap; | |||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | |||||
import java.util.Set; | import java.util.Set; | ||||
import org.assertj.core.api.Fail; | import org.assertj.core.api.Fail; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import static java.util.Collections.singletonList; | import static java.util.Collections.singletonList; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||||
import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY; | |||||
import static org.sonar.api.issue.impact.SoftwareQuality.RELIABILITY; | |||||
import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY; | |||||
import static org.sonar.api.resources.Qualifiers.APP; | import static org.sonar.api.resources.Qualifiers.APP; | ||||
import static org.sonar.api.rules.CleanCodeAttributeCategory.ADAPTABLE; | |||||
import static org.sonar.api.rules.CleanCodeAttributeCategory.CONSISTENT; | |||||
import static org.sonar.api.rules.CleanCodeAttributeCategory.INTENTIONAL; | |||||
import static org.sonar.api.rules.CleanCodeAttributeCategory.RESPONSIBLE; | |||||
import static org.sonar.api.utils.DateUtils.addDays; | import static org.sonar.api.utils.DateUtils.addDays; | ||||
import static org.sonar.api.utils.DateUtils.parseDate; | import static org.sonar.api.utils.DateUtils.parseDate; | ||||
import static org.sonar.api.utils.DateUtils.parseDateTime; | import static org.sonar.api.utils.DateUtils.parseDateTime; | ||||
// Search for issues of project 1 having less than 15 days | // Search for issues of project 1 having less than 15 days | ||||
assertThatSearchReturnsOnly(IssueQuery.builder() | assertThatSearchReturnsOnly(IssueQuery.builder() | ||||
.createdAfterByProjectUuids(ImmutableMap.of(project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -15), true))), | |||||
.createdAfterByProjectUuids(Map.of(project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -15), true))), | |||||
project1Issue1.key()); | project1Issue1.key()); | ||||
// Search for issues of project 1 having less than 14 days and project 2 having less then 25 days | // Search for issues of project 1 having less than 14 days and project 2 having less then 25 days | ||||
assertThatSearchReturnsOnly(IssueQuery.builder() | assertThatSearchReturnsOnly(IssueQuery.builder() | ||||
.createdAfterByProjectUuids(ImmutableMap.of( | |||||
project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -14), true), | |||||
project2.uuid(), new IssueQuery.PeriodStart(addDays(now, -25), true))), | |||||
.createdAfterByProjectUuids(Map.of( | |||||
project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -14), true), | |||||
project2.uuid(), new IssueQuery.PeriodStart(addDays(now, -25), true))), | |||||
project1Issue1.key(), project2Issue1.key()); | project1Issue1.key(), project2Issue1.key()); | ||||
// Search for issues of project 1 having less than 30 days | // Search for issues of project 1 having less than 30 days | ||||
assertThatSearchReturnsOnly(IssueQuery.builder() | assertThatSearchReturnsOnly(IssueQuery.builder() | ||||
.createdAfterByProjectUuids(ImmutableMap.of( | |||||
project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -30), true))), | |||||
.createdAfterByProjectUuids(Map.of( | |||||
project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -30), true))), | |||||
project1Issue1.key(), project1Issue2.key()); | project1Issue1.key(), project1Issue2.key()); | ||||
// Search for issues of project 1 and project 2 having less than 5 days | // Search for issues of project 1 and project 2 having less than 5 days | ||||
assertThatSearchReturnsOnly(IssueQuery.builder() | assertThatSearchReturnsOnly(IssueQuery.builder() | ||||
.createdAfterByProjectUuids(ImmutableMap.of( | |||||
.createdAfterByProjectUuids(Map.of( | |||||
project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -5), true), | project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -5), true), | ||||
project2.uuid(), new IssueQuery.PeriodStart(addDays(now, -5), true)))); | project2.uuid(), new IssueQuery.PeriodStart(addDays(now, -5), true)))); | ||||
} | } | ||||
// Search for issues of project 1 branch 1 having less than 15 days | // Search for issues of project 1 branch 1 having less than 15 days | ||||
assertThatSearchReturnsOnly(IssueQuery.builder() | assertThatSearchReturnsOnly(IssueQuery.builder() | ||||
.mainBranch(false) | |||||
.createdAfterByProjectUuids(ImmutableMap.of(project1Branch1.uuid(), new IssueQuery.PeriodStart(addDays(now, -15), true))), | |||||
.mainBranch(false) | |||||
.createdAfterByProjectUuids(Map.of(project1Branch1.uuid(), new IssueQuery.PeriodStart(addDays(now, -15), true))), | |||||
project1Branch1Issue1.key()); | project1Branch1Issue1.key()); | ||||
// Search for issues of project 1 branch 1 having less than 14 days and project 2 branch 1 having less then 25 days | // Search for issues of project 1 branch 1 having less than 14 days and project 2 branch 1 having less then 25 days | ||||
assertThatSearchReturnsOnly(IssueQuery.builder() | assertThatSearchReturnsOnly(IssueQuery.builder() | ||||
.mainBranch(false) | |||||
.createdAfterByProjectUuids(ImmutableMap.of( | |||||
project1Branch1.uuid(), new IssueQuery.PeriodStart(addDays(now, -14), true), | |||||
project2Branch1.uuid(), new IssueQuery.PeriodStart(addDays(now, -25), true))), | |||||
.mainBranch(false) | |||||
.createdAfterByProjectUuids(Map.of( | |||||
project1Branch1.uuid(), new IssueQuery.PeriodStart(addDays(now, -14), true), | |||||
project2Branch1.uuid(), new IssueQuery.PeriodStart(addDays(now, -25), true))), | |||||
project1Branch1Issue1.key(), project2Branch1Issue1.key()); | project1Branch1Issue1.key(), project2Branch1Issue1.key()); | ||||
// Search for issues of project 1 branch 1 having less than 30 days | // Search for issues of project 1 branch 1 having less than 30 days | ||||
assertThatSearchReturnsOnly(IssueQuery.builder() | assertThatSearchReturnsOnly(IssueQuery.builder() | ||||
.mainBranch(false) | |||||
.createdAfterByProjectUuids(ImmutableMap.of( | |||||
project1Branch1.uuid(), new IssueQuery.PeriodStart(addDays(now, -30), true))), | |||||
.mainBranch(false) | |||||
.createdAfterByProjectUuids(Map.of( | |||||
project1Branch1.uuid(), new IssueQuery.PeriodStart(addDays(now, -30), true))), | |||||
project1Branch1Issue1.key(), project1Branch1Issue2.key()); | project1Branch1Issue1.key(), project1Branch1Issue2.key()); | ||||
// Search for issues of project 1 branch 1 and project 2 branch 2 having less than 5 days | // Search for issues of project 1 branch 1 and project 2 branch 2 having less than 5 days | ||||
assertThatSearchReturnsOnly(IssueQuery.builder() | assertThatSearchReturnsOnly(IssueQuery.builder() | ||||
.mainBranch(false) | .mainBranch(false) | ||||
.createdAfterByProjectUuids(ImmutableMap.of( | |||||
.createdAfterByProjectUuids(Map.of( | |||||
project1Branch1.uuid(), new IssueQuery.PeriodStart(addDays(now, -5), true), | project1Branch1.uuid(), new IssueQuery.PeriodStart(addDays(now, -5), true), | ||||
project2Branch1.uuid(), new IssueQuery.PeriodStart(addDays(now, -5), true)))); | project2Branch1.uuid(), new IssueQuery.PeriodStart(addDays(now, -5), true)))); | ||||
} | } | ||||
// Search for issues of project 1 and project 2 that are new code on a branch using reference for new code | // Search for issues of project 1 and project 2 that are new code on a branch using reference for new code | ||||
assertThatSearchReturnsOnly(IssueQuery.builder() | assertThatSearchReturnsOnly(IssueQuery.builder() | ||||
.newCodeOnReferenceByProjectUuids(Set.of(project1.uuid(), project2.uuid())), | |||||
.newCodeOnReferenceByProjectUuids(Set.of(project1.uuid(), project2.uuid())), | |||||
project1Issue1.key(), project2Issue2.key()); | project1Issue1.key(), project2Issue2.key()); | ||||
} | } | ||||
// Search for issues of project 1 branch 1 and project 2 branch 1 that are new code on a branch using reference for new code | // Search for issues of project 1 branch 1 and project 2 branch 1 that are new code on a branch using reference for new code | ||||
assertThatSearchReturnsOnly(IssueQuery.builder() | assertThatSearchReturnsOnly(IssueQuery.builder() | ||||
.mainBranch(false) | |||||
.newCodeOnReferenceByProjectUuids(Set.of(project1Branch1.uuid(), project2Branch1.uuid())), | |||||
.mainBranch(false) | |||||
.newCodeOnReferenceByProjectUuids(Set.of(project1Branch1.uuid(), project2Branch1.uuid())), | |||||
project1Branch1Issue2.key(), project2Branch1Issue2.key()); | project1Branch1Issue2.key(), project2Branch1Issue2.key()); | ||||
} | } | ||||
assertThatSearchReturnsOnly(IssueQuery.builder().codeVariants(singletonList("variant2")), "I1", "I2"); | assertThatSearchReturnsOnly(IssueQuery.builder().codeVariants(singletonList("variant2")), "I1", "I2"); | ||||
} | } | ||||
@Test | |||||
public void search_whenFilteringBySoftwareQualities_shouldReturnRelevantIssues() { | |||||
ComponentDto project = newPrivateProjectDto(); | |||||
ComponentDto file = newFileDto(project); | |||||
indexIssues( | |||||
newDoc("I1", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH, | |||||
SECURITY, org.sonar.api.issue.impact.Severity.LOW, | |||||
RELIABILITY, org.sonar.api.issue.impact.Severity.MEDIUM)), | |||||
newDoc("I2", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW, | |||||
SECURITY, org.sonar.api.issue.impact.Severity.LOW)), | |||||
newDoc("I3", project.uuid(), file).setImpacts(Map.of( | |||||
RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH)), | |||||
newDoc("I4", project.uuid(), file).setImpacts(Map.of( | |||||
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW))); | |||||
assertThatSearchReturnsOnly(IssueQuery.builder().impactSoftwareQualities(Set.of(MAINTAINABILITY.name())), | |||||
"I1", "I2", "I4"); | |||||
assertThatSearchReturnsOnly(IssueQuery.builder().impactSoftwareQualities(Set.of(MAINTAINABILITY.name(), RELIABILITY.name())), | |||||
"I1", "I2", "I3", "I4"); | |||||
assertThatSearchReturnsOnly(IssueQuery.builder().impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.HIGH.name())), | |||||
"I1", "I3"); | |||||
assertThatSearchReturnsOnly(IssueQuery.builder().impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.LOW.name(), org.sonar.api.issue.impact.Severity.MEDIUM.name())), | |||||
"I1", "I2", "I4"); | |||||
assertThatSearchReturnsOnly(IssueQuery.builder() | |||||
.impactSoftwareQualities(Set.of(MAINTAINABILITY.name())) | |||||
.impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.HIGH.name())), | |||||
"I1"); | |||||
} | |||||
@Test | |||||
public void search_whenFilteringByCleanCodeAttributeCategory_shouldReturnRelevantIssues() { | |||||
ComponentDto project = newPrivateProjectDto(); | |||||
ComponentDto file = newFileDto(project); | |||||
indexIssues( | |||||
newDoc("I1", project.uuid(), file).setCleanCodeAttributeCategory(ADAPTABLE.name()), | |||||
newDoc("I2", project.uuid(), file).setCleanCodeAttributeCategory(ADAPTABLE.name()), | |||||
newDoc("I3", project.uuid(), file).setCleanCodeAttributeCategory(CONSISTENT.name()), | |||||
newDoc("I4", project.uuid(), file).setCleanCodeAttributeCategory(INTENTIONAL.name()), | |||||
newDoc("I5", project.uuid(), file).setCleanCodeAttributeCategory(INTENTIONAL.name()), | |||||
newDoc("I6", project.uuid(), file).setCleanCodeAttributeCategory(INTENTIONAL.name()), | |||||
newDoc("I7", project.uuid(), file).setCleanCodeAttributeCategory(INTENTIONAL.name()), | |||||
newDoc("I8", project.uuid(), file).setCleanCodeAttributeCategory(RESPONSIBLE.name())); | |||||
assertThatSearchReturnsOnly(IssueQuery.builder().cleanCodeAttributesCategories(Set.of(ADAPTABLE.name())), | |||||
"I1", "I2"); | |||||
assertThatSearchReturnsOnly(IssueQuery.builder().cleanCodeAttributesCategories(Set.of(CONSISTENT.name(), INTENTIONAL.name())), | |||||
"I3", "I4", "I5", "I6", "I7"); | |||||
assertThatSearchReturnsOnly(IssueQuery.builder().cleanCodeAttributesCategories( | |||||
Set.of(CONSISTENT.name(), INTENTIONAL.name(), RESPONSIBLE.name(), ADAPTABLE.name())), | |||||
"I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8"); | |||||
} | |||||
private void indexView(String viewUuid, List<String> projectBranchUuids) { | private void indexView(String viewUuid, List<String> projectBranchUuids) { | ||||
viewIndexer.index(new ViewDoc().setUuid(viewUuid).setProjectBranchUuids(projectBranchUuids)); | viewIndexer.index(new ViewDoc().setUuid(viewUuid).setProjectBranchUuids(projectBranchUuids)); | ||||
} | } |
public static final String PARAM_TYPE = "type"; | public static final String PARAM_TYPE = "type"; | ||||
public static final String PARAM_ISSUES = "issues"; | public static final String PARAM_ISSUES = "issues"; | ||||
public static final String PARAM_SEVERITIES = "severities"; | public static final String PARAM_SEVERITIES = "severities"; | ||||
public static final String PARAM_SOFTWARE_QUALITIES = "softwareQualities"; | |||||
//TODO: To be discussed for the naming | |||||
public static final String PARAM_SOFTWARE_QUALITIES_SEVERTIIES = "softwareQualitiesSeverities"; | |||||
public static final String PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES = "cleanCodeAttributeCategories"; | |||||
public static final String PARAM_STATUSES = "statuses"; | public static final String PARAM_STATUSES = "statuses"; | ||||
public static final String PARAM_RESOLUTIONS = "resolutions"; | public static final String PARAM_RESOLUTIONS = "resolutions"; | ||||
public static final String PARAM_RESOLVED = "resolved"; | public static final String PARAM_RESOLVED = "resolved"; |