@@ -1,66 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2017 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.issue; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import org.sonar.db.issue.IssueDto; | |||
import org.sonar.server.user.UserSession; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
import static java.util.Objects.requireNonNull; | |||
import static org.sonar.api.web.UserRole.ISSUE_ADMIN; | |||
import static org.sonar.server.issue.AssignAction.ASSIGN_KEY; | |||
import static org.sonar.server.issue.CommentAction.COMMENT_KEY; | |||
import static org.sonar.server.issue.SetSeverityAction.SET_SEVERITY_KEY; | |||
import static org.sonar.server.issue.SetTypeAction.SET_TYPE_KEY; | |||
public class ActionFinder { | |||
private final UserSession userSession; | |||
public ActionFinder(UserSession userSession) { | |||
this.userSession = userSession; | |||
} | |||
public List<String> listAvailableActions(IssueDto issue) { | |||
List<String> availableActions = newArrayList(); | |||
String login = userSession.getLogin(); | |||
if (login == null) { | |||
return Collections.emptyList(); | |||
} | |||
availableActions.add(COMMENT_KEY); | |||
if (issue.getResolution() != null) { | |||
return availableActions; | |||
} | |||
availableActions.add(ASSIGN_KEY); | |||
availableActions.add("set_tags"); | |||
if (!login.equals(issue.getAssignee())) { | |||
// This action will be removed by | |||
availableActions.add("assign_to_me"); | |||
} | |||
if (userSession.hasComponentUuidPermission(ISSUE_ADMIN, requireNonNull(issue.getProjectUuid()))) { | |||
availableActions.add(SET_TYPE_KEY); | |||
availableActions.add(SET_SEVERITY_KEY); | |||
} | |||
return availableActions; | |||
} | |||
} |
@@ -20,7 +20,6 @@ | |||
package org.sonar.server.issue.ws; | |||
import org.sonar.core.platform.Module; | |||
import org.sonar.server.issue.ActionFinder; | |||
import org.sonar.server.issue.IssueFieldsSetter; | |||
import org.sonar.server.issue.IssueFinder; | |||
import org.sonar.server.issue.IssueQueryFactory; | |||
@@ -37,7 +36,6 @@ public class IssueWsModule extends Module { | |||
@Override | |||
protected void configureModule() { | |||
add( | |||
ActionFinder.class, | |||
IssueUpdater.class, | |||
IssueFinder.class, | |||
TransitionService.class, |
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableSet; | |||
import com.google.common.collect.MultimapBuilder; | |||
import com.google.common.collect.SetMultimap; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
@@ -40,7 +41,6 @@ import org.sonar.db.protobuf.DbIssues; | |||
import org.sonar.db.rule.RuleDefinitionDto; | |||
import org.sonar.db.user.UserDto; | |||
import org.sonar.server.es.Facets; | |||
import org.sonar.server.issue.ActionFinder; | |||
import org.sonar.server.issue.TransitionService; | |||
import org.sonar.server.user.UserSession; | |||
import org.sonarqube.ws.client.issue.IssuesWsParameters; | |||
@@ -51,7 +51,12 @@ import static com.google.common.collect.Lists.newArrayList; | |||
import static com.google.common.collect.Sets.difference; | |||
import static java.util.Collections.emptyList; | |||
import static java.util.stream.Stream.concat; | |||
import static org.sonar.api.web.UserRole.ISSUE_ADMIN; | |||
import static org.sonar.core.util.stream.MoreCollectors.toList; | |||
import static org.sonar.server.issue.AssignAction.ASSIGN_KEY; | |||
import static org.sonar.server.issue.CommentAction.COMMENT_KEY; | |||
import static org.sonar.server.issue.SetSeverityAction.SET_SEVERITY_KEY; | |||
import static org.sonar.server.issue.SetTypeAction.SET_TYPE_KEY; | |||
import static org.sonar.server.issue.ws.SearchAdditionalField.ACTIONS; | |||
import static org.sonar.server.issue.ws.SearchAdditionalField.COMMENTS; | |||
import static org.sonar.server.issue.ws.SearchAdditionalField.RULES; | |||
@@ -65,13 +70,11 @@ public class SearchResponseLoader { | |||
private final UserSession userSession; | |||
private final DbClient dbClient; | |||
private final ActionFinder actionService; | |||
private final TransitionService transitionService; | |||
public SearchResponseLoader(UserSession userSession, DbClient dbClient, ActionFinder actionService, TransitionService transitionService) { | |||
public SearchResponseLoader(UserSession userSession, DbClient dbClient, TransitionService transitionService) { | |||
this.userSession = userSession; | |||
this.dbClient = dbClient; | |||
this.actionService = actionService; | |||
this.transitionService = transitionService; | |||
} | |||
@@ -241,10 +244,16 @@ public class SearchResponseLoader { | |||
private void loadActionsAndTransitions(Collector collector, SearchResponseData result) { | |||
if (collector.contains(ACTIONS) || collector.contains(TRANSITIONS)) { | |||
Map<String, ComponentDto> componentsByProjectUuid = | |||
result.getComponents() | |||
.stream() | |||
.filter(ComponentDto::isRootProject) | |||
.collect(MoreCollectors.uniqueIndex(ComponentDto::projectUuid)); | |||
for (IssueDto dto : result.getIssues()) { | |||
// so that IssueDto can be used. | |||
if (collector.contains(ACTIONS)) { | |||
result.addActions(dto.getKey(), actionService.listAvailableActions(dto)); | |||
ComponentDto project = componentsByProjectUuid.get(dto.getProjectUuid()); | |||
result.addActions(dto.getKey(), listAvailableActions(dto, project)); | |||
} | |||
if (collector.contains(TRANSITIONS)) { | |||
// TODO workflow and action engines must not depend on org.sonar.api.issue.Issue but on a generic interface | |||
@@ -255,6 +264,25 @@ public class SearchResponseLoader { | |||
} | |||
} | |||
private List<String> listAvailableActions(IssueDto issue, ComponentDto project) { | |||
List<String> availableActions = newArrayList(); | |||
String login = userSession.getLogin(); | |||
if (login == null) { | |||
return Collections.emptyList(); | |||
} | |||
availableActions.add(COMMENT_KEY); | |||
if (issue.getResolution() != null) { | |||
return availableActions; | |||
} | |||
availableActions.add(ASSIGN_KEY); | |||
availableActions.add("set_tags"); | |||
if (userSession.hasComponentPermission(ISSUE_ADMIN, project)) { | |||
availableActions.add(SET_TYPE_KEY); | |||
availableActions.add(SET_SEVERITY_KEY); | |||
} | |||
return availableActions; | |||
} | |||
private static void completeTotalEffortFromFacet(@Nullable Facets facets, SearchResponseData result) { | |||
if (facets != null) { | |||
Map<String, Long> effortFacet = facets.get(IssuesWsParameters.FACET_MODE_EFFORT); |
@@ -33,7 +33,6 @@ | |||
"assign", | |||
"set_tags", | |||
"set_type", | |||
"assign_to_me", | |||
"set_severity" | |||
], | |||
"comments": [ |
@@ -33,7 +33,6 @@ | |||
"assign", | |||
"set_tags", | |||
"set_type", | |||
"assign_to_me", | |||
"set_severity" | |||
], | |||
"comments": [ |
@@ -33,7 +33,6 @@ | |||
"assign", | |||
"set_tags", | |||
"set_type", | |||
"assign_to_me", | |||
"set_severity" | |||
], | |||
"comments": [ |
@@ -33,7 +33,6 @@ | |||
"assign", | |||
"set_tags", | |||
"set_type", | |||
"assign_to_me", | |||
"set_severity" | |||
], | |||
"comments": [ |
@@ -33,7 +33,6 @@ | |||
"assign", | |||
"set_tags", | |||
"set_type", | |||
"assign_to_me", | |||
"set_severity" | |||
], | |||
"comments": [ |
@@ -33,7 +33,6 @@ | |||
"assign", | |||
"set_tags", | |||
"set_type", | |||
"assign_to_me", | |||
"set_severity" | |||
], | |||
"comments": [ |
@@ -33,7 +33,6 @@ | |||
"assign", | |||
"set_tags", | |||
"set_type", | |||
"assign_to_me", | |||
"set_severity" | |||
], | |||
"comments": [ |
@@ -33,7 +33,6 @@ | |||
"assign", | |||
"set_tags", | |||
"set_type", | |||
"assign_to_me", | |||
"set_severity" | |||
], | |||
"comments": [ |
@@ -1,88 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2017 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.issue; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.component.ComponentTesting; | |||
import org.sonar.db.issue.IssueDto; | |||
import org.sonar.db.organization.OrganizationTesting; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonar.api.issue.Issue.RESOLUTION_FIXED; | |||
import static org.sonar.api.web.UserRole.ISSUE_ADMIN; | |||
import static org.sonar.db.component.ComponentTesting.newFileDto; | |||
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; | |||
import static org.sonar.db.issue.IssueTesting.newDto; | |||
import static org.sonar.db.rule.RuleTesting.newXooX1; | |||
public class ActionFinderTest { | |||
static final String PROJECT_KEY = "PROJECT_KEY"; | |||
static final String PROJECT_UUID = "PROJECT_UUID"; | |||
static final String ISSUE_KEY = "ISSUE_KEY"; | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
@Rule | |||
public UserSessionRule userSession = UserSessionRule.standalone().logIn("arthur"); | |||
private ComponentDto project = newPrivateProjectDto(OrganizationTesting.newOrganizationDto(), PROJECT_UUID).setDbKey(PROJECT_KEY); | |||
private IssueDto issue = newDto(newXooX1().setId(10), newFileDto(project, null), project).setKee(ISSUE_KEY); | |||
private ActionFinder underTest = new ActionFinder(userSession); | |||
@Test | |||
public void return_provided_actions_without_set_severity_and_set_tpye_when_not_issue_admin() { | |||
assertThat(underTest.listAvailableActions(issue)).containsOnly("comment", "assign", "set_tags", "assign_to_me"); | |||
} | |||
@Test | |||
public void return_provided_actions_with_set_severity_and_set_type_when_issue_admin() { | |||
userSession.addProjectPermission(ISSUE_ADMIN, ComponentTesting.newPrivateProjectDto(OrganizationTesting.newOrganizationDto(), PROJECT_UUID)); | |||
assertThat(underTest.listAvailableActions(issue)).containsOnly("comment", "assign", "set_tags", "set_type", "assign_to_me", "set_severity"); | |||
} | |||
@Test | |||
public void return_no_actions_when_not_logged() { | |||
userSession.anonymous(); | |||
assertThat(underTest.listAvailableActions(issue)).isEmpty(); | |||
} | |||
@Test | |||
public void doest_not_return_assign_to_me_action_when_issue_already_assigned_to_user() { | |||
userSession.logIn("julien"); | |||
IssueDto issue = newDto(newXooX1().setId(10), newFileDto(project, null), project).setKee(ISSUE_KEY).setAssignee("julien"); | |||
assertThat(underTest.listAvailableActions(issue)).doesNotContain("assign_to_me"); | |||
} | |||
@Test | |||
public void return_only_comment_action_when_issue_has_a_resolution() { | |||
IssueDto issue = newDto(newXooX1().setId(10), newFileDto(project, null), project).setKee(ISSUE_KEY).setResolution(RESOLUTION_FIXED); | |||
assertThat(underTest.listAvailableActions(issue)).containsOnly("comment"); | |||
} | |||
} |
@@ -30,7 +30,7 @@ public class IssueWsModuleTest { | |||
public void verify_count_of_added_components() { | |||
ComponentContainer container = new ComponentContainer(); | |||
new IssueWsModule().configure(container); | |||
assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 31); | |||
assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 30); | |||
} | |||
} | |||
@@ -40,7 +40,6 @@ import org.sonar.db.issue.IssueDto; | |||
import org.sonar.db.organization.OrganizationDto; | |||
import org.sonar.db.rule.RuleDefinitionDto; | |||
import org.sonar.server.es.EsTester; | |||
import org.sonar.server.issue.ActionFinder; | |||
import org.sonar.server.issue.IssueFieldsSetter; | |||
import org.sonar.server.issue.IssueQueryFactory; | |||
import org.sonar.server.issue.TransitionService; | |||
@@ -97,8 +96,7 @@ public class SearchActionComponentsTest { | |||
private IssueQueryFactory issueQueryFactory = new IssueQueryFactory(dbClient, Clock.systemUTC(), userSession); | |||
private IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter(); | |||
private IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter); | |||
private SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, dbClient, new ActionFinder(userSession), | |||
new TransitionService(userSession, issueWorkflow)); | |||
private SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, dbClient, new TransitionService(userSession, issueWorkflow)); | |||
private Languages languages = new Languages(); | |||
private SearchResponseFormat searchResponseFormat = new SearchResponseFormat(new Durations(), new WsResponseCommonFormat(languages), languages, new AvatarResolverImpl()); | |||
private PermissionIndexerTester permissionIndexer = new PermissionIndexerTester(es, issueIndexer); |
@@ -50,7 +50,6 @@ import org.sonar.db.user.UserDto; | |||
import org.sonar.server.es.EsTester; | |||
import org.sonar.server.es.SearchOptions; | |||
import org.sonar.server.es.StartupIndexer; | |||
import org.sonar.server.issue.ActionFinder; | |||
import org.sonar.server.issue.IssueFieldsSetter; | |||
import org.sonar.server.issue.IssueQuery; | |||
import org.sonar.server.issue.IssueQueryFactory; | |||
@@ -99,8 +98,7 @@ public class SearchActionTest { | |||
private IssueQueryFactory issueQueryFactory = new IssueQueryFactory(dbClient, Clock.systemUTC(), userSessionRule); | |||
private IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter(); | |||
private IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter); | |||
private SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSessionRule, dbClient, new ActionFinder(userSessionRule), | |||
new TransitionService(userSessionRule, issueWorkflow)); | |||
private SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSessionRule, dbClient, new TransitionService(userSessionRule, issueWorkflow)); | |||
private Languages languages = new Languages(); | |||
private SearchResponseFormat searchResponseFormat = new SearchResponseFormat(new Durations(), new WsResponseCommonFormat(languages), languages, new AvatarResolverImpl()); | |||
private WsActionTester ws = new WsActionTester(new SearchAction(userSessionRule, issueIndex, issueQueryFactory, searchResponseLoader, searchResponseFormat)); |
@@ -7,8 +7,7 @@ | |||
"actions": [ | |||
"comment", | |||
"assign", | |||
"set_tags", | |||
"assign_to_me" | |||
"set_tags" | |||
], | |||
"transitions": [ | |||
"confirm", |
@@ -8,7 +8,6 @@ | |||
"comment", | |||
"assign", | |||
"set_tags", | |||
"assign_to_me", | |||
"set_type", | |||
"set_severity" | |||
], |