diff options
8 files changed, 94 insertions, 47 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java index 2921dc90ab8..f7e1c1c1900 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java @@ -202,15 +202,6 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> { return esFilter; } - public void deleteByProjectUuid(String uuid) { - QueryBuilder queryBuilder = QueryBuilders.filteredQuery( - QueryBuilders.matchAllQuery(), - FilterBuilders.boolFilter().must(FilterBuilders.termsFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, uuid)) - ); - - getClient().prepareDeleteByQuery(getIndexName()).setQuery(queryBuilder).get(); - } - public void deleteClosedIssuesOfProjectBefore(String uuid, Date beforeDate) { FilterBuilder projectFilter = FilterBuilders.boolFilter().must(FilterBuilders.termsFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, uuid)); FilterBuilder dateFilter = FilterBuilders.rangeFilter(IssueNormalizer.IssueField.ISSUE_CLOSE_DATE.field()).lt(beforeDate.getTime()); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_facets.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_facets.json index 3560bf84625..20a42e280a0 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_facets.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_facets.json @@ -80,6 +80,10 @@ { "val": "REMOVED", "count": 0 + }, + { + "val": "MUTED", + "count": 0 } ] }, diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_zero_facets.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_zero_facets.json index 65c2a5466c1..d4debbcb81f 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_zero_facets.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_zero_facets.json @@ -80,6 +80,10 @@ { "val": "REMOVED", "count": 0 + }, + { + "val": "MUTED", + "count": 0 } ] }, diff --git a/sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java b/sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java index 6ddf0a270c6..c12bce14343 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java @@ -104,7 +104,26 @@ public class IssueWorkflow implements BatchComponent, ServerComponent, Startable .from(Issue.STATUS_CONFIRMED).to(Issue.STATUS_RESOLVED) .functions(new SetResolution(Issue.RESOLUTION_FALSE_POSITIVE), SetAssignee.UNASSIGN) .requiredProjectPermission(UserRole.ISSUE_ADMIN) - .build()); + .build()) + + // resolve as muted + .transition(Transition.builder(DefaultTransitions.MUTE) + .from(Issue.STATUS_OPEN).to(Issue.STATUS_RESOLVED) + .functions(new SetResolution(Issue.RESOLUTION_MUTED), SetAssignee.UNASSIGN) + .requiredProjectPermission(UserRole.ISSUE_ADMIN) + .build()) + .transition(Transition.builder(DefaultTransitions.MUTE) + .from(Issue.STATUS_REOPENED).to(Issue.STATUS_RESOLVED) + .functions(new SetResolution(Issue.RESOLUTION_MUTED), SetAssignee.UNASSIGN) + .requiredProjectPermission(UserRole.ISSUE_ADMIN) + .build()) + .transition(Transition.builder(DefaultTransitions.MUTE) + .from(Issue.STATUS_CONFIRMED).to(Issue.STATUS_RESOLVED) + .functions(new SetResolution(Issue.RESOLUTION_MUTED), SetAssignee.UNASSIGN) + .requiredProjectPermission(UserRole.ISSUE_ADMIN) + .build() + ); + } private void buildAutomaticTransitions(StateMachine.Builder builder) { @@ -143,11 +162,11 @@ public class IssueWorkflow implements BatchComponent, ServerComponent, Startable // Reopen issues that are marked as resolved but that are still alive. // Manual issues are kept resolved. .transition(Transition.builder("automaticreopen") - .from(Issue.STATUS_RESOLVED).to(Issue.STATUS_REOPENED) - .conditions(new IsEndOfLife(false), new HasResolution(Issue.RESOLUTION_FIXED), new IsManual(false)) - .functions(new SetResolution(null), new SetCloseDate(false)) - .automatic() - .build() + .from(Issue.STATUS_RESOLVED).to(Issue.STATUS_REOPENED) + .conditions(new IsEndOfLife(false), new HasResolution(Issue.RESOLUTION_FIXED), new IsManual(false)) + .functions(new SetResolution(null), new SetCloseDate(false)) + .automatic() + .build() ); } diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 0b6a9905610..4dcf2f4b37e 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -655,6 +655,7 @@ issue.transition.resolve=Resolve issue.transition.falsepositive=False Positive issue.transition.reopen=Reopen issue.transition.close=Close +issue.transition.mute=Mute issue.set_severity=Change Severity issue.set_severity.submit=Change Severity issue.do_plan=Plan @@ -669,6 +670,7 @@ issue.status.CONFIRMED=Confirmed issue.status.CLOSED=Closed issue.resolution.FALSE-POSITIVE=False Positive issue.resolution.FIXED=Fixed +issue.resolution.MUTED=Muted issue.resolution.REMOVED=Removed issue.updated=Updated: issue.planned_for_x=Planned for {0} diff --git a/sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java b/sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java index 6961d78d72c..65b53358f16 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java @@ -47,7 +47,7 @@ public class IssueWorkflowTest { IssueWorkflow workflow = new IssueWorkflow(new FunctionExecutor(updater), updater); @Test - public void should_init_state_machine() throws Exception { + public void init_state_machine() throws Exception { assertThat(workflow.machine()).isNull(); workflow.start(); assertThat(workflow.machine()).isNotNull(); @@ -60,54 +60,50 @@ public class IssueWorkflowTest { } @Test - public void should_list_statuses() throws Exception { + public void list_statuses() throws Exception { workflow.start(); // order is important for UI assertThat(workflow.statusKeys()).containsSequence(Issue.STATUS_OPEN, Issue.STATUS_CONFIRMED, Issue.STATUS_REOPENED, Issue.STATUS_RESOLVED, Issue.STATUS_CLOSED); } @Test - public void should_list_out_transitions_from_status_open() throws Exception { + public void list_out_transitions_from_status_open() throws Exception { workflow.start(); DefaultIssue issue = new DefaultIssue().setStatus(Issue.STATUS_OPEN); List<Transition> transitions = workflow.outTransitions(issue); - assertThat(transitions).hasSize(3); - assertThat(keys(transitions)).containsOnly("confirm", "falsepositive", "resolve"); + assertThat(keys(transitions)).containsOnly("confirm", "falsepositive", "resolve", "mute"); } @Test - public void should_list_out_transitions_from_status_confirmed() throws Exception { + public void list_out_transitions_from_status_confirmed() throws Exception { workflow.start(); DefaultIssue issue = new DefaultIssue().setStatus(Issue.STATUS_CONFIRMED); List<Transition> transitions = workflow.outTransitions(issue); - assertThat(transitions).hasSize(3); - assertThat(keys(transitions)).containsOnly("unconfirm", "falsepositive", "resolve"); + assertThat(keys(transitions)).containsOnly("unconfirm", "falsepositive", "resolve", "mute"); } @Test - public void should_list_out_transitions_from_status_resolved() throws Exception { + public void list_out_transitions_from_status_resolved() throws Exception { workflow.start(); DefaultIssue issue = new DefaultIssue().setStatus(Issue.STATUS_RESOLVED); List<Transition> transitions = workflow.outTransitions(issue); - assertThat(transitions).hasSize(1); assertThat(keys(transitions)).containsOnly("reopen"); } @Test - public void should_list_out_transitions_from_status_reopen() throws Exception { + public void list_out_transitions_from_status_reopen() throws Exception { workflow.start(); DefaultIssue issue = new DefaultIssue().setStatus(Issue.STATUS_REOPENED); List<Transition> transitions = workflow.outTransitions(issue); - assertThat(transitions).hasSize(3); - assertThat(keys(transitions)).containsOnly("confirm", "resolve", "falsepositive"); + assertThat(keys(transitions)).containsOnly("confirm", "mute", "resolve", "falsepositive", "mute"); } @Test - public void should_list_no_out_transition_from_status_closed() throws Exception { + public void list_no_out_transition_from_status_closed() throws Exception { workflow.start(); DefaultIssue issue = new DefaultIssue().setStatus(Issue.STATUS_CLOSED); @@ -116,7 +112,7 @@ public class IssueWorkflowTest { } @Test - public void should_list_out_transitions_from_status_closed_on_manual_issue() throws Exception { + public void list_out_transitions_from_status_closed_on_manual_issue() throws Exception { workflow.start(); // Manual issue because of reporter @@ -127,12 +123,11 @@ public class IssueWorkflowTest { .setReporter("simon"); List<Transition> transitions = workflow.outTransitions(issue); - assertThat(transitions).hasSize(1); assertThat(keys(transitions)).containsOnly("reopen"); } @Test - public void should_fail_if_unknown_status_when_listing_transitions() throws Exception { + public void fail_if_unknown_status_when_listing_transitions() throws Exception { workflow.start(); DefaultIssue issue = new DefaultIssue().setStatus("xxx"); @@ -146,7 +141,7 @@ public class IssueWorkflowTest { @Test - public void should_do_automatic_transition() throws Exception { + public void do_automatic_transition() throws Exception { workflow.start(); DefaultIssue issue = new DefaultIssue() @@ -164,7 +159,7 @@ public class IssueWorkflowTest { } @Test - public void should_close_open_dead_issue() throws Exception { + public void close_open_dead_issue() throws Exception { workflow.start(); DefaultIssue issue = new DefaultIssue() @@ -182,7 +177,7 @@ public class IssueWorkflowTest { } @Test - public void should_close_reopened_dead_issue() throws Exception { + public void close_reopened_dead_issue() throws Exception { workflow.start(); DefaultIssue issue = new DefaultIssue() @@ -200,7 +195,7 @@ public class IssueWorkflowTest { } @Test - public void should_close_confirmed_dead_issue() throws Exception { + public void close_confirmed_dead_issue() throws Exception { workflow.start(); DefaultIssue issue = new DefaultIssue() @@ -219,7 +214,7 @@ public class IssueWorkflowTest { @Test - public void should_fail_if_unknown_status_on_automatic_trans() throws Exception { + public void fail_if_unknown_status_on_automatic_trans() throws Exception { workflow.start(); DefaultIssue issue = new DefaultIssue() @@ -237,7 +232,7 @@ public class IssueWorkflowTest { } @Test - public void should_flag_as_false_positive() throws Exception { + public void flag_as_false_positive() throws Exception { DefaultIssue issue = new DefaultIssue() .setKey("ABCDE") .setStatus(Issue.STATUS_OPEN) @@ -255,7 +250,25 @@ public class IssueWorkflowTest { } @Test - public void manual_issues_should_be_resolved_then_closed() throws Exception { + public void mute() throws Exception { + DefaultIssue issue = new DefaultIssue() + .setKey("ABCDE") + .setStatus(Issue.STATUS_OPEN) + .setRuleKey(RuleKey.of("squid", "AvoidCycle")) + .setAssignee("morgan"); + + workflow.start(); + workflow.doTransition(issue, DefaultTransitions.MUTE, IssueChangeContext.createScan(new Date())); + + assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_MUTED); + assertThat(issue.status()).isEqualTo(Issue.STATUS_RESOLVED); + + // should remove assignee + assertThat(issue.assignee()).isNull(); + } + + @Test + public void manual_issues_be_resolved_then_closed() throws Exception { // Manual issue because of reporter DefaultIssue issue = new DefaultIssue() .setKey("ABCDE") @@ -268,7 +281,8 @@ public class IssueWorkflowTest { assertThat(workflow.outTransitions(issue)).containsOnly( Transition.create("confirm", "OPEN", "CONFIRMED"), Transition.create("resolve", "OPEN", "RESOLVED"), - Transition.create("falsepositive", "OPEN", "RESOLVED") + Transition.create("falsepositive", "OPEN", "RESOLVED"), + Transition.create("mute", "OPEN", "RESOLVED") ); workflow.doTransition(issue, "resolve", mock(IssueChangeContext.class)); @@ -285,7 +299,7 @@ public class IssueWorkflowTest { } @Test - public void manual_issues_should_be_confirmed_then_kept_open() throws Exception { + public void manual_issues_be_confirmed_then_kept_open() throws Exception { // Manual issue because of reporter DefaultIssue issue = new DefaultIssue() .setKey("ABCDE") @@ -298,7 +312,8 @@ public class IssueWorkflowTest { assertThat(workflow.outTransitions(issue)).containsOnly( Transition.create("confirm", "OPEN", "CONFIRMED"), Transition.create("resolve", "OPEN", "RESOLVED"), - Transition.create("falsepositive", "OPEN", "RESOLVED") + Transition.create("falsepositive", "OPEN", "RESOLVED"), + Transition.create("mute", "OPEN", "RESOLVED") ); workflow.doTransition(issue, "confirm", mock(IssueChangeContext.class)); @@ -308,7 +323,8 @@ public class IssueWorkflowTest { assertThat(workflow.outTransitions(issue)).containsOnly( Transition.create("unconfirm", "CONFIRMED", "REOPENED"), Transition.create("resolve", "CONFIRMED", "RESOLVED"), - Transition.create("falsepositive", "CONFIRMED", "RESOLVED") + Transition.create("falsepositive", "CONFIRMED", "RESOLVED"), + Transition.create("mute", "CONFIRMED", "RESOLVED") ); // keep confirmed and unresolved @@ -323,7 +339,7 @@ public class IssueWorkflowTest { } @Test - public void manual_issue_on_removed_rule_should_be_closed() throws Exception { + public void manual_issue_on_removed_rule_be_closed() throws Exception { // Manual issue because of reporter DefaultIssue issue = new DefaultIssue() .setKey("ABCDE") @@ -341,7 +357,7 @@ public class IssueWorkflowTest { } @Test - public void manual_issue_on_removed_component_should_be_closed() throws Exception { + public void manual_issue_on_removed_component_be_closed() throws Exception { // Manual issue because of reporter DefaultIssue issue = new DefaultIssue() .setKey("ABCDE") diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/DefaultTransitions.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/DefaultTransitions.java index 94109badb36..b8de744b266 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/DefaultTransitions.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/DefaultTransitions.java @@ -36,7 +36,12 @@ public interface DefaultTransitions { String CLOSE = "close"; /** + * @since 5.1 + */ + String MUTE = "mute"; + + /** * @since 4.4 */ - List<String> ALL = ImmutableList.of(CONFIRM, UNCONFIRM, REOPEN, RESOLVE, FALSE_POSITIVE, CLOSE); + List<String> ALL = ImmutableList.of(CONFIRM, UNCONFIRM, REOPEN, RESOLVE, FALSE_POSITIVE, MUTE, CLOSE); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java index 9bc6322ea1b..55caecffcae 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java @@ -61,7 +61,13 @@ public interface Issue extends Serializable { */ String RESOLUTION_REMOVED = "REMOVED"; - List<String> RESOLUTIONS = ImmutableList.of(RESOLUTION_FALSE_POSITIVE, RESOLUTION_FIXED, RESOLUTION_REMOVED); + /** + * Issue is irrelevant in the context and was muted by user. + * @since 5.1 + */ + String RESOLUTION_MUTED = "MUTED"; + + List<String> RESOLUTIONS = ImmutableList.of(RESOLUTION_FALSE_POSITIVE, RESOLUTION_MUTED, RESOLUTION_FIXED, RESOLUTION_REMOVED); /** * Return all available statuses |