import java.util.List;
import java.util.Optional;
import java.util.Set;
-import javax.annotation.Nullable;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import org.sonar.db.Pagination;
public class IssueDao implements Dao {
public static final int DEFAULT_PAGE_SIZE = 1000;
+ public static final int BIG_PAGE_SIZE = 1000000;
public Optional<IssueDto> selectByKey(DbSession session, String key) {
return Optional.ofNullable(mapper(session).selectByKey(key));
}
public Set<String> selectIssueKeysByComponentUuid(DbSession session, String componentUuid) {
- return selectIssueKeysByComponentUuid(session, componentUuid, emptyList(), emptyList(), emptyList(),
- null, false);
+ return mapper(session).selectIssueKeysByComponentUuid(componentUuid);
}
public Set<String> selectIssueKeysByComponentUuid(DbSession session, String componentUuid,
List<String> includingRepositories, List<String> excludingRepositories,
- List<String> languages, @Nullable Boolean resolvedOnly, boolean openIssuesOnly) {
- return mapper(session).selectIssueKeysByComponentUuid(componentUuid, includingRepositories, excludingRepositories,
- languages, resolvedOnly, openIssuesOnly);
+ List<String> languages, int page) {
+ return mapper(session).selectIssueKeysByComponentUuidWithFilters(componentUuid, includingRepositories, excludingRepositories,
+ languages, Pagination.forPage(page).andSize(BIG_PAGE_SIZE));
}
- public Set<String> selectIssueKeysByComponentUuidAndChangedSinceDate(DbSession session, String componentUuid, long changedSince) {
+ public Set<String> selectIssueKeysByComponentUuidAndChangedSinceDate(DbSession session, String componentUuid, long changedSince, int page) {
return selectIssueKeysByComponentUuidAndChangedSinceDate(session, componentUuid, changedSince, emptyList(), emptyList(),
- emptyList(), null);
+ emptyList(), page);
}
public Set<String> selectIssueKeysByComponentUuidAndChangedSinceDate(DbSession session, String componentUuid, long changedSince,
List<String> includingRepositories, List<String> excludingRepositories,
- List<String> languages, @Nullable Boolean resolvedOnly) {
+ List<String> languages, int page) {
return mapper(session).selectIssueKeysByComponentUuidAndChangedSinceDate(componentUuid, changedSince,
- includingRepositories, excludingRepositories, languages, resolvedOnly);
+ includingRepositories, excludingRepositories, languages, Pagination.forPage(page).andSize(BIG_PAGE_SIZE));
}
public List<IssueDto> selectByComponentUuidPaginated(DbSession session, String componentUuid, int page) {
return session.getMapper(IssueMapper.class);
}
- public List<IssueDto> selectByBranch(DbSession dbSession, IssueQueryParams issueQueryParams, int page) {
- Pagination pagination = Pagination.forPage(page).andSize(DEFAULT_PAGE_SIZE);
- return mapper(dbSession).selectByBranch(issueQueryParams, pagination);
+ public List<IssueDto> selectByBranch(DbSession dbSession, Set<String> issueKeysSnapshot, IssueQueryParams issueQueryParams) {
+ return mapper(dbSession).selectByBranch(issueKeysSnapshot, issueQueryParams.getChangedSince());
}
public List<String> selectRecentlyClosedIssues(DbSession dbSession, IssueQueryParams issueQueryParams) {
List<IssueDto> selectByKeys(List<String> keys);
- Set<String> selectIssueKeysByComponentUuid(@Param("componentUuid") String componentUuid,
+ Set<String> selectIssueKeysByComponentUuid(@Param("componentUuid") String componentUuid);
+
+ Set<String> selectIssueKeysByComponentUuidWithFilters(@Param("componentUuid") String componentUuid,
@Param("includingRepositories") List<String> includingRepositories,
@Param("excludingRepositories") List<String> excludingRepositories,
- @Param("languages") List<String> languages, @Param("resolvedOnly") @Nullable Boolean resolvedOnly,
- @Param("openIssuesOnly") boolean openIssuesOnly);
+ @Param("languages") List<String> languages, @Param("pagination") Pagination pagination);
Set<String> selectIssueKeysByComponentUuidAndChangedSinceDate(@Param("componentUuid") String componentUuid,
@Param("changedSince") long changedSince,
@Param("includingRepositories") List<String> includingRepositories,
@Param("excludingRepositories") List<String> excludingRepositories,
- @Param("languages") List<String> languages, @Param("resolvedOnly") @Nullable Boolean resolvedOnly);
+ @Param("languages") List<String> languages, @Param("pagination") Pagination pagination);
List<IssueDto> selectByComponentUuidPaginated(@Param("componentUuid") String componentUuid,
@Param("pagination") Pagination pagination);
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("queryParams") IssueQueryParams issueQueryParams,
- @Param("pagination") Pagination pagination);
List<String> selectRecentlyClosedIssues(@Param("queryParams") IssueQueryParams issueQueryParams);
}
select
i.kee
from issues i
- <if test="includingRepositories != null || excludingRepositories != null || languages != null">
- inner join rules r on i.rule_uuid = r.uuid
- </if>
+ where
+ i.project_uuid=#{componentUuid,jdbcType=VARCHAR}
+ </select>
+
+ <select id="selectIssueKeysByComponentUuidWithFilters" parameterType="string" resultType="string">
+ select
+ i.kee
+ from rules r
+ inner join issues i on i.rule_uuid = r.uuid
where
i.project_uuid=#{componentUuid,jdbcType=VARCHAR}
- <choose>
- <when test="openIssuesOnly == true">
- AND i.issue_type != 4
- AND i.status != 'CLOSED'
- </when>
- <when test="resolvedOnly == true">
- AND i.issue_type != 4
- AND i.status = 'RESOLVED'
- </when>
- </choose>
- <if test="includingRepositories != null and includingRepositories.size() > 0">
+ AND i.status <> 'CLOSED'
+ <if test="includingRepositories.size() > 0">
AND r.plugin_name IN
<foreach item="ruleRepository" index="index" collection="includingRepositories" open="(" separator="," close=")">
#{ruleRepository}
</foreach>
</if>
- <if test="excludingRepositories != null and excludingRepositories.size() > 0">
+ <if test="excludingRepositories.size() > 0">
AND r.plugin_name NOT IN
<foreach item="ruleRepository" index="index" collection="excludingRepositories" open="(" separator="," close=")">
#{ruleRepository}
</foreach>
</if>
- <if test="languages != null and languages.size() > 0">
+ <if test="languages.size() > 0">
AND r.language IN
<foreach item="language" index="index" collection="languages" open="(" separator="," close=")">
#{language}
</foreach>
</if>
+ order by i.kee
+ limit #{pagination.pageSize,jdbcType=INTEGER} offset #{pagination.offset,jdbcType=INTEGER}
+ </select>
+
+ <select id="selectIssueKeysByComponentUuidWithFilters" parameterType="map" resultType="string" databaseId="mssql">
+ select
+ i.kee
+ from
+ (select
+ row_number() over(order by i.kee ASC) as row_number,
+ i.kee
+ from rules r
+ inner join issues i on i.rule_uuid = r.uuid
+ where i.project_uuid=#{componentUuid,jdbcType=VARCHAR}
+ AND i.status <> 'CLOSED'
+ <if test="includingRepositories.size() > 0">
+ AND r.plugin_name IN
+ <foreach item="ruleRepository" index="index" collection="includingRepositories" open="(" separator="," close=")">
+ #{ruleRepository}
+ </foreach>
+ </if>
+ <if test="excludingRepositories.size() > 0">
+ AND r.plugin_name NOT IN
+ <foreach item="ruleRepository" index="index" collection="excludingRepositories" open="(" separator="," close=")">
+ #{ruleRepository}
+ </foreach>
+ </if>
+ <if test="languages.size() > 0">
+ AND r.language IN
+ <foreach item="language" index="index" collection="languages" open="(" separator="," close=")">
+ #{language}
+ </foreach>
+ </if>
+ order by row_number asc
+ offset #{pagination.offset} rows
+ fetch next #{pagination.pageSize,jdbcType=INTEGER} rows only) i
+ </select>
+
+ <select id="selectIssueKeysByComponentUuidWithFilters" parameterType="map" resultType="string" databaseId="oracle">
+ select
+ i.kee
+ from (
+ select rownum as rn, t.* from (
+ select
+ i.kee
+ from rules r
+ inner join issues i on i.rule_uuid = r.uuid
+ where i.project_uuid=#{componentUuid,jdbcType=VARCHAR}
+ AND i.status <> 'CLOSED'
+ <if test="includingRepositories.size() > 0">
+ AND r.plugin_name IN
+ <foreach item="ruleRepository" index="index" collection="includingRepositories" open="(" separator="," close=")">
+ #{ruleRepository}
+ </foreach>
+ </if>
+ <if test="excludingRepositories.size() > 0">
+ AND r.plugin_name NOT IN
+ <foreach item="ruleRepository" index="index" collection="excludingRepositories" open="(" separator="," close=")">
+ #{ruleRepository}
+ </foreach>
+ </if>
+ <if test="languages.size() > 0">
+ AND r.language IN
+ <foreach item="language" index="index" collection="languages" open="(" separator="," close=")">
+ #{language}
+ </foreach>
+ </if>
+ order by i.kee ASC
+ ) t ) i
+ where
+ i.rn between #{pagination.startRowNumber,jdbcType=INTEGER} and #{pagination.endRowNumber,jdbcType=INTEGER}
+ order by i.rn asc
</select>
<select id="selectIssueKeysByComponentUuidAndChangedSinceDate" parameterType="map" resultType="string">
select
i.kee
- from issues i
- <if test="includingRepositories != null || excludingRepositories != null || languages != null">
- inner join rules r on i.rule_uuid = r.uuid
- </if>
+ from rules r
+ inner join issues i on i.rule_uuid = r.uuid
where
i.project_uuid=#{componentUuid,jdbcType=VARCHAR}
AND i.issue_update_date >= #{changedSince,jdbcType=BIGINT}
- AND i.status != 'CLOSED'
- AND i.issue_type != 4
- <if test="resolvedOnly == true">
- AND i.status = 'RESOLVED'
- </if>
- <if test="includingRepositories != null and includingRepositories.size() > 0">
+ AND i.status <> 'CLOSED'
+ <if test="includingRepositories.size() > 0">
AND r.plugin_name IN
<foreach item="ruleRepository" index="index" collection="includingRepositories" open="(" separator="," close=")">
#{ruleRepository}
</foreach>
</if>
- <if test="excludingRepositories != null and excludingRepositories.size() > 0">
+ <if test="excludingRepositories.size() > 0">
AND r.plugin_name NOT IN
<foreach item="ruleRepository" index="index" collection="excludingRepositories" open="(" separator="," close=")">
#{ruleRepository}
</foreach>
</if>
- <if test="languages != null and languages.size() > 0">
+ <if test="languages.size() > 0">
AND r.language IN
<foreach item="language" index="index" collection="languages" open="(" separator="," close=")">
#{language}
</foreach>
</if>
+ order by i.kee
+ limit #{pagination.pageSize,jdbcType=INTEGER} offset #{pagination.offset,jdbcType=INTEGER}
+ </select>
+
+ <select id="selectIssueKeysByComponentUuidAndChangedSinceDate" parameterType="map" resultType="string" databaseId="mssql">
+ select
+ i.kee
+ from
+ (select
+ row_number() over(order by i.kee ASC) as row_number,
+ i.kee
+ from rules r
+ inner join issues i on i.rule_uuid = r.uuid
+ where i.project_uuid=#{componentUuid,jdbcType=VARCHAR}
+ AND i.issue_update_date >= #{changedSince,jdbcType=BIGINT}
+ AND i.status <> 'CLOSED'
+ <if test="includingRepositories.size() > 0">
+ AND r.plugin_name IN
+ <foreach item="ruleRepository" index="index" collection="includingRepositories" open="(" separator="," close=")">
+ #{ruleRepository}
+ </foreach>
+ </if>
+ <if test="excludingRepositories.size() > 0">
+ AND r.plugin_name NOT IN
+ <foreach item="ruleRepository" index="index" collection="excludingRepositories" open="(" separator="," close=")">
+ #{ruleRepository}
+ </foreach>
+ </if>
+ <if test="languages.size() > 0">
+ AND r.language IN
+ <foreach item="language" index="index" collection="languages" open="(" separator="," close=")">
+ #{language}
+ </foreach>
+ </if>
+ order by row_number asc
+ offset #{pagination.offset} rows
+ fetch next #{pagination.pageSize,jdbcType=INTEGER} rows only) i
+ </select>
+
+ <select id="selectIssueKeysByComponentUuidAndChangedSinceDate" parameterType="map" resultType="string" databaseId="oracle">
+ select
+ i.kee
+ from
+ (select rownum as rn, t.* from (
+ select
+ i.kee
+ from rules r
+ inner join issues i on i.rule_uuid = r.uuid
+ where i.project_uuid=#{componentUuid,jdbcType=VARCHAR}
+ AND i.issue_update_date >= #{changedSince,jdbcType=BIGINT}
+ AND i.status <> 'CLOSED'
+ <if test="includingRepositories.size() > 0">
+ AND r.plugin_name IN
+ <foreach item="ruleRepository" index="index" collection="includingRepositories" open="(" separator="," close=")">
+ #{ruleRepository}
+ </foreach>
+ </if>
+ <if test="excludingRepositories.size() > 0">
+ AND r.plugin_name NOT IN
+ <foreach item="ruleRepository" index="index" collection="excludingRepositories" open="(" separator="," close=")">
+ #{ruleRepository}
+ </foreach>
+ </if>
+ <if test="languages.size() > 0">
+ AND r.language IN
+ <foreach item="language" index="index" collection="languages" open="(" separator="," close=")">
+ #{language}
+ </foreach>
+ </if>
+ order by i.kee ASC
+ ) t
+ ) i
+ where
+ i.rn between #{pagination.startRowNumber,jdbcType=INTEGER} and #{pagination.endRowNumber,jdbcType=INTEGER}
+ order by i.rn asc
</select>
<select id="selectByComponentUuidPaginated" parameterType="map" resultType="Issue">
left join new_code_reference_issues n on i.kee = n.issue_key
</select>
-
- <sql id="selectByBranchColumnsFinal">
- result.kee as kee,
- result.ruleUuid as ruleUuid,
- result.createdAt as createdAt,
- result.status as status,
- result.ruleType as ruleType,
- result.ruleRepo as ruleRepo,
- result.ruleKey as ruleKey,
- result.message as message,
- result.severity as severity,
- result.manualSeverity as manualSeverity,
- result.type as type,
- result.locations as locations,
- result.component_uuid,
- c.path as filePath,
- result.assigneeUuid
- </sql>
-
- <sql id="selectByBranchColumnsOuterQuery">
- t.kee as kee,
- t.ruleUuid as ruleUuid,
- t.createdAt as createdAt,
- t.status as status,
- t.ruleType as ruleType,
- t.ruleRepo as ruleRepo,
- t.ruleKey as ruleKey,
- t.message as message,
- t.severity as severity,
- t.manualSeverity as manualSeverity,
- t.type as type,
- t.locations as locations,
- t.component_uuid,
- t.assigneeUuid
- </sql>
-
<sql id="selectByBranchColumns">
i.kee as kee,
i.rule_uuid as ruleUuid,
inner join rules r on r.uuid = i.rule_uuid
inner join components p on p.uuid=i.component_uuid
where
- i.project_uuid = #{queryParams.branchUuid}
- AND i.status <> 'CLOSED'
- AND i.issue_type <> 4
- <if test="queryParams.changedSince != null">
- AND i.issue_update_date >= #{queryParams.changedSince,jdbcType=BIGINT}
+ <if test="keys.size() > 0">
+ i.kee IN
+ <foreach collection="keys" open="(" close=")" item="key" separator=",">
+ #{key,jdbcType=VARCHAR}
+ </foreach>
+ </if>
+ <if test="changedSince != null">
+ AND i.issue_update_date >= #{changedSince,jdbcType=BIGINT}
</if>
- <if test="queryParams.resolvedOnly == true">
- AND i.status = 'RESOLVED'
- </if>
- <if test="queryParams.ruleRepositories.size() > 0">
- AND r.plugin_name IN
- <foreach item="ruleRepository" index="index" collection="queryParams.ruleRepositories" open="(" separator="," close=")">
- #{ruleRepository}
- </foreach>
- </if>
- <if test="queryParams.languages.size() > 0">
- AND r.language IN
- <foreach item="language" index="index" collection="queryParams.languages" open="(" separator="," close=")">
- #{language}
- </foreach>
- </if>
- order by i.kee ASC
- limit #{pagination.pageSize,jdbcType=INTEGER} offset #{pagination.offset,jdbcType=INTEGER}
- </select>
-
- <select id="selectByBranch" parameterType="map" resultType="Issue" databaseId="mssql">
- select
- <include refid="selectByBranchColumnsFinal"/>
- from
- (select
- <include refid="selectByBranchColumnsOuterQuery"/>
- from (
- select
- row_number() over(order by i.kee ASC) as row_number,
- <include refid="selectByBranchColumns"/>
- from issues i
- inner join rules r on r.uuid = i.rule_uuid
- where
- i.project_uuid = #{queryParams.branchUuid}
- AND i.status <> 'CLOSED'
- AND i.issue_type <> 4
- <if test="queryParams.changedSince != null">
- AND i.issue_update_date >= #{queryParams.changedSince,jdbcType=BIGINT}
- </if>
- <if test="queryParams.resolvedOnly == true">
- AND i.status = 'RESOLVED'
- </if>
- <if test="queryParams.ruleRepositories.size() > 0">
- AND r.plugin_name IN
- <foreach item="ruleRepository" index="index" collection="queryParams.ruleRepositories" open="(" separator="," close=")">
- #{ruleRepository}
- </foreach>
- </if>
- <if test="queryParams.languages.size() > 0">
- AND r.language IN
- <foreach item="language" index="index" collection="queryParams.languages" open="(" separator="," close=")">
- #{language}
- </foreach>
- </if>
- order by row_number asc
- offset #{pagination.offset} rows
- fetch next #{pagination.pageSize,jdbcType=INTEGER} rows only
- ) as t) as result
- inner join components c on c.uuid=result.component_uuid
- </select>
-
- <select id="selectByBranch" parameterType="map" resultType="Issue" databaseId="oracle">
- select <include refid="selectByBranchColumnsFinal"/> from
- (select <include refid="selectByBranchColumnsOuterQuery"/> from (
- select rownum as rn, a.* from (
- select
- <include refid="selectByBranchColumns"/>
- from issues i
- inner join rules r on r.uuid = i.rule_uuid
- where
- i.project_uuid = #{queryParams.branchUuid}
- AND i.status <> 'CLOSED'
- AND i.issue_type <> 4
- <if test="queryParams.changedSince != null">
- AND i.issue_update_date >= #{queryParams.changedSince,jdbcType=BIGINT}
- </if>
- <if test="queryParams.resolvedOnly == true">
- AND i.status = 'RESOLVED'
- </if>
- <if test="queryParams.ruleRepositories.size() > 0">
- AND r.plugin_name IN
- <foreach item="ruleRepository" index="index" collection="queryParams.ruleRepositories" open="(" separator="," close=")">
- #{ruleRepository}
- </foreach>
- </if>
- <if test="queryParams.languages.size() > 0">
- AND r.language IN
- <foreach item="language" index="index" collection="queryParams.languages" open="(" separator="," close=")">
- #{language}
- </foreach>
- </if>
- order by i.kee ASC
- ) a
- ) t
- where
- t.rn between #{pagination.startRowNumber,jdbcType=INTEGER} and #{pagination.endRowNumber,jdbcType=INTEGER}
- order by t.rn asc) result
- inner join components c on c.uuid=result.component_uuid
</select>
<select id="selectRecentlyClosedIssues" resultType="string">
select i.kee
- from issues i
- inner join rules r on r.uuid = i.rule_uuid
+ from rules r
+ inner join issues i on r.uuid = i.rule_uuid
where
i.project_uuid = #{queryParams.branchUuid}
AND issue_update_date >= #{queryParams.changedSince}
// Filter by including repositories
Set<String> issues = underTest.selectIssueKeysByComponentUuid(db.getSession(), PROJECT_UUID, List.of("xoo"),
- emptyList(), emptyList(), null, false);
+ emptyList(), emptyList(), 1);
// results are not ordered, so do not use "containsExactly"
- assertThat(issues).containsOnly("I1", "I2", "I3");
+ assertThat(issues).containsOnly("I1", "I3");
// Filter by excluding repositories
issues = underTest.selectIssueKeysByComponentUuid(db.getSession(), PROJECT_UUID, emptyList(), List.of("xoo"),
- emptyList(), null, false);
+ emptyList(), 1);
assertThat(issues).isEmpty();
// Filter by language
- issues = underTest.selectIssueKeysByComponentUuid(db.getSession(), PROJECT_UUID, emptyList(), emptyList(), List.of("xoo"), null, false);
- assertThat(issues).containsOnly("I1", "I2", "I3");
-
- // Filter by resolved only
- issues = underTest.selectIssueKeysByComponentUuid(db.getSession(), PROJECT_UUID, emptyList(), emptyList(), emptyList(), true, false);
- assertThat(issues).containsOnly("I1");
-
- // Filter by non-closed issues only
- issues = underTest.selectIssueKeysByComponentUuid(db.getSession(), PROJECT_UUID, emptyList(), emptyList(), emptyList(), null, true);
+ issues = underTest.selectIssueKeysByComponentUuid(db.getSession(), PROJECT_UUID, emptyList(), emptyList(), List.of("xoo"), 1);
assertThat(issues).containsOnly("I1", "I3");
}
// Insert I3, I4, where t1 < t2
IntStream.range(3, 5).forEach(i -> underTest.insert(db.getSession(), newIssueDto("I" + i).setUpdatedAt(t1)));
- Set<String> issues = underTest.selectIssueKeysByComponentUuidAndChangedSinceDate(db.getSession(), PROJECT_UUID, t2);
+ Set<String> issues = underTest.selectIssueKeysByComponentUuidAndChangedSinceDate(db.getSession(), PROJECT_UUID, t2, 1);
- // results are not ordered, so do not use "containsExactly"
- assertThat(issues).containsOnly("I1");
+ assertThat(issues).contains("I1");
}
@Test
// Filter by including repositories
Set<String> issues = underTest.selectIssueKeysByComponentUuidAndChangedSinceDate(db.getSession(), PROJECT_UUID, t2, List.of("xoo"),
- emptyList(), emptyList(), null);
+ emptyList(), emptyList(), 1);
// results are not ordered, so do not use "containsExactly"
- assertThat(issues).containsOnly("I1");
+ assertThat(issues).contains("I1");
// Filter by excluding repositories
issues = underTest.selectIssueKeysByComponentUuidAndChangedSinceDate(db.getSession(), PROJECT_UUID, t2,
- emptyList(), List.of("xoo"), emptyList(), null);
+ emptyList(), List.of("xoo"), emptyList(), 1);
assertThat(issues).isEmpty();
// Filter by language
issues = underTest.selectIssueKeysByComponentUuidAndChangedSinceDate(db.getSession(), PROJECT_UUID, t2, emptyList(),
- emptyList(), List.of("xoo"), null);
- assertThat(issues).containsOnly("I1");
-
- // Filter by resolved only
- issues = underTest.selectIssueKeysByComponentUuidAndChangedSinceDate(db.getSession(), PROJECT_UUID, t2, emptyList(),
- emptyList(), emptyList(), true);
- assertThat(issues).containsOnly("I1");
+ emptyList(), List.of("xoo"), 1);
+ assertThat(issues).contains("I1");
}
List<String> statusesB = List.of(STATUS_OPEN, STATUS_RESOLVED);
IntStream.range(0, statusesB.size()).forEach(i -> insertBranchIssue(branchB, fileB, rule, "B" + i, statusesB.get(i), updatedAt));
- List<IssueDto> branchAIssuesA1 = underTest.selectByBranch(db.getSession(), buildSelectByBranchQuery(branchA, "java", false, changedSince), 1);
+ List<IssueDto> branchAIssuesA1 = underTest.selectByBranch(db.getSession(), Set.of("issueA0", "issueA1", "issueA3"),
+ buildSelectByBranchQuery(branchA, "java", false, changedSince));
assertThat(branchAIssuesA1)
.extracting(IssueDto::getKey, IssueDto::getStatus)
tuple("issueA3", STATUS_RESOLVED)
);
- List<IssueDto> branchAIssuesA2 = underTest.selectByBranch(db.getSession(), buildSelectByBranchQuery(branchA, "java", true, changedSince), 1);
+ List<IssueDto> branchAIssuesA2 = underTest.selectByBranch(db.getSession(), Set.of("issueA0", "issueA1", "issueA3"),
+ buildSelectByBranchQuery(branchA, "java", true, changedSince));
assertThat(branchAIssuesA2)
.extracting(IssueDto::getKey, IssueDto::getStatus)
- .containsExactly(tuple("issueA3", STATUS_RESOLVED));
+ .containsExactly(tuple("issueA0", STATUS_OPEN),
+ tuple("issueA1", STATUS_REVIEWED),
+ tuple("issueA3", STATUS_RESOLVED));
- List<IssueDto> branchBIssuesB1 = underTest.selectByBranch(db.getSession(), buildSelectByBranchQuery(branchB, "java", false, changedSince), 1);
+ List<IssueDto> branchBIssuesB1 = underTest.selectByBranch(db.getSession(), Set.of("issueB0", "issueB1"), buildSelectByBranchQuery(branchB, "java", false, changedSince));
assertThat(branchBIssuesB1)
.extracting(IssueDto::getKey, IssueDto::getStatus)
tuple("issueB1", STATUS_RESOLVED)
);
- List<IssueDto> branchBIssuesB2 = underTest.selectByBranch(db.getSession(), buildSelectByBranchQuery(branchB, "java", true, changedSince), 1);
+ List<IssueDto> branchBIssuesB2 = underTest.selectByBranch(db.getSession(), Set.of("issueB0", "issueB1"), buildSelectByBranchQuery(branchB, "java", true, changedSince));
assertThat(branchBIssuesB2)
.extracting(IssueDto::getKey, IssueDto::getStatus)
- .containsExactly(tuple("issueB1", STATUS_RESOLVED));
+ .containsExactly(tuple("issueB0", STATUS_OPEN),
+ tuple("issueB1", STATUS_RESOLVED));
}
@Test
import static org.sonar.api.web.UserRole.USER;
public abstract class BasePullAction implements IssuesWsAction {
-
protected static final String PROJECT_KEY_PARAM = "projectKey";
protected static final String BRANCH_NAME_PARAM = "branchName";
protected static final String LANGUAGES_PARAM = "languages";
var issuesRetriever = new PullActionIssuesRetriever(dbClient, queryParams);
- Set<String> issueKeysSnapshot = new HashSet<>(getIssueKeysSnapshot(queryParams));
- Consumer<List<IssueDto>> listConsumer = issueDtos -> pullActionResponseWriter.appendIssuesToResponse(issueDtos, outputStream);
- issuesRetriever.processIssuesByBatch(dbSession, issueKeysSnapshot, listConsumer);
+ processNonClosedIssuesInBatches(dbSession, queryParams, outputStream, issuesRetriever);
if (queryParams.getChangedSince() != null) {
// in the "incremental mode" we need to send SonarLint also recently closed issues keys
protected abstract IssueQueryParams initializeQueryParams(BranchDto branchDto, @Nullable List<String> languages,
@Nullable List<String> ruleRepositories, boolean resolvedOnly, @Nullable Long changedSince);
- protected abstract Set<String> getIssueKeysSnapshot(IssueQueryParams queryParams);
+ protected abstract Set<String> getIssueKeysSnapshot(IssueQueryParams queryParams, int page);
+
+ private void processNonClosedIssuesInBatches(DbSession dbSession, IssueQueryParams queryParams, OutputStream outputStream,
+ PullActionIssuesRetriever issuesRetriever) {
+ int nextPage = 1;
+ do {
+ Set<String> issueKeysSnapshot = new HashSet<>(getIssueKeysSnapshot(queryParams, nextPage));
+ Consumer<List<IssueDto>> listConsumer = issueDtos -> pullActionResponseWriter.appendIssuesToResponse(issueDtos, outputStream);
+ issuesRetriever.processIssuesByBatch(dbSession, issueKeysSnapshot, listConsumer);
+ if (issueKeysSnapshot.isEmpty()) {
+ nextPage = -1;
+ } else {
+ nextPage++;
+ }
+ } while (nextPage > 0);
+ }
}
}
@Override
- protected Set<String> getIssueKeysSnapshot(IssueQueryParams issueQueryParams) {
+ protected Set<String> getIssueKeysSnapshot(IssueQueryParams issueQueryParams, int page) {
try (DbSession dbSession = dbClient.openSession(false)) {
Optional<Long> changedSinceDate = ofNullable(issueQueryParams.getChangedSince());
if (changedSinceDate.isPresent()) {
return dbClient.issueDao().selectIssueKeysByComponentUuidAndChangedSinceDate(dbSession, issueQueryParams.getBranchUuid(),
changedSinceDate.get(), issueQueryParams.getRuleRepositories(), taintChecker.getTaintRepositories(),
- issueQueryParams.getLanguages(), issueQueryParams.isResolvedOnly());
+ issueQueryParams.getLanguages(), page);
}
return dbClient.issueDao().selectIssueKeysByComponentUuid(dbSession, issueQueryParams.getBranchUuid(),
issueQueryParams.getRuleRepositories(), taintChecker.getTaintRepositories(),
- issueQueryParams.getLanguages(), issueQueryParams.isResolvedOnly(), true);
+ issueQueryParams.getLanguages(), page);
}
}
return new IssueQueryParams(branchDto.getUuid(), languages, ruleRepositories, taintChecker.getTaintRepositories(), resolvedOnly, changedSince);
}
-
@Override
protected void validateRuleRepositories(List<String> ruleRepositories) {
checkArgument(ruleRepositories
.stream()
.filter(taintChecker.getTaintRepositories()::contains)
.count() == 0, "Incorrect rule repositories list: it should only include repositories that define Issues, and no Taint Vulnerabilities");
-
}
}
}
@Override
- protected Set<String> getIssueKeysSnapshot(IssueQueryParams issueQueryParams) {
+ protected Set<String> getIssueKeysSnapshot(IssueQueryParams issueQueryParams, int page) {
Optional<Long> changedSinceDate = ofNullable(issueQueryParams.getChangedSince());
try (DbSession dbSession = dbClient.openSession(false)) {
if (changedSinceDate.isPresent()) {
return dbClient.issueDao().selectIssueKeysByComponentUuidAndChangedSinceDate(dbSession, issueQueryParams.getBranchUuid(),
changedSinceDate.get(), issueQueryParams.getRuleRepositories(), emptyList(),
- issueQueryParams.getLanguages(), false);
+ issueQueryParams.getLanguages(), page);
}
return dbClient.issueDao().selectIssueKeysByComponentUuid(dbSession, issueQueryParams.getBranchUuid(),
issueQueryParams.getRuleRepositories(),
- emptyList(), issueQueryParams.getLanguages(),
- issueQueryParams.isResolvedOnly(), true);
+ emptyList(), issueQueryParams.getLanguages(),page);
}
}
@Override
protected IssueQueryParams initializeQueryParams(BranchDto branchDto, @Nullable List<String> languages,
@Nullable List<String> ruleRepositories, boolean resolvedOnly, @Nullable Long changedSince) {
- return new IssueQueryParams(branchDto.getUuid(), languages, taintChecker.getTaintRepositories(), emptyList(), resolvedOnly, changedSince);
+ return new IssueQueryParams(branchDto.getUuid(), languages, taintChecker.getTaintRepositories(), emptyList(), false, changedSince);
}
@Override
*/
package org.sonar.server.issue.ws.pull;
+import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.issue.IssueQueryParams;
+import static java.util.stream.Collectors.toList;
import static org.sonar.db.issue.IssueDao.DEFAULT_PAGE_SIZE;
public class PullActionIssuesRetriever {
}
public void processIssuesByBatch(DbSession dbSession, Set<String> issueKeysSnapshot, Consumer<List<IssueDto>> listConsumer) {
- int nextPage = 1;
- boolean hasMoreIssues = true;
+ boolean hasMoreIssues = !issueKeysSnapshot.isEmpty();
+ long offset = 0;
+
+ List<IssueDto> issueDtos = new ArrayList<>();
while (hasMoreIssues) {
- List<IssueDto> issueDtos = nextOpenIssues(dbSession, nextPage);
- listConsumer.accept(filterDuplicateIssues(issueDtos, issueKeysSnapshot));
- nextPage++;
- if (issueDtos.isEmpty() || issueDtos.size() < DEFAULT_PAGE_SIZE) {
- hasMoreIssues = false;
- }
+ Set<String> page = paginate(issueKeysSnapshot, offset);
+ issueDtos.addAll(filterIssues(nextOpenIssues(dbSession, page)));
+ offset += page.size();
+ hasMoreIssues = offset < issueKeysSnapshot.size();
}
+
+ listConsumer.accept(issueDtos);
}
- private static List<IssueDto> filterDuplicateIssues(List<IssueDto> issues, Set<String> issueKeysSnapshot) {
+ private List<IssueDto> filterIssues(List<IssueDto> issues) {
return issues
.stream()
- .filter(issue -> isUniqueIssue(issue.getKee(), issueKeysSnapshot))
- .collect(Collectors.toList());
+ .filter(i -> hasCorrectTypeAndStatus(i, issueQueryParams))
+ .collect(toList());
}
- private static boolean isUniqueIssue(String issueKey, Set<String> issueKeysSnapshot) {
- boolean isUniqueIssue = issueKeysSnapshot.contains(issueKey);
-
- if (isUniqueIssue) {
- issueKeysSnapshot.remove(issueKey);
- }
-
- return isUniqueIssue;
+ private static boolean hasCorrectTypeAndStatus(IssueDto issueDto, IssueQueryParams queryParams) {
+ return issueDto.getType() != 4 &&
+ (queryParams.isResolvedOnly() ? issueDto.getStatus().equals("RESOLVED") : true);
}
public List<String> retrieveClosedIssues(DbSession dbSession) {
return dbClient.issueDao().selectRecentlyClosedIssues(dbSession, issueQueryParams);
}
- private List<IssueDto> nextOpenIssues(DbSession dbSession, int nextPage) {
- return dbClient.issueDao().selectByBranch(dbSession, issueQueryParams, nextPage);
+ private List<IssueDto> nextOpenIssues(DbSession dbSession, Set<String> issueKeysSnapshot) {
+ return dbClient.issueDao().selectByBranch(dbSession, issueKeysSnapshot, issueQueryParams);
+ }
+
+ private static Set<String> paginate(Set<String> issueKeys, long offset) {
+ return issueKeys
+ .stream()
+ .skip(offset)
+ .limit(DEFAULT_PAGE_SIZE)
+ .collect(Collectors.toSet());
}
}
import java.util.stream.IntStream;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.sonar.db.DbClient;
import org.sonar.db.issue.IssueDao;
import org.sonar.db.issue.IssueDto;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class PullActionIssuesRetrieverTest {
private final List<String> ruleRepositories = List.of("js-security", "java");
private final Long defaultChangedSince = 1_000_000L;
- private final IssueQueryParams queryParams = new IssueQueryParams(branchUuid, languages, ruleRepositories, null, false, defaultChangedSince);
+ private final IssueQueryParams queryParams = new IssueQueryParams(branchUuid, languages, ruleRepositories, null,
+ false, defaultChangedSince);
private final IssueDao issueDao = mock(IssueDao.class);
@Before
@Test
public void processIssuesByBatch_givenNoIssuesReturnedByDatabase_noIssuesConsumed() {
var pullActionIssuesRetriever = new PullActionIssuesRetriever(dbClient, queryParams);
- when(issueDao.selectByBranch(any(), any(), anyInt()))
+ when(issueDao.selectByBranch(any(), any(), any()))
.thenReturn(List.of());
List<IssueDto> returnedDtos = new ArrayList<>();
Consumer<List<IssueDto>> listConsumer = returnedDtos::addAll;
var pullActionIssuesRetriever = new PullActionIssuesRetriever(dbClient, queryParams);
List<IssueDto> thousandIssues = IntStream.rangeClosed(1, 1000).mapToObj(i -> new IssueDto().setKee(Integer.toString(i))).collect(Collectors.toList());
IssueDto singleIssue = new IssueDto().setKee("kee");
- when(issueDao.selectByBranch(any(), any(), anyInt()))
+ when(issueDao.selectByBranch(any(), any(), any()))
.thenReturn(thousandIssues)
.thenReturn(List.of(singleIssue));
List<IssueDto> returnedDtos = new ArrayList<>();
thousandIssueUuidsSnapshot.add(singleIssue.getKee());
pullActionIssuesRetriever.processIssuesByBatch(dbClient.openSession(true), thousandIssueUuidsSnapshot, listConsumer);
- assertThat(returnedDtos).hasSize(1001);
- }
+ ArgumentCaptor<Set<String>> uuidsCaptor = ArgumentCaptor.forClass(Set.class);
+ verify(issueDao, times(2)).selectByBranch(any(), uuidsCaptor.capture(), any());
+ List<Set<String>> capturedSets = uuidsCaptor.getAllValues();
+ assertThat(capturedSets.get(0)).hasSize(1000);
+ assertThat(capturedSets.get(1)).hasSize(1);
- @Test
- public void processIssuesByBatch_filter_out_duplicate_issue_entries() {
- var pullActionIssuesRetriever = new PullActionIssuesRetriever(dbClient, queryParams);
- IssueDto issue1 = new IssueDto().setKee("kee1");
- IssueDto issue2 = new IssueDto().setKee("kee2");
- List<IssueDto> issues = List.of(issue1, issue1, issue1, issue2);
- when(issueDao.selectByBranch(any(), any(), anyInt()))
- .thenReturn(issues);
- List<IssueDto> returnedDtos = new ArrayList<>();
- Consumer<List<IssueDto>> listConsumer = returnedDtos::addAll;
-
- Set<String> thousandIssueKeysSnapshot = issues.stream().map(IssueDto::getKee).collect(Collectors.toSet());
- pullActionIssuesRetriever.processIssuesByBatch(dbClient.openSession(true), thousandIssueKeysSnapshot, listConsumer);
-
- assertThat(returnedDtos)
- .hasSize(2)
- .containsExactlyInAnyOrder(issue1, issue2);
+ assertThat(returnedDtos).hasSize(1001);
}
@Test
public void processIssuesByBatch_correctly_processes_all_issues_regardless_of_creation_timestamp() {
var pullActionIssuesRetriever = new PullActionIssuesRetriever(dbClient, queryParams);
- List<IssueDto> issuesWithSameCreationTimestamp = IntStream.rangeClosed(1, 100).mapToObj(i -> new IssueDto().setKee(Integer.toString(i)).setCreatedAt(100L)).collect(Collectors.toList());
- when(issueDao.selectByBranch(any(), any(), anyInt()))
+ List<IssueDto> issuesWithSameCreationTimestamp = IntStream.rangeClosed(1, 100).mapToObj(i -> new IssueDto()
+ .setKee(Integer.toString(i)).setCreatedAt(100L)).collect(Collectors.toList());
+ when(issueDao.selectByBranch(any(), any(), any()))
.thenReturn(issuesWithSameCreationTimestamp);
List<IssueDto> returnedDtos = new ArrayList<>();
Consumer<List<IssueDto>> listConsumer = returnedDtos::addAll;