aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2020-04-13 15:37:35 -0500
committersonartech <sonartech@sonarsource.com>2020-04-17 20:03:43 +0000
commitb922a7648b5a6d4a26a659900ceb8644cbc5c10b (patch)
treea8d5088c04085eeff2a1776d560c82d5af0e14e9
parent2ba726630087bb0baae61e05e1c20943ec9e6931 (diff)
downloadsonarqube-b922a7648b5a6d4a26a659900ceb8644cbc5c10b.tar.gz
sonarqube-b922a7648b5a6d4a26a659900ceb8644cbc5c10b.zip
SONAR-13196 Search issues since leak period returns all issues if no leak period exists
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java2
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java21
-rw-r--r--server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java47
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java14
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java2
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java80
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java34
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java135
-rw-r--r--server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/filter_by_leak_period.json51
-rw-r--r--server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/filter_by_leak_period_has_no_effect_on_prs.json69
10 files changed, 388 insertions, 67 deletions
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
index d9b1ecadc96..58c00a5fa8d 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
@@ -638,7 +638,7 @@ public class IssueIndex {
}
private void validateCreationDateBounds(@Nullable Date createdBefore, @Nullable Date createdAfter) {
- Preconditions.checkArgument(createdAfter == null || createdAfter.before(new Date(system.now())),
+ Preconditions.checkArgument(createdAfter == null || createdAfter.compareTo(new Date(system.now())) <= 0,
"Start bound cannot be in the future");
Preconditions.checkArgument(createdAfter == null || createdBefore == null || createdAfter.before(createdBefore),
"Start bound cannot be larger or equal to end bound");
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
index 7dd6221826f..d6c7f69d8c3 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
@@ -95,7 +95,7 @@ public class IssueQueryFactory {
.map(Enum::name)
.collect(MoreCollectors.toSet(RuleType.values().length - 1));
private static final ComponentDto UNKNOWN_COMPONENT = new ComponentDto().setUuid(UNKNOWN).setProjectUuid(UNKNOWN);
-
+ private static final Set<String> QUALIFIERS_WITHOUT_LEAK_PERIOD = new HashSet<>(Arrays.asList(Qualifiers.APP, Qualifiers.VIEW, Qualifiers.SUBVIEW));
private final DbClient dbClient;
private final Clock clock;
private final UserSession userSession;
@@ -145,8 +145,6 @@ public class IssueQueryFactory {
}
private void setCreatedAfterFromDates(IssueQuery.Builder builder, @Nullable Date createdAfter, @Nullable String createdInLast, boolean createdAfterInclusive) {
- checkArgument(createdAfter == null || createdInLast == null, format("Parameters %s and %s cannot be set simultaneously", PARAM_CREATED_AFTER, PARAM_CREATED_IN_LAST));
-
Date actualCreatedAfter = createdAfter;
if (createdInLast != null) {
actualCreatedAfter = Date.from(
@@ -171,20 +169,26 @@ public class IssueQueryFactory {
String createdInLast = request.getCreatedInLast();
if (request.getSinceLeakPeriod() == null || !request.getSinceLeakPeriod()) {
+ checkArgument(createdAfter == null || createdInLast == null, format("Parameters %s and %s cannot be set simultaneously", PARAM_CREATED_AFTER, PARAM_CREATED_IN_LAST));
setCreatedAfterFromDates(builder, createdAfter, createdInLast, true);
} else {
checkArgument(createdAfter == null, "Parameters '%s' and '%s' cannot be set simultaneously", PARAM_CREATED_AFTER, PARAM_SINCE_LEAK_PERIOD);
+ checkArgument(createdInLast == null, format("Parameters %s and %s cannot be set simultaneously", PARAM_CREATED_IN_LAST, PARAM_SINCE_LEAK_PERIOD));
+
checkArgument(componentUuids.size() == 1, "One and only one component must be provided when searching since leak period");
ComponentDto component = componentUuids.iterator().next();
- Date createdAfterFromSnapshot = findCreatedAfterFromComponentUuid(dbSession, component);
- setCreatedAfterFromDates(builder, createdAfterFromSnapshot, createdInLast, false);
+
+ if (!QUALIFIERS_WITHOUT_LEAK_PERIOD.contains(component.qualifier()) && request.getPullRequest() == null) {
+ Date createdAfterFromSnapshot = findCreatedAfterFromComponentUuid(dbSession, component);
+ setCreatedAfterFromDates(builder, createdAfterFromSnapshot, null, false);
+ }
}
}
- @CheckForNull
private Date findCreatedAfterFromComponentUuid(DbSession dbSession, ComponentDto component) {
Optional<SnapshotDto> snapshot = dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, component.uuid());
- return snapshot.map(s -> longToDate(s.getPeriodDate())).orElse(null);
+ // if last analysis has no period date, then no issue should be considered new.
+ return snapshot.map(s -> longToDate(s.getPeriodDate())).orElseGet(() -> new Date(clock.millis()));
}
private boolean mergeDeprecatedComponentParameters(DbSession session, SearchRequest request, List<ComponentDto> allComponents) {
@@ -319,7 +323,7 @@ public class IssueQueryFactory {
}
private void addCreatedAfterByProjects(IssueQuery.Builder builder, DbSession dbSession, SearchRequest request, Set<String> applicationUuids) {
- if (request.getSinceLeakPeriod() == null || !request.getSinceLeakPeriod()) {
+ if (request.getSinceLeakPeriod() == null || !request.getSinceLeakPeriod() || request.getPullRequest() != null) {
return;
}
@@ -331,6 +335,7 @@ public class IssueQueryFactory {
.stream()
.filter(s -> s.getPeriodDate() != null)
.collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> new PeriodStart(longToDate(s.getPeriodDate()), false)));
+
builder.createdAfterByProjectUuids(leakByProjects);
}
diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java
index 050eb283ae7..c86fb938571 100644
--- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java
+++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueQueryFactoryTest.java
@@ -24,6 +24,7 @@ import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
+import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -43,6 +44,7 @@ import org.sonar.server.tester.UserSessionRule;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -276,6 +278,7 @@ public class IssueQueryFactoryTest {
@Test
public void application_search_project_issues_on_leak() {
Date now = new Date();
+ when(clock.millis()).thenReturn(now.getTime());
ComponentDto project1 = db.components().insertPublicProject();
SnapshotDto analysis1 = db.components().insertSnapshot(project1, s -> s.setPeriodDate(addDays(now, -14).getTime()));
ComponentDto project2 = db.components().insertPublicProject();
@@ -292,8 +295,8 @@ public class IssueQueryFactoryTest {
.setSinceLeakPeriod(true));
assertThat(result.createdAfterByProjectUuids()).hasSize(1);
- assertThat(result.createdAfterByProjectUuids().get(project1.uuid()).date().getTime()).isEqualTo(analysis1.getPeriodDate());
- assertThat(result.createdAfterByProjectUuids().get(project1.uuid()).inclusive()).isFalse();
+ assertThat(result.createdAfterByProjectUuids().entrySet()).extracting(Map.Entry::getKey, e -> e.getValue().date(), e -> e.getValue().inclusive()).containsOnly(
+ tuple(project1.uuid(), new Date(analysis1.getPeriodDate()), false));
assertThat(result.viewUuids()).containsExactlyInAnyOrder(application.uuid());
}
@@ -392,14 +395,14 @@ public class IssueQueryFactoryTest {
assertThat(underTest.create(new SearchRequest()
.setProjectKeys(singletonList(branch.getKey()))
.setBranch(branch.getBranch())))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
- .containsOnly(branch.uuid(), singletonList(project.uuid()), false);
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
+ .containsOnly(branch.uuid(), singletonList(project.uuid()), false);
assertThat(underTest.create(new SearchRequest()
.setComponentKeys(singletonList(branch.getKey()))
.setBranch(branch.getBranch())))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
- .containsOnly(branch.uuid(), singletonList(project.uuid()), false);
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
+ .containsOnly(branch.uuid(), singletonList(project.uuid()), false);
}
@Test
@@ -411,22 +414,22 @@ public class IssueQueryFactoryTest {
assertThat(underTest.create(new SearchRequest()
.setComponentKeys(singletonList(file.getKey()))
.setBranch(branch.getBranch())))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.fileUuids()), IssueQuery::isMainBranch)
- .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.fileUuids()), IssueQuery::isMainBranch)
+ .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
assertThat(underTest.create(new SearchRequest()
.setComponentKeys(singletonList(branch.getKey()))
.setFileUuids(singletonList(file.uuid()))
.setBranch(branch.getBranch())))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.fileUuids()), IssueQuery::isMainBranch)
- .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.fileUuids()), IssueQuery::isMainBranch)
+ .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
assertThat(underTest.create(new SearchRequest()
.setProjectKeys(singletonList(branch.getKey()))
.setFileUuids(singletonList(file.uuid()))
.setBranch(branch.getBranch())))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.fileUuids()), IssueQuery::isMainBranch)
- .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.fileUuids()), IssueQuery::isMainBranch)
+ .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
}
@Test
@@ -439,8 +442,8 @@ public class IssueQueryFactoryTest {
.setComponentKeys(singletonList(file.getKey()))
.setBranch(branch.getBranch())
.setOnComponentOnly(true)))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.componentUuids()), IssueQuery::isMainBranch)
- .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.componentUuids()), IssueQuery::isMainBranch)
+ .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
}
@Test
@@ -451,13 +454,13 @@ public class IssueQueryFactoryTest {
assertThat(underTest.create(new SearchRequest()
.setProjectKeys(singletonList(project.getKey()))
.setBranch("master")))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
- .containsOnly(project.uuid(), singletonList(project.uuid()), true);
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
+ .containsOnly(project.uuid(), singletonList(project.uuid()), true);
assertThat(underTest.create(new SearchRequest()
.setComponentKeys(singletonList(project.getKey()))
.setBranch("master")))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
- .containsOnly(project.uuid(), singletonList(project.uuid()), true);
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
+ .containsOnly(project.uuid(), singletonList(project.uuid()), true);
}
@Test
@@ -492,16 +495,16 @@ public class IssueQueryFactoryTest {
assertThat(underTest.create(new SearchRequest()
.setComponentKeys(singletonList(applicationBranch1.getKey()))
.setBranch(applicationBranch1.getBranch())))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
- .containsOnly(applicationBranch1.uuid(), Collections.emptyList(), false);
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
+ .containsOnly(applicationBranch1.uuid(), Collections.emptyList(), false);
// Search on project1Branch1
assertThat(underTest.create(new SearchRequest()
.setComponentKeys(singletonList(applicationBranch1.getKey()))
.setProjectKeys(singletonList(project1.getKey()))
.setBranch(applicationBranch1.getBranch())))
- .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
- .containsOnly(applicationBranch1.uuid(), singletonList(project1.uuid()), false);
+ .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
+ .containsOnly(applicationBranch1.uuid(), singletonList(project1.uuid()), false);
}
@Test
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java
index 3676f4abfa4..1a18e72dcde 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java
@@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableSet;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -44,6 +45,7 @@ import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.Paging;
+import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
@@ -101,13 +103,14 @@ public class SearchAction implements HotspotsWsAction {
private final UserSession userSession;
private final IssueIndex issueIndex;
private final HotspotWsResponseFormatter responseFormatter;
+ private System2 system2;
- public SearchAction(DbClient dbClient, UserSession userSession, IssueIndex issueIndex,
- HotspotWsResponseFormatter responseFormatter) {
+ public SearchAction(DbClient dbClient, UserSession userSession, IssueIndex issueIndex, HotspotWsResponseFormatter responseFormatter, System2 system2) {
this.dbClient = dbClient;
this.userSession = userSession;
this.issueIndex = issueIndex;
this.responseFormatter = responseFormatter;
+ this.system2 = system2;
}
@Override
@@ -292,10 +295,11 @@ public class SearchAction implements HotspotsWsAction {
builder.mainBranch(false);
}
- if (wsRequest.isSinceLeakPeriod()) {
- dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, p.uuid())
+ if (wsRequest.isSinceLeakPeriod() && !wsRequest.getPullRequest().isPresent()) {
+ Date sinceDate = dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, p.uuid())
.map(s -> longToDate(s.getPeriodDate()))
- .ifPresent(d -> builder.createdAfter(d, false));
+ .orElseGet(() -> new Date(system2.now()));
+ builder.createdAfter(sinceDate, false);
}
});
if (!hotspotKeys.isEmpty()) {
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
index 83a3ed8dc03..36e261558a8 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
@@ -307,7 +307,7 @@ public class SearchAction implements IssuesWsAction {
.setExampleValue("1m2w (1 month 2 weeks)");
action.createParam(PARAM_SINCE_LEAK_PERIOD)
.setDescription("To retrieve issues created since the leak period.<br>" +
- "If this parameter is set to a truthy value, createdAfter must not be set and one component id or key must be provided.")
+ "If this parameter is set to a truthy value, createdAfter must not be set and one component uuid or key must be provided.")
.setBooleanPossibleValues()
.setDefaultValue("false");
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java
index dfa747882ce..c4f2c6a6cc5 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java
@@ -40,6 +40,7 @@ import javax.annotation.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.sonar.api.impl.utils.TestSystem2;
import org.sonar.api.issue.Issue;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.System2;
@@ -50,7 +51,6 @@ import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.issue.IssueDto;
-import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.rule.RuleTesting;
import org.sonar.server.es.EsTester;
@@ -104,6 +104,7 @@ public class SearchActionTest {
@Rule
public UserSessionRule userSessionRule = UserSessionRule.standalone();
+ private TestSystem2 system2 = new TestSystem2();
private DbClient dbClient = dbTester.getDbClient();
private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(dbTester);
@@ -113,7 +114,7 @@ public class SearchActionTest {
private PermissionIndexer permissionIndexer = new PermissionIndexer(dbClient, es.client(), issueIndexer);
private HotspotWsResponseFormatter responseFormatter = new HotspotWsResponseFormatter(defaultOrganizationProvider);
- private SearchAction underTest = new SearchAction(dbClient, userSessionRule, issueIndex, responseFormatter);
+ private SearchAction underTest = new SearchAction(dbClient, userSessionRule, issueIndex, responseFormatter, system2);
private WsActionTester actionTester = new WsActionTester(underTest);
@Test
@@ -724,8 +725,8 @@ public class SearchActionTest {
.setParam("hotspots", IntStream.range(2, 10).mapToObj(String::valueOf).collect(joining(",")))
.setParam("onlyMine", "true")
.execute())
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Parameter 'onlyMine' can be used with parameter 'projectKey' only");
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Parameter 'onlyMine' can be used with parameter 'projectKey' only");
}
@Test
@@ -738,8 +739,8 @@ public class SearchActionTest {
.setParam("projectKey", project.getKey())
.setParam("onlyMine", "true")
.execute())
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Parameter 'onlyMine' requires user to be logged in");
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Parameter 'onlyMine' requires user to be logged in");
}
@Test
@@ -1340,7 +1341,7 @@ public class SearchActionTest {
SearchWsResponse responseOnLeak = newRequest(project,
t -> t.setParam("sinceLeakPeriod", "true"))
- .executeProtobuf(SearchWsResponse.class);
+ .executeProtobuf(SearchWsResponse.class);
assertThat(responseOnLeak.getHotspotsList())
.extracting(SearchWsResponse.Hotspot::getKey)
.containsExactlyInAnyOrder(Stream.concat(
@@ -1351,6 +1352,69 @@ public class SearchActionTest {
}
@Test
+ public void returns_nothing_when_sinceLeakPeriod_is_true_and_no_period_exists() {
+ long referenceDate = 800_996_999_332L;
+
+ system2.setNow(referenceDate + 10_000);
+ ComponentDto project = dbTester.components().insertPublicProject();
+ userSessionRule.registerComponents(project);
+ indexPermissions();
+ ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
+ dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(referenceDate).setLast(false));
+ dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(null).setLast(true));
+ RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
+ IssueDto afterRef = dbTester.issues().insertHotspot(rule, project, file, t -> t.setIssueCreationTime(referenceDate + 1000));
+ IssueDto atRef = dbTester.issues().insertHotspot(rule, project, file, t -> t.setType(SECURITY_HOTSPOT).setIssueCreationTime(referenceDate));
+ IssueDto beforeRef = dbTester.issues().insertHotspot(rule, project, file, t -> t.setIssueCreationTime(referenceDate - 1000));
+ indexIssues();
+
+ SearchWsResponse responseAll = newRequest(project)
+ .executeProtobuf(SearchWsResponse.class);
+ assertThat(responseAll.getHotspotsList())
+ .extracting(SearchWsResponse.Hotspot::getKey)
+ .containsExactlyInAnyOrder(Stream.of(afterRef, atRef, beforeRef)
+ .map(IssueDto::getKey)
+ .toArray(String[]::new));
+
+ SearchWsResponse responseOnLeak = newRequest(project,
+ t -> t.setParam("sinceLeakPeriod", "true"))
+ .executeProtobuf(SearchWsResponse.class);
+ assertThat(responseOnLeak.getHotspotsList()).isEmpty();
+ }
+
+ @Test
+ public void returnsall_issues_when_sinceLeakPeriod_is_true_and_is_pr() {
+ long referenceDate = 800_996_999_332L;
+
+ system2.setNow(referenceDate + 10_000);
+ ComponentDto project = dbTester.components().insertPublicProject();
+ ComponentDto pr = dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST).setKey("pr"));
+ userSessionRule.registerComponents(project);
+ indexPermissions();
+ ComponentDto file = dbTester.components().insertComponent(newFileDto(pr));
+ dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(referenceDate).setLast(true));
+ dbTester.components().insertSnapshot(pr, t -> t.setPeriodDate(null).setLast(true));
+ RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
+ IssueDto afterRef = dbTester.issues().insertHotspot(rule, pr, file, t -> t.setIssueCreationTime(referenceDate + 1000));
+ IssueDto atRef = dbTester.issues().insertHotspot(rule, pr, file, t -> t.setType(SECURITY_HOTSPOT).setIssueCreationTime(referenceDate));
+ IssueDto beforeRef = dbTester.issues().insertHotspot(rule, pr, file, t -> t.setIssueCreationTime(referenceDate - 1000));
+ indexIssues();
+
+ SearchWsResponse responseAll = newRequest(project).setParam("pullRequest", "pr")
+ .executeProtobuf(SearchWsResponse.class);
+ assertThat(responseAll.getHotspotsList())
+ .extracting(SearchWsResponse.Hotspot::getKey)
+ .containsExactlyInAnyOrder(Stream.of(afterRef, atRef, beforeRef)
+ .map(IssueDto::getKey)
+ .toArray(String[]::new));
+
+ SearchWsResponse responseOnLeak = newRequest(project,
+ t -> t.setParam("sinceLeakPeriod", "true").setParam("pullRequest", "pr"))
+ .executeProtobuf(SearchWsResponse.class);
+ assertThat(responseOnLeak.getHotspotsList()).hasSize(3);
+ }
+
+ @Test
public void verify_response_example() {
ComponentDto project = dbTester.components().insertPublicProject(componentDto -> componentDto
.setName("test-project")
@@ -1375,7 +1439,7 @@ public class SearchActionTest {
return insertHotspot(rule, project, fileWithHotspot, issueDto -> issueDto.setKee("hotspot-" + i)
.setAssigneeUuid("assignee-uuid")
.setAuthorLogin("joe")
- .setMessage("message-" +i)
+ .setMessage("message-" + i)
.setLine(10 + i)
.setIssueCreationTime(time)
.setIssueUpdateTime(time)
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java
index e61776b8b6a..1d6314dcb93 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java
@@ -168,12 +168,12 @@ public class SearchActionComponentsTest {
assertThat(ws.newRequest()
.setParam(PARAM_COMPONENT_KEYS, module1.getKey())
.executeProtobuf(SearchWsResponse.class).getIssuesList()).extracting(Issue::getKey)
- .containsExactlyInAnyOrder(issue1.getKey());
+ .containsExactlyInAnyOrder(issue1.getKey());
assertThat(ws.newRequest()
.setParam(PARAM_MODULE_UUIDS, module1.uuid())
.executeProtobuf(SearchWsResponse.class).getIssuesList()).extracting(Issue::getKey)
- .containsExactlyInAnyOrder(issue1.getKey());
+ .containsExactlyInAnyOrder(issue1.getKey());
}
@Test
@@ -484,11 +484,11 @@ public class SearchActionComponentsTest {
.setParam(PARAM_COMPONENT_KEYS, applicationBranch1.getKey())
.setParam(PARAM_BRANCH, applicationBranch1.getBranch())
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getKey, Issue::getComponent, Issue::getProject, Issue::getBranch, Issue::hasBranch)
- .containsExactlyInAnyOrder(
- tuple(issueOnProject1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getBranch(), true),
- tuple(issueOnFileOnProject1Branch1.getKey(), fileOnProject1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getBranch(), true),
- tuple(issueOnProject2.getKey(), project2.getKey(), project2.getKey(), "", false));
+ .extracting(Issue::getKey, Issue::getComponent, Issue::getProject, Issue::getBranch, Issue::hasBranch)
+ .containsExactlyInAnyOrder(
+ tuple(issueOnProject1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getBranch(), true),
+ tuple(issueOnFileOnProject1Branch1.getKey(), fileOnProject1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getBranch(), true),
+ tuple(issueOnProject2.getKey(), project2.getKey(), project2.getKey(), "", false));
// Issues on project1Branch1
assertThat(ws.newRequest()
@@ -496,10 +496,10 @@ public class SearchActionComponentsTest {
.setParam(PARAM_PROJECTS, project1.getKey())
.setParam(PARAM_BRANCH, applicationBranch1.getBranch())
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
- .containsExactlyInAnyOrder(
- tuple(issueOnProject1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getBranch()),
- tuple(issueOnFileOnProject1Branch1.getKey(), fileOnProject1Branch1.getKey(), project1Branch1.getBranch()));
+ .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
+ .containsExactlyInAnyOrder(
+ tuple(issueOnProject1Branch1.getKey(), project1Branch1.getKey(), project1Branch1.getBranch()),
+ tuple(issueOnFileOnProject1Branch1.getKey(), fileOnProject1Branch1.getKey(), project1Branch1.getBranch()));
}
@Test
@@ -670,24 +670,24 @@ public class SearchActionComponentsTest {
.setParam(PARAM_COMPONENT_KEYS, project.getKey())
.setParam(PARAM_BRANCH, branch.getBranch())
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
- .containsExactlyInAnyOrder(tuple(branchIssue.getKey(), branchFile.getKey(), branchFile.getBranch()));
+ .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
+ .containsExactlyInAnyOrder(tuple(branchIssue.getKey(), branchFile.getKey(), branchFile.getBranch()));
// On project key + branch
assertThat(ws.newRequest()
.setParam(PARAM_PROJECT_KEYS, project.getKey())
.setParam(PARAM_BRANCH, branch.getBranch())
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
- .containsExactlyInAnyOrder(tuple(branchIssue.getKey(), branchFile.getKey(), branchFile.getBranch()));
+ .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
+ .containsExactlyInAnyOrder(tuple(branchIssue.getKey(), branchFile.getKey(), branchFile.getBranch()));
// On file key + branch
assertThat(ws.newRequest()
.setParam(PARAM_COMPONENT_KEYS, branchFile.getKey())
.setParam(PARAM_BRANCH, branch.getBranch())
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
- .containsExactlyInAnyOrder(tuple(branchIssue.getKey(), branchFile.getKey(), branchFile.getBranch()));
+ .extracting(Issue::getKey, Issue::getComponent, Issue::getBranch)
+ .containsExactlyInAnyOrder(tuple(branchIssue.getKey(), branchFile.getKey(), branchFile.getBranch()));
}
@Test
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
index 68ba8cc8037..ed1c329999c 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
@@ -44,8 +44,10 @@ import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.component.SnapshotDto;
import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.organization.OrganizationDto;
@@ -94,6 +96,7 @@ import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.api.utils.DateUtils.parseDate;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
+import static org.sonar.db.component.ComponentDto.PULL_REQUEST_SEPARATOR;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.issue.IssueTesting.newDto;
import static org.sonar.server.tester.UserSessionRule.standalone;
@@ -106,7 +109,9 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNEES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_KEYS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AFTER;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_HIDE_COMMENTS;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PULL_REQUEST;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SINCE_LEAK_PERIOD;
public class SearchActionTest {
@@ -509,6 +514,126 @@ public class SearchActionTest {
}
@Test
+ public void filter_by_leak_period() {
+ UserDto john = db.users().insertUser(u -> u.setLogin("john").setName("John").setEmail("john@email.com"));
+ UserDto alice = db.users().insertUser(u -> u.setLogin("alice").setName("Alice").setEmail("alice@email.com"));
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertComponent(ComponentTesting.newPublicProjectDto(organization, "PROJECT_ID").setDbKey("PROJECT_KEY"));
+ SnapshotDto snapshotDto = db.components().insertSnapshot(project, s -> s.setLast(true).setPeriodDate(parseDateTime("2014-09-05T00:00:00+0100").getTime()));
+ indexPermissions();
+
+ ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
+ RuleDto rule = newIssueRule();
+ IssueDto issue1 = newDto(rule, file, project)
+ .setIssueCreationDate(parseDateTime("2014-09-04T00:00:00+0100"))
+ .setIssueUpdateDate(parseDateTime("2017-12-04T00:00:00+0100"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
+ .setSeverity("MAJOR")
+ .setAssigneeUuid(john.getUuid());
+ IssueDto issue2 = newDto(rule, file, project)
+ .setIssueCreationDate(parseDateTime("2014-09-06T00:00:00+0100"))
+ .setIssueUpdateDate(parseDateTime("2017-12-04T00:00:00+0100"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("7b112bd4-b650-4037-80bc-82fd47d4eac2")
+ .setSeverity("MAJOR")
+ .setAssigneeUuid(alice.getUuid());
+ dbClient.issueDao().insert(session, issue1, issue2);
+ session.commit();
+ indexIssues();
+
+ userSession.logIn(john);
+
+ ws.newRequest()
+ .setParam(PARAM_SINCE_LEAK_PERIOD, "true")
+ .setParam(PARAM_COMPONENT_KEYS, "PROJECT_KEY")
+ .execute()
+ .assertJson(this.getClass(), "filter_by_leak_period.json");
+ }
+
+ @Test
+ public void filter_by_leak_period_without_a_period() {
+ UserDto john = db.users().insertUser(u -> u.setLogin("john").setName("John").setEmail("john@email.com"));
+ UserDto alice = db.users().insertUser(u -> u.setLogin("alice").setName("Alice").setEmail("alice@email.com"));
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertComponent(ComponentTesting.newPublicProjectDto(organization, "PROJECT_ID").setDbKey("PROJECT_KEY"));
+ SnapshotDto snapshotDto = db.components().insertSnapshot(project);
+ indexPermissions();
+ ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
+ RuleDto rule = newIssueRule();
+ IssueDto issue1 = newDto(rule, file, project)
+ .setIssueCreationDate(parseDateTime("2014-09-04T00:00:00+0100"))
+ .setIssueUpdateDate(parseDateTime("2017-12-04T00:00:00+0100"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
+ .setSeverity("MAJOR")
+ .setAssigneeUuid(john.getUuid());
+ IssueDto issue2 = newDto(rule, file, project)
+ .setIssueCreationDate(parseDateTime("2014-09-04T00:00:00+0100"))
+ .setIssueUpdateDate(parseDateTime("2017-12-04T00:00:00+0100"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("7b112bd4-b650-4037-80bc-82fd47d4eac2")
+ .setSeverity("MAJOR")
+ .setAssigneeUuid(alice.getUuid());
+ dbClient.issueDao().insert(session, issue1, issue2);
+ session.commit();
+ indexIssues();
+
+ userSession.logIn(john);
+
+ ws.newRequest()
+ .setParam(PARAM_COMPONENT_KEYS, "PROJECT_KEY")
+ .setParam(PARAM_SINCE_LEAK_PERIOD, "true")
+ .execute()
+ .assertJson(this.getClass(), "empty_result.json");
+ }
+
+ @Test
+ public void filter_by_leak_period_has_no_effect_on_prs() {
+ UserDto john = db.users().insertUser(u -> u.setLogin("john").setName("John").setEmail("john@email.com"));
+ UserDto alice = db.users().insertUser(u -> u.setLogin("alice").setName("Alice").setEmail("alice@email.com"));
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPublicProject(organization, c -> c.setUuid("PROJECT_ID").setDbKey("PROJECT_KEY"));
+ ComponentDto pr = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST).setKey("pr"));
+ SnapshotDto snapshotDto = db.components().insertSnapshot(pr);
+ indexPermissions();
+ ComponentDto file = db.components().insertComponent(newFileDto(pr, null, "FILE_ID").setDbKey("FILE_KEY" + PULL_REQUEST_SEPARATOR + "pr"));
+ RuleDto rule = newIssueRule();
+ IssueDto issue1 = newDto(rule, file, pr)
+ .setIssueCreationDate(parseDateTime("2014-09-04T00:00:00+0100"))
+ .setIssueUpdateDate(parseDateTime("2017-12-04T00:00:00+0100"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
+ .setSeverity("MAJOR")
+ .setAssigneeUuid(john.getUuid());
+ IssueDto issue2 = newDto(rule, file, pr)
+ .setIssueCreationDate(parseDateTime("2014-09-04T00:00:00+0100"))
+ .setIssueUpdateDate(parseDateTime("2017-12-04T00:00:00+0100"))
+ .setEffort(10L)
+ .setStatus("OPEN")
+ .setKee("7b112bd4-b650-4037-80bc-82fd47d4eac2")
+ .setSeverity("MAJOR")
+ .setAssigneeUuid(alice.getUuid());
+ dbClient.issueDao().insert(session, issue1, issue2);
+ session.commit();
+ indexIssues();
+
+ userSession.logIn(john);
+
+ ws.newRequest()
+ .setParam(PARAM_COMPONENT_KEYS, "PROJECT_KEY")
+ .setParam(PARAM_PULL_REQUEST, "pr")
+ .setParam(PARAM_SINCE_LEAK_PERIOD, "true")
+ .execute()
+ .assertJson(this.getClass(), "filter_by_leak_period_has_no_effect_on_prs.json");
+ }
+
+ @Test
public void return_empty_when_login_is_unknown() {
UserDto john = db.users().insertUser(u -> u.setLogin("john").setName("John").setEmail("john@email.com"));
UserDto alice = db.users().insertUser(u -> u.setLogin("alice").setName("Alice").setEmail("alice@email.com"));
@@ -620,7 +745,7 @@ public class SearchActionTest {
assertThat(ws.newRequest()
.setMultiParam("author", singletonList("unknown"))
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .isEmpty();
+ .isEmpty();
}
@Test
@@ -652,8 +777,8 @@ public class SearchActionTest {
// This parameter will be ignored
.setParam("authors", "leia")
.executeProtobuf(SearchWsResponse.class).getIssuesList())
- .extracting(Issue::getKey)
- .containsExactlyInAnyOrder(issue2.getKey());
+ .extracting(Issue::getKey)
+ .containsExactlyInAnyOrder(issue2.getKey());
}
@Test
@@ -829,8 +954,8 @@ public class SearchActionTest {
assertThatThrownBy(() -> ws.newRequest()
.setParam("types", RuleType.SECURITY_HOTSPOT.toString())
.execute())
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Value of parameter 'types' (SECURITY_HOTSPOT) must be one of: [CODE_SMELL, BUG, VULNERABILITY]");
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Value of parameter 'types' (SECURITY_HOTSPOT) must be one of: [CODE_SMELL, BUG, VULNERABILITY]");
}
@Test
diff --git a/server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/filter_by_leak_period.json b/server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/filter_by_leak_period.json
new file mode 100644
index 00000000000..0b868a72643
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/filter_by_leak_period.json
@@ -0,0 +1,51 @@
+{
+ "total": 1,
+ "p": 1,
+ "ps": 100,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 1
+ },
+ "effortTotal": 10,
+ "debtTotal": 10,
+ "issues": [
+ {
+ "key": "7b112bd4-b650-4037-80bc-82fd47d4eac2",
+ "rule": "xoo:x1",
+ "severity": "MAJOR",
+ "component": "FILE_KEY",
+ "project": "PROJECT_KEY",
+ "flows": [],
+ "status": "OPEN",
+ "message": "",
+ "effort": "10min",
+ "debt": "10min",
+ "assignee": "alice",
+ "tags": [],
+ "creationDate": "2014-09-06T00:00:00+0100",
+ "updateDate": "2017-12-04T00:00:00+0100",
+ "type": "CODE_SMELL",
+ }
+ ],
+ "components": [
+ {
+ "key": "FILE_KEY",
+ "uuid": "FILE_ID",
+ "enabled": true,
+ "qualifier": "FIL",
+ "name": "NAME_FILE_ID",
+ "longName": "null/NAME_FILE_ID",
+ "path": "null/NAME_FILE_ID"
+ },
+ {
+ "key": "PROJECT_KEY",
+ "uuid": "PROJECT_ID",
+ "enabled": true,
+ "qualifier": "TRK",
+ "name": "NAME_PROJECT_ID",
+ "longName": "LONG_NAME_PROJECT_ID"
+ }
+ ],
+ "facets": []
+}
diff --git a/server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/filter_by_leak_period_has_no_effect_on_prs.json b/server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/filter_by_leak_period_has_no_effect_on_prs.json
new file mode 100644
index 00000000000..75400d0b309
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/filter_by_leak_period_has_no_effect_on_prs.json
@@ -0,0 +1,69 @@
+{
+ "total": 2,
+ "p": 1,
+ "ps": 100,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2
+ },
+ "effortTotal": 20,
+ "debtTotal": 20,
+ "issues": [
+ {
+ "key": "7b112bd4-b650-4037-80bc-82fd47d4eac2",
+ "rule": "xoo:x1",
+ "severity": "MAJOR",
+ "component": "FILE_KEY",
+ "project": "PROJECT_KEY",
+ "flows": [],
+ "status": "OPEN",
+ "message": "",
+ "effort": "10min",
+ "debt": "10min",
+ "assignee": "alice",
+ "tags": [],
+ "creationDate": "2014-09-04T00:00:00+0100",
+ "updateDate": "2017-12-04T00:00:00+0100",
+ "type": "CODE_SMELL",
+ "pullRequest": "pr"
+ },
+ {
+ "key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
+ "rule": "xoo:x1",
+ "severity": "MAJOR",
+ "component": "FILE_KEY",
+ "project": "PROJECT_KEY",
+ "flows": [],
+ "status": "OPEN",
+ "message": "",
+ "effort": "10min",
+ "debt": "10min",
+ "assignee": "john",
+ "tags": [],
+ "creationDate": "2014-09-04T00:00:00+0100",
+ "updateDate": "2017-12-04T00:00:00+0100",
+ "type": "CODE_SMELL",
+ "pullRequest": "pr"
+ }
+ ],
+ "components": [
+ {
+ "key": "FILE_KEY",
+ "uuid": "FILE_ID",
+ "enabled": true,
+ "qualifier": "FIL",
+ "name": "NAME_FILE_ID",
+ "longName": "null/NAME_FILE_ID",
+ "path": "null/NAME_FILE_ID",
+ "pullRequest": "pr"
+ },
+ {
+ "key": "PROJECT_KEY",
+ "enabled": true,
+ "qualifier": "TRK",
+ "pullRequest": "pr"
+ }
+ ],
+ "facets": []
+}