Quellcode durchsuchen

Draft of WS api/issues/search2

- protobuf specification of response
- do not rely on ES docs to return data (will allow to remove _source
   from ES index)
- sanitize response format
- sanitize request parameters (no more 'hideXXX' and 'f')
tags/5.2-RC1
Simon Brandhof vor 8 Jahren
Ursprung
Commit
a3c98b7bf6
66 geänderte Dateien mit 10240 neuen und 6942 gelöschten Zeilen
  1. 0
    1537
      server/sonar-server/src/main/gen-java/org/sonarqube/ws/Rules.java
  2. 10
    7
      server/sonar-server/src/main/java/org/sonar/server/es/Facets.java
  3. 25
    158
      server/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java
  4. 3
    27
      server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
  5. 12
    10
      server/sonar-server/src/main/java/org/sonar/server/issue/IssueCommentService.java
  6. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
  7. 0
    3
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java
  8. 369
    0
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/Search2Action.java
  9. 1
    5
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
  10. 83
    0
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAdditionalField.java
  11. 167
    0
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseData.java
  12. 291
    0
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
  13. 241
    0
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java
  14. 7
    0
      server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
  15. 5
    5
      server/sonar-server/src/main/java/org/sonar/server/rule/ws/ListAction.java
  16. 111
    0
      server/sonar-server/src/main/java/org/sonar/server/ws/WsResponseCommonFormat.java
  17. 54
    0
      server/sonar-server/src/main/java/org/sonar/server/ws/WsUtils.java
  18. 0
    39
      server/sonar-server/src/main/protobuf/ws-rules.proto
  19. 0
    243
      server/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java
  20. 0
    10
      server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
  21. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceMediumTest.java
  22. 14
    14
      server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceTest.java
  23. 2
    1
      server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
  24. 1
    23
      server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java
  25. 100
    99
      server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java
  26. 92
    99
      server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java
  27. 3
    3
      server/sonar-server/src/test/java/org/sonar/server/rule/ws/ListActionTest.java
  28. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/ws/TestRequest.java
  29. 38
    0
      server/sonar-server/src/test/java/org/sonar/server/ws/WsUtilsTest.java
  30. 11
    14
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/display_file_facet.json
  31. 13
    18
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/display_module_facet.json
  32. 4
    7
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/display_non_sticky_project_facet.json
  33. 11
    18
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/display_sticky_project_facet.json
  34. 8
    18
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/issues_on_different_projects.json
  35. 2
    2
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_authors.json
  36. 4
    6
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_developer.json
  37. 2
    4
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_directory_uuid.json
  38. 2
    4
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_file_key.json
  39. 2
    4
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_file_uuid.json
  40. 2
    5
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_project_uuid.json
  41. 2
    4
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_test_key.json
  42. 2
    5
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_view_uuid.json
  43. 1
    2
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/assigned_to_me_facet_sticky.json
  44. 0
    5
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/components_contains_sub_projects.json
  45. 0
    1
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/default_page_size_is_100.json
  46. 0
    1
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/deprecated_paging.json
  47. 3
    4
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_facets.json
  48. 8
    8
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_facets_debt.json
  49. 4
    4
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_zero_facets.json
  50. 1
    4
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/empty_result.json
  51. 2
    2
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/filter_by_assigned_to_me.json
  52. 2
    2
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/hide_rules.json
  53. 0
    62
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue.json
  54. 4
    4
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_on_removed_file.json
  55. 1
    4
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_action_plan.json
  56. 0
    26
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_comment.json
  57. 41
    0
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_comments.json
  58. 0
    58
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_snippet.json
  59. 7
    1
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/load_additional_fields.json
  60. 24
    0
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/response_contains_all_fields_except_additional_fields.json
  61. 6
    6
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/sort_by_updated_at.json
  62. 2
    0
      sonar-plugin-api/src/main/java/org/sonar/api/issue/action/Actions.java
  63. 538
    348
      sonar-ws/src/main/gen-java/org/sonarqube/ws/Common.java
  64. 7814
    3974
      sonar-ws/src/main/gen-java/org/sonarqube/ws/Issues.java
  65. 18
    11
      sonar-ws/src/main/protobuf/ws-common.proto
  66. 66
    19
      sonar-ws/src/main/protobuf/ws-issues.proto

+ 0
- 1537
server/sonar-server/src/main/gen-java/org/sonarqube/ws/Rules.java
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 10
- 7
server/sonar-server/src/main/java/org/sonar/server/es/Facets.java Datei anzeigen

@@ -28,6 +28,7 @@ import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.HasAggregations;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogram;
import org.elasticsearch.search.aggregations.bucket.missing.Missing;
@@ -38,20 +39,22 @@ public class Facets {

public static final String TOTAL = "total";

private final Map<String, LinkedHashMap<String, Long>> facetsByName = new LinkedHashMap<>();
private final LinkedHashMap<String, LinkedHashMap<String, Long>> facetsByName;

public Facets(LinkedHashMap<String, LinkedHashMap<String, Long>> facetsByName) {
this.facetsByName = facetsByName;
}

public Facets(SearchResponse response) {
if (response.getAggregations() != null) {
for (Aggregation facet : response.getAggregations()) {
this.facetsByName = new LinkedHashMap<>();
Aggregations aggregations = response.getAggregations();
if (aggregations != null) {
for (Aggregation facet : aggregations) {
processAggregation(facet);
}
}
}

public Facets(Terms terms) {
processTermsAggregation(terms);
}

private void processAggregation(Aggregation aggregation) {
if (Missing.class.isAssignableFrom(aggregation.getClass())) {
processMissingAggregation((Missing) aggregation);

+ 25
- 158
server/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java Datei anzeigen

@@ -20,29 +20,10 @@

package org.sonar.server.issue;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import java.util.Date;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.sonar.api.component.Component;
import org.sonar.api.config.Settings;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.action.Action;
import org.sonar.api.issue.action.Actions;
import org.sonar.api.issue.action.Function;
import org.sonar.api.server.ServerSide;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.property.PropertiesDao;
import org.sonar.db.property.PropertyDto;
import org.sonar.api.web.UserRole;
import org.sonar.db.issue.IssueDto;
import org.sonar.server.user.UserSession;

import static com.google.common.collect.Lists.newArrayList;
@@ -53,144 +34,30 @@ import static com.google.common.collect.Lists.newArrayList;
@ServerSide
public class ActionService {

private final DbClient dbClient;
private final IssueService issueService;
private final IssueStorage issueStorage;
private final IssueUpdater updater;
private final Settings settings;
private final PropertiesDao propertiesDao;
private final Actions actions;

public ActionService(DbClient dbClient, IssueService issueService, IssueStorage issueStorage, IssueUpdater updater, Settings settings, PropertiesDao propertiesDao,
Actions actions) {
this.dbClient = dbClient;
this.issueService = issueService;
this.issueStorage = issueStorage;
this.updater = updater;
this.settings = settings;
this.propertiesDao = propertiesDao;
this.actions = actions;
}

public List<Action> listAllActions() {
return actions.list();
}

public List<Action> listAvailableActions(String issueKey) {
DbSession session = dbClient.openSession(false);
try {
return listAvailableActions(issueService.getByKeyForUpdate(session, issueKey).toDefaultIssue());
} finally {
session.close();
}
}

public List<Action> listAvailableActions(Issue issue) {
return newArrayList(Iterables.filter(actions.list(), new SupportIssue(issue)));
}

public Issue execute(String issueKey, String actionKey, UserSession userSession) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(actionKey), "Missing action");

DbSession session = dbClient.openSession(false);
try {
DefaultIssue issue = issueService.getByKeyForUpdate(session, issueKey).toDefaultIssue();
Action action = getAction(actionKey);
if (action == null) {
throw new IllegalArgumentException("Action is not found : " + actionKey);
private final UserSession userSession;

public ActionService(UserSession userSession) {
this.userSession = userSession;
}

public List<String> listAvailableActions(IssueDto issue) {
List<String> actions = newArrayList();
String login = userSession.getLogin();
if (login != null) {
actions.add("comment");
if (issue.getResolution() == null) {
actions.add("assign");
actions.add("set_tags");
if (!login.equals(issue.getAssignee())) {
actions.add("assign_to_me");
}
actions.add("plan");
String projectUuid = issue.getProjectUuid();
if (projectUuid != null && userSession.hasProjectPermissionByUuid(UserRole.ISSUE_ADMIN, projectUuid)) {
actions.add("set_severity");
}
}
if (!action.supports(issue)) {
throw new IllegalStateException("A condition is not respected");
}

IssueChangeContext changeContext = IssueChangeContext.createUser(new Date(), userSession.getLogin());
Component project = dbClient.componentDao().selectOrFailByKey(session, issue.projectKey());
FunctionContext functionContext = new FunctionContext(issue, updater, changeContext, getProjectSettings(project));
for (Function function : action.functions()) {
function.execute(functionContext);
}
issueStorage.save(issue);
return issue;
} finally {
session.close();
}
}

@CheckForNull
private Action getAction(final String actionKey) {
return Iterables.find(actions.list(), new ActionMatchKey(actionKey), null);
}

// TODO org.sonar.server.properties.ProjectSettings should be used instead
public Settings getProjectSettings(Component project) {
Settings projectSettings = new Settings(settings);
List<PropertyDto> properties = propertiesDao.selectProjectProperties(project.key());
for (PropertyDto dto : properties) {
projectSettings.setProperty(dto.getKey(), dto.getValue());
}
return projectSettings;
}

static class FunctionContext implements Function.Context {

private final DefaultIssue issue;
private final IssueUpdater updater;
private final IssueChangeContext changeContext;
private final Settings projectSettings;

FunctionContext(DefaultIssue issue, IssueUpdater updater, IssueChangeContext changeContext, Settings projectSettings) {
this.updater = updater;
this.issue = issue;
this.changeContext = changeContext;
this.projectSettings = projectSettings;
}

@Override
public Issue issue() {
return issue;
}

@Override
public Settings projectSettings() {
return projectSettings;
}

@Override
public Function.Context setAttribute(String key, @Nullable String value) {
updater.setAttribute(issue, key, value, changeContext);
return this;
}

@Override
public Function.Context addComment(String text) {
updater.addComment(issue, text, changeContext);
return this;
}
}

private static class SupportIssue implements Predicate<Action> {
private final Issue issue;

public SupportIssue(Issue issue) {
this.issue = issue;
}

@Override
public boolean apply(@Nonnull Action action) {
return action.supports(issue);
}
}

private static class ActionMatchKey implements Predicate<Action> {
private final String key;

public ActionMatchKey(String key) {
this.key = key;
}

@Override
public boolean apply(@Nonnull Action action) {
return action.key().equals(key);
}
return actions;
}
}

+ 3
- 27
server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java Datei anzeigen

@@ -27,7 +27,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -81,7 +80,6 @@ import org.sonar.server.user.ws.UserJsonWriter;
import org.sonar.server.util.RubyUtils;
import org.sonar.server.util.Validation;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;

/**
@@ -176,10 +174,6 @@ public class InternalRubyIssueService {
return changelogService.formatDiffs(diffs);
}

public List<String> listPluginActions() {
return newArrayList(Iterables.transform(actionService.listAllActions(), ActionToKey.INSTANCE));
}

public List<DefaultIssueComment> findComments(String issueKey) {
return commentService.findComments(issueKey);
}
@@ -231,7 +225,7 @@ public class InternalRubyIssueService {
public Result<IssueComment> addComment(String issueKey, String text) {
Result<IssueComment> result = Result.of();
try {
result.set(commentService.addComment(issueKey, text, userSession));
result.set(commentService.addComment(issueKey, text));
} catch (Exception e) {
result.addError(e.getMessage());
}
@@ -239,13 +233,13 @@ public class InternalRubyIssueService {
}

public IssueComment deleteComment(String commentKey) {
return commentService.deleteComment(commentKey, userSession);
return commentService.deleteComment(commentKey);
}

public Result<IssueComment> editComment(String commentKey, String newText) {
Result<IssueComment> result = Result.of();
try {
result.set(commentService.editComment(commentKey, newText, userSession));
result.set(commentService.editComment(commentKey, newText));
} catch (Exception e) {
result.addError(e.getMessage());
}
@@ -434,24 +428,6 @@ public class InternalRubyIssueService {
return result;
}

public Result<Issue> executeAction(String issueKey, String actionKey) {
Result<Issue> result = Result.of();
try {
result.set(actionService.execute(issueKey, actionKey, userSession));
} catch (Exception e) {
result.addError(e.getMessage());
}
return result;
}

public List<Action> listActions(String issueKey) {
return actionService.listAvailableActions(issueKey);
}

public List<Action> listActions(Issue issue) {
return actionService.listAvailableActions(issue);
}

public IssueQuery emptyIssueQuery() {
return issueQueryService.createFromMap(Maps.<String, Object>newHashMap());
}

+ 12
- 10
server/sonar-server/src/main/java/org/sonar/server/issue/IssueCommentService.java Datei anzeigen

@@ -44,28 +44,26 @@ import java.util.List;

import static com.google.common.collect.Lists.newArrayList;

/**
* @since 3.6
*/
@ServerSide
public class IssueCommentService {

private final DbClient dbClient;
private final IssueService issueService;
private final IssueUpdater updater;
private final UserSession userSession;

public IssueCommentService(DbClient dbClient, IssueService issueService, IssueUpdater updater) {
public IssueCommentService(DbClient dbClient, IssueService issueService, IssueUpdater updater, UserSession userSession) {
this.dbClient = dbClient;
this.issueService = issueService;
this.updater = updater;
this.userSession = userSession;
}

public List<DefaultIssueComment> findComments(String issueKey) {
return findComments(newArrayList(issueKey));
}

public List<DefaultIssueComment> findComments(DbSession session, String issueKey) {
return findComments(session, newArrayList(issueKey));
public List<DefaultIssueComment> findComments(DbSession dbSession, String issueKey) {
return findComments(dbSession, newArrayList(issueKey));
}

public List<DefaultIssueComment> findComments(Collection<String> issueKeys) {
@@ -85,7 +83,7 @@ public class IssueCommentService {
return dbClient.issueChangeDao().selectCommentByKey(commentKey);
}

public IssueComment addComment(String issueKey, String text, UserSession userSession) {
public IssueComment addComment(String issueKey, String text) {
verifyLoggedIn(userSession);
if (StringUtils.isBlank(text)) {
throw new BadRequestException("Cannot add empty comments to an issue");
@@ -110,7 +108,7 @@ public class IssueCommentService {
}
}

public IssueComment deleteComment(String commentKey, UserSession userSession) {
public IssueComment deleteComment(String commentKey) {
DefaultIssueComment comment = dbClient.issueChangeDao().selectCommentByKey(commentKey);
if (comment == null) {
throw new NotFoundException("Comment not found: " + commentKey);
@@ -126,7 +124,7 @@ public class IssueCommentService {
return comment;
}

public IssueComment editComment(String commentKey, String text, UserSession userSession) {
public IssueComment editComment(String commentKey, String text) {
DefaultIssueComment comment = dbClient.issueChangeDao().selectCommentByKey(commentKey);
if (StringUtils.isBlank(text)) {
throw new BadRequestException("Cannot add empty comments to an issue");
@@ -149,6 +147,10 @@ public class IssueCommentService {
return comment;
}

public boolean canEditOrDelete(IssueChangeDto dto) {
return userSession.isLoggedIn() && userSession.getLogin().equals(dto.getUserLogin());
}

private void verifyLoggedIn(UserSession userSession) {
if (!userSession.isLoggedIn()) {
throw new UnauthorizedException("User is not logged in");

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java Datei anzeigen

@@ -135,7 +135,7 @@ public class IssueService {
List<Transition> allowedTransitions = new ArrayList<>();
for (Transition transition : outTransitions) {
String projectUuid = issue.projectUuid();
if (StringUtils.isBlank(transition.requiredProjectPermission()) ||
if (userSession.isLoggedIn() && StringUtils.isBlank(transition.requiredProjectPermission()) ||
(projectUuid != null && userSession.hasProjectPermissionByUuid(transition.requiredProjectPermission(), projectUuid))) {
allowedTransitions.add(transition);
}

+ 0
- 3
server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java Datei anzeigen

@@ -80,9 +80,6 @@ public class IssueActionsWriter {
if (projectUuid != null && userSession.hasProjectPermissionByUuid(UserRole.ISSUE_ADMIN, projectUuid)) {
actions.add("set_severity");
}
for (Action action : actionService.listAvailableActions(issue)) {
actions.add(action.key());
}
}
}
return actions;

+ 369
- 0
server/sonar-server/src/main/java/org/sonar/server/issue/ws/Search2Action.java Datei anzeigen

@@ -0,0 +1,369 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.ws;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.io.Resources;
import java.util.Collection;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.api.issue.Issue;
import org.sonar.api.rule.Severity;
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.server.ws.WebService.Param;
import org.sonar.api.utils.Paging;
import org.sonar.core.rule.RuleKeyFunctions;
import org.sonar.server.es.Facets;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.es.SearchResult;
import org.sonar.server.issue.IssueQuery;
import org.sonar.server.issue.IssueQueryService;
import org.sonar.server.issue.IssueService;
import org.sonar.server.issue.filter.IssueFilterParameters;
import org.sonar.server.issue.index.IssueDoc;
import org.sonar.server.issue.index.IssueIndex;
import org.sonar.server.user.UserSession;
import org.sonar.server.ws.WsUtils;
import org.sonarqube.ws.Issues;

import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Iterables.concat;
import static java.util.Collections.singletonList;

public class Search2Action implements IssuesWsAction {

private static final String INTERNAL_PARAMETER_DISCLAIMER = "This parameter is mostly used by the Issues page, please prefer usage of the componentKeys parameter. ";
public static final String ADDITIONAL_FIELDS = "additionalFields";
public static final String SEARCH_ACTION = "search2";

private final UserSession userSession;
private final IssueService service;
private final IssueQueryService issueQueryService;
private final SearchResponseLoader searchResponseLoader;
private final SearchResponseFormat searchResponseFormat;

public Search2Action(UserSession userSession, IssueService service, IssueQueryService issueQueryService,
SearchResponseLoader searchResponseLoader, SearchResponseFormat searchResponseFormat) {
this.userSession = userSession;
this.service = service;
this.issueQueryService = issueQueryService;
this.searchResponseLoader = searchResponseLoader;
this.searchResponseFormat = searchResponseFormat;
}

@Override
public void define(WebService.NewController controller) {
WebService.NewAction action = controller
.createAction(SEARCH_ACTION)
.setHandler(this)
.setDescription(
"Search for issues. Requires Browse permission on project(s)")
.setSince("3.6")
.setResponseExample(Resources.getResource(this.getClass(), "example-search.json"));

action.addPagingParams(100);
action.createParam(Param.FACETS)
.setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.")
.setPossibleValues(IssueIndex.SUPPORTED_FACETS);
action.createParam(IssueFilterParameters.FACET_MODE)
.setDefaultValue(IssueFilterParameters.FACET_MODE_COUNT)
.setDescription("Choose the returned value for facet items, either count of issues or sum of debt.")
.setPossibleValues(IssueFilterParameters.FACET_MODE_COUNT, IssueFilterParameters.FACET_MODE_DEBT);
action.addSortParams(IssueQuery.SORTS, null, true);
action.createParam(ADDITIONAL_FIELDS)
.setDescription("Comma-separated list of the optional fields to be returned in response.")
.setPossibleValues(SearchAdditionalField.possibleValues());
addComponentRelatedParams(action);
action.createParam(IssueFilterParameters.ISSUES)
.setDescription("Comma-separated list of issue keys")
.setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
action.createParam(IssueFilterParameters.SEVERITIES)
.setDescription("Comma-separated list of severities")
.setExampleValue(Severity.BLOCKER + "," + Severity.CRITICAL)
.setPossibleValues(Severity.ALL);
action.createParam(IssueFilterParameters.STATUSES)
.setDescription("Comma-separated list of statuses")
.setExampleValue(Issue.STATUS_OPEN + "," + Issue.STATUS_REOPENED)
.setPossibleValues(Issue.STATUSES);
action.createParam(IssueFilterParameters.RESOLUTIONS)
.setDescription("Comma-separated list of resolutions")
.setExampleValue(Issue.RESOLUTION_FIXED + "," + Issue.RESOLUTION_REMOVED)
.setPossibleValues(Issue.RESOLUTIONS);
action.createParam(IssueFilterParameters.RESOLVED)
.setDescription("To match resolved or unresolved issues")
.setBooleanPossibleValues();
action.createParam(IssueFilterParameters.RULES)
.setDescription("Comma-separated list of coding rule keys. Format is <repository>:<rule>")
.setExampleValue("squid:AvoidCycles");
action.createParam(IssueFilterParameters.TAGS)
.setDescription("Comma-separated list of tags.")
.setExampleValue("security,convention");
action.createParam(IssueFilterParameters.ACTION_PLANS)
.setDescription("Comma-separated list of action plan keys (not names)")
.setExampleValue("3f19de90-1521-4482-a737-a311758ff513");
action.createParam(IssueFilterParameters.PLANNED)
.setDescription("To retrieve issues associated to an action plan or not")
.setBooleanPossibleValues();
action.createParam(IssueFilterParameters.REPORTERS)
.setDescription("Comma-separated list of reporter logins")
.setExampleValue("admin");
action.createParam(IssueFilterParameters.AUTHORS)
.setDescription("Comma-separated list of SCM accounts")
.setExampleValue("torvalds@linux-foundation.org");
action.createParam(IssueFilterParameters.ASSIGNEES)
.setDescription("Comma-separated list of assignee logins. The value '__me__' can be used as a placeholder for user who performs the request")
.setExampleValue("admin,usera,__me__");
action.createParam(IssueFilterParameters.ASSIGNED)
.setDescription("To retrieve assigned or unassigned issues")
.setBooleanPossibleValues();
action.createParam(IssueFilterParameters.LANGUAGES)
.setDescription("Comma-separated list of languages. Available since 4.4")
.setExampleValue("java,js");
action.createParam(IssueFilterParameters.CREATED_AT)
.setDescription("To retrieve issues created at a given date. Format: date or datetime ISO formats")
.setExampleValue("2013-05-01 (or 2013-05-01T13:00:00+0100)");
action.createParam(IssueFilterParameters.CREATED_AFTER)
.setDescription("To retrieve issues created after the given date (exclusive). Format: date or datetime ISO formats. If this parameter is set, createdSince must not be set")
.setExampleValue("2013-05-01 (or 2013-05-01T13:00:00+0100)");
action.createParam(IssueFilterParameters.CREATED_BEFORE)
.setDescription("To retrieve issues created before the given date (exclusive). Format: date or datetime ISO formats")
.setExampleValue("2013-05-01 (or 2013-05-01T13:00:00+0100)");
action.createParam(IssueFilterParameters.CREATED_IN_LAST)
.setDescription("To retrieve issues created during a time span before the current time (exclusive). " +
"Accepted units are 'y' for year, 'm' for month, 'w' for week and 'd' for day. " +
"If this parameter is set, createdAfter must not be set")
.setExampleValue("1m2w (1 month 2 weeks)");
}

private static void addComponentRelatedParams(WebService.NewAction action) {
action.createParam(IssueFilterParameters.ON_COMPONENT_ONLY)
.setDescription("Return only issues at a component's level, not on its descendants (modules, directories, files, etc). " +
"This parameter is only considered when componentKeys or componentUuids is set. " +
"Using the deprecated componentRoots or componentRootUuids parameters will set this parameter to false. " +
"Using the deprecated components parameter will set this parameter to true.")
.setBooleanPossibleValues()
.setDefaultValue("false");

action.createParam(IssueFilterParameters.COMPONENT_KEYS)
.setDescription("To retrieve issues associated to a specific list of components sub-components (comma-separated list of component keys). " +
"A component can be a view, developer, project, module, directory or file. " +
"If this parameter is set, componentUuids must not be set.")
.setExampleValue("org.apache.struts:struts:org.apache.struts.Action");
action.createParam(IssueFilterParameters.COMPONENTS)
.setDescription("Deprecated since 5.1. If used, will have the same meaning as componentKeys AND onComponentOnly=true.");
action.createParam(IssueFilterParameters.COMPONENT_UUIDS)
.setDescription("To retrieve issues associated to a specific list of components their sub-components (comma-separated list of component UUIDs). " +
INTERNAL_PARAMETER_DISCLAIMER +
"A component can be a project, module, directory or file. " +
"If this parameter is set, componentKeys must not be set.")
.setExampleValue("584a89f2-8037-4f7b-b82c-8b45d2d63fb2");

action.createParam(IssueFilterParameters.COMPONENT_ROOTS)
.setDescription("Deprecated since 5.1. If used, will have the same meaning as componentKeys AND onComponentOnly=false.");
action.createParam(IssueFilterParameters.COMPONENT_ROOT_UUIDS)
.setDescription("Deprecated since 5.1. If used, will have the same meaning as componentUuids AND onComponentOnly=false.");

action.createParam(IssueFilterParameters.PROJECTS)
.setDescription("Deprecated since 5.1. See projectKeys");
action.createParam(IssueFilterParameters.PROJECT_KEYS)
.setDescription("To retrieve issues associated to a specific list of projects (comma-separated list of project keys). " +
INTERNAL_PARAMETER_DISCLAIMER +
"If this parameter is set, projectUuids must not be set.")
.setDeprecatedKey(IssueFilterParameters.PROJECTS)
.setExampleValue("org.apache.struts:struts:org.apache.struts.Action");
action.createParam(IssueFilterParameters.PROJECT_UUIDS)
.setDescription("To retrieve issues associated to a specific list of projects (comma-separated list of project UUIDs). " +
INTERNAL_PARAMETER_DISCLAIMER +
"Views are not supported. If this parameter is set, projectKeys must not be set.")
.setExampleValue("7d8749e8-3070-4903-9188-bdd82933bb92");

action.createParam(IssueFilterParameters.MODULE_UUIDS)
.setDescription("To retrieve issues associated to a specific list of modules (comma-separated list of module UUIDs). " +
INTERNAL_PARAMETER_DISCLAIMER +
"Views are not supported. If this parameter is set, moduleKeys must not be set.")
.setExampleValue("7d8749e8-3070-4903-9188-bdd82933bb92");

action.createParam(IssueFilterParameters.DIRECTORIES)
.setDescription("Since 5.1. To retrieve issues associated to a specific list of directories (comma-separated list of directory paths). " +
"This parameter is only meaningful when a module is selected. " +
INTERNAL_PARAMETER_DISCLAIMER)
.setExampleValue("src/main/java/org/sonar/server/");

action.createParam(IssueFilterParameters.FILE_UUIDS)
.setDescription("To retrieve issues associated to a specific list of files (comma-separated list of file UUIDs). " +
INTERNAL_PARAMETER_DISCLAIMER)
.setExampleValue("bdd82933-3070-4903-9188-7d8749e8bb92");
}

@Override
public final void handle(Request request, Response response) throws Exception {
// prepare the Elasticsearch request
SearchOptions options = new SearchOptions();
options.setPage(request.mandatoryParamAsInt(Param.PAGE), request.mandatoryParamAsInt(Param.PAGE_SIZE));
options.addFacets(request.paramAsStrings(Param.FACETS));
EnumSet<SearchAdditionalField> additionalFields = SearchAdditionalField.getFromRequest(request);
IssueQuery query = issueQueryService.createFromRequest(request);

// execute request
SearchResult<IssueDoc> result = service.search(query, options);
List<String> issueKeys = from(result.getDocs()).transform(IssueDocToKey.INSTANCE).toList();

// load the additional information to be returned in response
SearchResponseLoader.Collector collector = new SearchResponseLoader.Collector(additionalFields, issueKeys);
collectLoggedInUser(collector);
collectRequestParams(collector, request);
Facets facets = null;
if (!options.getFacets().isEmpty()) {
facets = result.getFacets();
// add missing values to facets. For example if assignee "john" and facet on "assignees" are requested, then
// "john" should always be listed in the facet. If it is not present, then it is added with value zero.
// This is a constraint from webapp UX.
completeFacets(facets, options, request);
collectFacets(collector, facets);
}
SearchResponseData data = searchResponseLoader.load(collector, facets);

// format response

// Filter and reorder facets according to the requested ordered names.
// Must be done after loading of data as the "hidden" facet "debt"
// can be used to get total debt.
facets = reorderFacets(facets, options.getFacets());

// FIXME allow long in Paging
Paging paging = Paging.create(options.getLimit(), options.getPage(), (int) result.getTotal());
Issues.Search responseBody = searchResponseFormat.format(additionalFields, data, paging, facets);
WsUtils.writeProtobuf(responseBody, request, response);
}

private Facets reorderFacets(@Nullable Facets facets, Collection<String> orderedNames) {
if (facets == null) {
return null;
}
LinkedHashMap<String, LinkedHashMap<String, Long>> orderedFacets = new LinkedHashMap<>();
for (String facetName : orderedNames) {
LinkedHashMap<String, Long> facet = facets.get(facetName);
if (facet != null) {
orderedFacets.put(facetName, facet);
}
}
return new Facets(orderedFacets);
}

private void completeFacets(Facets facets, SearchOptions options, Request request) {
addMandatoryValuesToFacet(facets, IssueFilterParameters.SEVERITIES, Severity.ALL);
addMandatoryValuesToFacet(facets, IssueFilterParameters.STATUSES, Issue.STATUSES);
addMandatoryValuesToFacet(facets, IssueFilterParameters.RESOLUTIONS, concat(singletonList(""), Issue.RESOLUTIONS));
addMandatoryValuesToFacet(facets, IssueFilterParameters.PROJECT_UUIDS, request.paramAsStrings(IssueFilterParameters.PROJECT_UUIDS));

List<String> assignees = Lists.newArrayList("");
List<String> assigneesFromRequest = request.paramAsStrings(IssueFilterParameters.ASSIGNEES);
if (assigneesFromRequest != null) {
assignees.addAll(assigneesFromRequest);
assignees.remove(IssueQueryService.LOGIN_MYSELF);
}
addMandatoryValuesToFacet(facets, IssueFilterParameters.ASSIGNEES, assignees);
addMandatoryValuesToFacet(facets, IssueFilterParameters.FACET_ASSIGNED_TO_ME, singletonList(userSession.getLogin()));
addMandatoryValuesToFacet(facets, IssueFilterParameters.REPORTERS, request.paramAsStrings(IssueFilterParameters.REPORTERS));
addMandatoryValuesToFacet(facets, IssueFilterParameters.RULES, request.paramAsStrings(IssueFilterParameters.RULES));
addMandatoryValuesToFacet(facets, IssueFilterParameters.LANGUAGES, request.paramAsStrings(IssueFilterParameters.LANGUAGES));
addMandatoryValuesToFacet(facets, IssueFilterParameters.TAGS, request.paramAsStrings(IssueFilterParameters.TAGS));
List<String> actionPlans = Lists.newArrayList("");
List<String> actionPlansFromRequest = request.paramAsStrings(IssueFilterParameters.ACTION_PLANS);
if (actionPlansFromRequest != null) {
actionPlans.addAll(actionPlansFromRequest);
}
addMandatoryValuesToFacet(facets, IssueFilterParameters.ACTION_PLANS, actionPlans);
addMandatoryValuesToFacet(facets, IssueFilterParameters.COMPONENT_UUIDS, request.paramAsStrings(IssueFilterParameters.COMPONENT_UUIDS));

for (String facetName : options.getFacets()) {
LinkedHashMap<String, Long> buckets = facets.get(facetName);
if (!IssueFilterParameters.FACET_ASSIGNED_TO_ME.equals(facetName)) {
if (buckets != null) {
List<String> requestParams = request.paramAsStrings(facetName);
if (requestParams != null) {
for (String param : requestParams) {
if (!buckets.containsKey(param) && !IssueQueryService.LOGIN_MYSELF.equals(param)) {
// Prevent appearance of a glitch value due to dedicated parameter for this facet
buckets.put(param, 0L);
}
}
}
}
}
}
}

private void addMandatoryValuesToFacet(Facets facets, String facetName, @Nullable Iterable<String> mandatoryValues) {
Map<String, Long> buckets = facets.get(facetName);
if (buckets != null && mandatoryValues != null) {
for (String mandatoryValue : mandatoryValues) {
if (!buckets.containsKey(mandatoryValue)) {
buckets.put(mandatoryValue, 0L);
}
}
}
}

private void collectLoggedInUser(SearchResponseLoader.Collector collector) {
if (userSession.isLoggedIn()) {
collector.add(SearchAdditionalField.USERS, userSession.getLogin());
}
}

private void collectFacets(SearchResponseLoader.Collector collector, Facets facets) {
Set<String> facetRules = facets.getBucketKeys(IssueFilterParameters.RULES);
if (facetRules != null) {
collector.addAll(SearchAdditionalField.RULES, from(facetRules).transform(RuleKeyFunctions.stringToRuleKey()));
}
collector.addProjectUuids(facets.getBucketKeys(IssueFilterParameters.PROJECT_UUIDS));
collector.addComponentUuids(facets.getBucketKeys(IssueFilterParameters.COMPONENT_UUIDS));
collector.addComponentUuids(facets.getBucketKeys(IssueFilterParameters.FILE_UUIDS));
collector.addComponentUuids(facets.getBucketKeys(IssueFilterParameters.MODULE_UUIDS));
collector.addAll(SearchAdditionalField.USERS, facets.getBucketKeys(IssueFilterParameters.ASSIGNEES));
collector.addAll(SearchAdditionalField.USERS, facets.getBucketKeys(IssueFilterParameters.REPORTERS));
collector.addAll(SearchAdditionalField.ACTION_PLANS, facets.getBucketKeys(IssueFilterParameters.ACTION_PLANS));
}

private void collectRequestParams(SearchResponseLoader.Collector collector, Request request) {
collector.addProjectUuids(request.paramAsStrings(IssueFilterParameters.PROJECT_UUIDS));
collector.addComponentUuids(request.paramAsStrings(IssueFilterParameters.FILE_UUIDS));
collector.addComponentUuids(request.paramAsStrings(IssueFilterParameters.MODULE_UUIDS));
collector.addComponentUuids(request.paramAsStrings(IssueFilterParameters.COMPONENT_ROOT_UUIDS));
collector.addAll(SearchAdditionalField.USERS, request.paramAsStrings(IssueFilterParameters.ASSIGNEES));
collector.addAll(SearchAdditionalField.USERS, request.paramAsStrings(IssueFilterParameters.REPORTERS));
collector.addAll(SearchAdditionalField.ACTION_PLANS, request.paramAsStrings(IssueFilterParameters.ACTION_PLANS));
}

private enum IssueDocToKey implements Function<IssueDoc, String> {
INSTANCE;
@Override
public String apply(IssueDoc input) {
return input.key();
}
}
}

+ 1
- 5
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java Datei anzeigen

@@ -273,7 +273,7 @@ public class SearchAction implements IssuesWsAction {
options.addFacets(request.paramAsStrings(WebService.Param.FACETS));

IssueQuery query = issueQueryService.createFromRequest(request);
SearchResult<IssueDoc> result = execute(query, options);
SearchResult<IssueDoc> result = service.search(query, options);

JsonWriter json = response.newJsonWriter().beginObject();
options.writeJson(json, result.getTotal());
@@ -286,10 +286,6 @@ public class SearchAction implements IssuesWsAction {
json.endObject().close();
}

private SearchResult<IssueDoc> execute(IssueQuery query, SearchOptions options) {
return service.search(query, options);
}

private void writeResponse(Request request, SearchResult<IssueDoc> result, JsonWriter json) {
Map<String, Long> debtFacet = result.getFacets().get(IssueIndex.DEBT_AGGREGATION_NAME);
if (debtFacet != null) {

+ 83
- 0
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAdditionalField.java Datei anzeigen

@@ -0,0 +1,83 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.ws;

import com.google.common.collect.Lists;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.CheckForNull;
import org.sonar.api.server.ws.Request;

public enum SearchAdditionalField {

ACTIONS("actions"),
ACTION_PLANS("actionPlans"),
COMMENTS("comments"),
LANGUAGES("languages"),
RULES("rules"),
TRANSITIONS("transitions"),
USERS("users");

public static final String ALL_ALIAS = "_all";
public static final EnumSet<SearchAdditionalField> ALL_ADDITIONAL_FIELDS = EnumSet.allOf(SearchAdditionalField.class);

private final String label;

SearchAdditionalField(String label) {
this.label = label;
}

private static final Map<String, SearchAdditionalField> BY_LABELS = new HashMap<>();

static {
for (SearchAdditionalField f : values()) {
BY_LABELS.put(f.label, f);
}
}

@CheckForNull
public static SearchAdditionalField findByLabel(String label) {
return BY_LABELS.get(label);
}

public static Collection<String> possibleValues() {
List<String> possibles = Lists.newArrayList(ALL_ALIAS);
possibles.addAll(BY_LABELS.keySet());
return possibles;
}

public static EnumSet<SearchAdditionalField> getFromRequest(Request request) {
List<String> labels = request.paramAsStrings(Search2Action.ADDITIONAL_FIELDS);
if (labels == null) {
return EnumSet.noneOf(SearchAdditionalField.class);
}
EnumSet<SearchAdditionalField> fields = EnumSet.noneOf(SearchAdditionalField.class);
for (String label : labels) {
if (label.equals(ALL_ALIAS)) {
return EnumSet.allOf(SearchAdditionalField.class);
}
fields.add(findByLabel(label));
}
return fields;
}
}

+ 167
- 0
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseData.java Datei anzeigen

@@ -0,0 +1,167 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.ws;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.core.issue.workflow.Transition;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.ActionPlanDto;
import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.user.UserDto;

import static com.google.common.base.Preconditions.checkNotNull;

/**
* All the data required to write response of api/issues/search
*/
public class SearchResponseData {

private final List<IssueDto> issues;

private Long debtTotal = null;
private List<UserDto> users = null;
private List<RuleDto> rules = null;
private List<ActionPlanDto> actionPlans = null;
private final Map<String, ComponentDto> componentsByUuid = new HashMap<>();
private final ListMultimap<String, IssueChangeDto> commentsByIssueKey = ArrayListMultimap.create();
private final ListMultimap<String, String> actionsByIssueKey = ArrayListMultimap.create();
private final ListMultimap<String, Transition> transitionsByIssueKey = ArrayListMultimap.create();
private final Set<String> updatableComments = new HashSet<>();

public SearchResponseData(List<IssueDto> issues) {
checkNotNull(issues);
this.issues = issues;
}

public List<IssueDto> getIssues() {
return issues;
}

@CheckForNull
public Collection<ComponentDto> getComponents() {
return componentsByUuid.values();
}

@CheckForNull
public ComponentDto getComponentByUuid(String uuid) {
return componentsByUuid.get(uuid);
}

@CheckForNull
public List<UserDto> getUsers() {
return users;
}

@CheckForNull
public List<RuleDto> getRules() {
return rules;
}

@CheckForNull
public List<ActionPlanDto> getActionPlans() {
return actionPlans;
}

@CheckForNull
public List<IssueChangeDto> getCommentsForIssueKey(String issueKey) {
if (commentsByIssueKey.containsKey(issueKey)) {
return commentsByIssueKey.get(issueKey);
}
return null;
}

@CheckForNull
public List<String> getActionsForIssueKey(String issueKey) {
if (actionsByIssueKey.containsKey(issueKey)) {
return actionsByIssueKey.get(issueKey);
}
return null;
}

@CheckForNull
public List<Transition> getTransitionsForIssueKey(String issueKey) {
if (transitionsByIssueKey.containsKey(issueKey)) {
return transitionsByIssueKey.get(issueKey);
}
return null;
}

public void setUsers(@Nullable List<UserDto> users) {
this.users = users;
}

public void setRules(@Nullable List<RuleDto> rules) {
this.rules = rules;
}

public void setActionPlans(@Nullable List<ActionPlanDto> actionPlans) {
this.actionPlans = actionPlans;
}

public void setComments(@Nullable List<IssueChangeDto> comments) {
for (IssueChangeDto comment : comments) {
commentsByIssueKey.put(comment.getIssueKey(), comment);
}
}

public void addComponents(@Nullable Collection<ComponentDto> dtos) {
if (dtos != null) {
for (ComponentDto dto : dtos) {
componentsByUuid.put(dto.uuid(), dto);
}
}
}

public void addActions(String issueKey, List<String> actions) {
actionsByIssueKey.putAll(issueKey, actions);
}

public void addTransitions(String issueKey, List<Transition> transitions) {
transitionsByIssueKey.putAll(issueKey, transitions);
}

public void addUpdatableComment(String commentKey) {
updatableComments.add(commentKey);
}

public boolean isUpdatableComment(String commentKey) {
return updatableComments.contains(commentKey);
}

@CheckForNull
public Long getDebtTotal() {
return debtTotal;
}

public void setDebtTotal(@Nullable Long debtTotal) {
this.debtTotal = debtTotal;
}
}

+ 291
- 0
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java Datei anzeigen

@@ -0,0 +1,291 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.ws;

import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Languages;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.Durations;
import org.sonar.api.utils.Paging;
import org.sonar.core.issue.workflow.Transition;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.ActionPlanDto;
import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.user.UserDto;
import org.sonar.markdown.Markdown;
import org.sonar.server.es.Facets;
import org.sonar.server.ws.WsResponseCommonFormat;
import org.sonarqube.ws.Common;
import org.sonarqube.ws.Issues;

import static com.google.common.base.Strings.nullToEmpty;

public class SearchResponseFormat {

private final Durations durations;
private final WsResponseCommonFormat commonFormat;
private final Languages languages;

public SearchResponseFormat(Durations durations, WsResponseCommonFormat commonFormat, Languages languages) {
this.durations = durations;
this.commonFormat = commonFormat;
this.languages = languages;
}

public Issues.Search format(Set<SearchAdditionalField> fields, SearchResponseData data,
@Nullable Paging paging, @Nullable Facets facets) {
Issues.Search.Builder response = Issues.Search.newBuilder();

if (paging != null) {
formatPaging(paging, response);
}
formatDebtTotal(data, response);
formatIssues(fields, data, response);
formatComponents(data, response);
if (facets != null) {
formatFacets(facets, response);
}
if (fields.contains(SearchAdditionalField.RULES)) {
formatRules(data, response);
}
if (fields.contains(SearchAdditionalField.USERS)) {
formatUsers(data, response);
}
if (fields.contains(SearchAdditionalField.ACTION_PLANS)) {
formatActionPlans(data, response);
}
if (fields.contains(SearchAdditionalField.LANGUAGES)) {
formatLanguages(response);
}
return response.build();
}

private void formatDebtTotal(SearchResponseData data, Issues.Search.Builder response) {
Long debt = data.getDebtTotal();
if (debt != null) {
response.setDebtTotal(debt);
}
}

private void formatPaging(Paging paging, Issues.Search.Builder response) {
response.setP(paging.pageIndex());
response.setPs(paging.pageSize());
response.setTotal(paging.total());
response.setPaging(commonFormat.formatPaging(paging));
}

private void formatIssues(Set<SearchAdditionalField> fields, SearchResponseData data, Issues.Search.Builder response) {
Issues.Issue.Builder issueBuilder = Issues.Issue.newBuilder();
for (IssueDto dto : data.getIssues()) {
issueBuilder.clear();
formatIssue(issueBuilder, dto, data);
if (fields.contains(SearchAdditionalField.ACTIONS)) {
formatIssueActions(data, issueBuilder, dto);
}
if (fields.contains(SearchAdditionalField.TRANSITIONS)) {
formatIssueTransitions(data, issueBuilder, dto);
}
if (fields.contains(SearchAdditionalField.COMMENTS)) {
formatIssueComments(data, issueBuilder, dto);
}
// TODO attributes
response.addIssues(issueBuilder.build());
}
}

private void formatIssue(Issues.Issue.Builder issueBuilder, IssueDto dto, SearchResponseData data) {
issueBuilder.setKey(dto.getKey());
ComponentDto component = data.getComponentByUuid(dto.getComponentUuid());
issueBuilder.setComponent(dto.getComponentUuid());
// Only used for the compatibility with the Issues Java WS Client <= 4.4 used by Eclipse
issueBuilder.setComponentId(component.getId());

ComponentDto project = data.getComponentByUuid(dto.getProjectUuid());
if (project != null) {
issueBuilder.setProject(project.uuid());
}
issueBuilder.setRule(dto.getRuleKey().toString());
issueBuilder.setSeverity(Common.Severity.valueOf(dto.getSeverity()));
issueBuilder.setAssignee(nullToEmpty(dto.getAssignee()));
issueBuilder.setReporter(nullToEmpty(dto.getReporter()));
issueBuilder.setResolution(nullToEmpty(dto.getResolution()));
issueBuilder.setStatus(nullToEmpty(dto.getStatus()));
issueBuilder.setActionPlan(nullToEmpty(dto.getActionPlanKey()));
issueBuilder.setMessage(nullToEmpty(dto.getMessage()));
issueBuilder.addAllTags(dto.getTags());
Long debt = dto.getDebt();
if (debt != null) {
issueBuilder.setDebt(durations.encode(Duration.create(debt)));
}
Integer line = dto.getLine();
if (line != null) {
issueBuilder.setLine(line);
}
issueBuilder.setAuthor(nullToEmpty(dto.getAuthorLogin()));
Date date = dto.getIssueCreationDate();
if (date != null) {
issueBuilder.setCreationDate(DateUtils.formatDateTime(date));
}
date = dto.getIssueUpdateDate();
if (date != null) {
issueBuilder.setUpdateDate(DateUtils.formatDateTime(date));
}
date = dto.getIssueCloseDate();
if (date != null) {
issueBuilder.setCloseDate(DateUtils.formatDateTime(date));
}
}

private void formatIssueTransitions(SearchResponseData data, Issues.Issue.Builder issueBuilder, IssueDto dto) {
issueBuilder.setTransitionsPresentIfEmpty(true);
List<Transition> transitions = data.getTransitionsForIssueKey(dto.getKey());
if (transitions != null) {
for (Transition transition : transitions) {
issueBuilder.addTransitions(transition.key());
}
}
}

private void formatIssueActions(SearchResponseData data, Issues.Issue.Builder issueBuilder, IssueDto dto) {
issueBuilder.setActionsPresentIfEmpty(true);
List<String> actions = data.getActionsForIssueKey(dto.getKey());
if (actions != null) {
issueBuilder.addAllActions(actions);
}
}

private void formatIssueComments(SearchResponseData data, Issues.Issue.Builder issueBuilder, IssueDto dto) {
issueBuilder.setCommentsPresentIfEmpty(true);
List<IssueChangeDto> comments = data.getCommentsForIssueKey(dto.getKey());
if (comments != null) {
Issues.Comment.Builder commentBuilder = Issues.Comment.newBuilder();
for (IssueChangeDto comment : comments) {
String markdown = comment.getChangeData();
commentBuilder
.clear()
.setKey(comment.getKey())
.setLogin(nullToEmpty(comment.getUserLogin()))
.setUpdatable(data.isUpdatableComment(comment.getKey()))
.setCreatedAt(DateUtils.formatDateTime(new Date(comment.getCreatedAt())));
if (markdown != null) {
commentBuilder
.setHtmlText(Markdown.convertToHtml(markdown))
.setMarkdown(markdown);
}
issueBuilder.addComments(commentBuilder.build());
}
}
}

private void formatRules(SearchResponseData data, Issues.Search.Builder response) {
response.setRulesPresentIfEmpty(true);
List<RuleDto> rules = data.getRules();
if (rules != null) {
for (RuleDto rule : rules) {
response.addRules(commonFormat.formatRule(rule));
}
}
}

private void formatComponents(SearchResponseData data, Issues.Search.Builder response) {
response.setComponentsPresentIfEmpty(true);
Collection<ComponentDto> components = data.getComponents();
if (components != null) {
for (ComponentDto dto : components) {
response.addComponents(commonFormat.formatComponent(dto));
}
}
}

private void formatUsers(SearchResponseData data, Issues.Search.Builder response) {
response.setUsersPresentIfEmpty(true);
List<UserDto> users = data.getUsers();
if (users != null) {
for (UserDto user : users) {
response.addUsers(commonFormat.formatUser(user));
}
}
}

private void formatActionPlans(SearchResponseData data, Issues.Search.Builder response) {
response.setActionPlansPresentIfEmpty(true);
List<ActionPlanDto> actionPlans = data.getActionPlans();
if (actionPlans != null) {
Issues.ActionPlan.Builder planBuilder = Issues.ActionPlan.newBuilder();
for (ActionPlanDto actionPlan : actionPlans) {
planBuilder
.clear()
.setKey(actionPlan.getKey())
.setName(nullToEmpty(actionPlan.getName()))
.setStatus(nullToEmpty(actionPlan.getStatus()))
.setProject(nullToEmpty(actionPlan.getProjectUuid()));
Date deadLine = actionPlan.getDeadLine();
if (deadLine != null) {
planBuilder.setDeadLine(DateUtils.formatDateTime(deadLine));
}
response.addActionPlans(planBuilder.build());
}
}
}

private void formatLanguages(Issues.Search.Builder response) {
response.setLanguagesPresentIfEmpty(true);
Issues.Language.Builder builder = Issues.Language.newBuilder();
for (Language lang : languages.all()) {
builder
.clear()
.setKey(lang.getKey())
.setName(lang.getName());
response.addLanguages(builder.build());
}
}

private void formatFacets(Facets facets, Issues.Search.Builder response) {
response.setFacetsPresentIfEmpty(true);
Common.Facet.Builder facetBuilder = Common.Facet.newBuilder();
for (Map.Entry<String, LinkedHashMap<String, Long>> facet : facets.getAll().entrySet()) {
facetBuilder.clear();
facetBuilder.setProperty(facet.getKey());
LinkedHashMap<String, Long> buckets = facet.getValue();
if (buckets != null) {
for (Map.Entry<String, Long> bucket : buckets.entrySet()) {
Common.FacetValue.Builder valueBuilder = facetBuilder.addValuesBuilder();
valueBuilder.setVal(bucket.getKey());
valueBuilder.setCount(bucket.getValue());
valueBuilder.build();
}
} else {
facetBuilder.addAllValues(Collections.<Common.FacetValue>emptyList());
}
response.addFacets(facetBuilder.build());
}
}
}

+ 241
- 0
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java Datei anzeigen

@@ -0,0 +1,241 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.ws;

import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.api.rule.RuleKey;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.server.es.Facets;
import org.sonar.server.issue.ActionService;
import org.sonar.server.issue.IssueCommentService;
import org.sonar.server.issue.IssueService;
import org.sonar.server.issue.index.IssueIndex;

import static com.google.common.collect.Lists.newArrayList;
import static org.sonar.server.issue.ws.SearchAdditionalField.ACTIONS;
import static org.sonar.server.issue.ws.SearchAdditionalField.ACTION_PLANS;
import static org.sonar.server.issue.ws.SearchAdditionalField.COMMENTS;
import static org.sonar.server.issue.ws.SearchAdditionalField.RULES;
import static org.sonar.server.issue.ws.SearchAdditionalField.TRANSITIONS;
import static org.sonar.server.issue.ws.SearchAdditionalField.USERS;

/**
* Loads all the information required for the response of api/issues/search.
*/
public class SearchResponseLoader {

private final DbClient dbClient;
private final IssueService issueService;
private final ActionService actionService;
private final IssueCommentService commentService;

public SearchResponseLoader(DbClient dbClient, IssueService issueService, ActionService actionService, IssueCommentService commentService) {
this.dbClient = dbClient;
this.issueService = issueService;
this.actionService = actionService;
this.commentService = commentService;
}

/**
* The issue keys are given by the multi-criteria search in Elasticsearch index.
*/
public SearchResponseData load(Collector collector, @Nullable Facets facets) {
DbSession dbSession = dbClient.openSession(false);
try {
SearchResponseData result = new SearchResponseData(dbClient.issueDao().selectByOrderedKeys(dbSession, collector.getIssueKeys()));
collector.collect(result.getIssues());

loadRules(collector, dbSession, result);
// order is important - loading of comments complete the list of users: loadComments() is
// before loadUsers()
loadComments(collector, dbSession, result);
loadUsers(collector, dbSession, result);
loadActionPlans(collector, dbSession, result);
loadComponents(collector, dbSession, result);
loadActionsAndTransitions(collector, result);
completeTotalDebtFromFacet(facets, result);
return result;
} finally {
dbClient.closeSession(dbSession);
}
}

private void loadUsers(Collector collector, DbSession dbSession, SearchResponseData result) {
if (collector.contains(USERS)) {
result.setUsers(dbClient.userDao().selectByLogins(dbSession, collector.<String>get(USERS)));
}
}

private void loadComments(Collector collector, DbSession dbSession, SearchResponseData result) {
if (collector.contains(COMMENTS)) {
List<IssueChangeDto> comments = dbClient.issueChangeDao().selectByTypeAndIssueKeys(dbSession, collector.getIssueKeys(), IssueChangeDto.TYPE_COMMENT);
result.setComments(comments);
for (IssueChangeDto comment : comments) {
collector.add(USERS, comment.getUserLogin());
if (commentService.canEditOrDelete(comment)) {
result.addUpdatableComment(comment.getKey());
}
}
}
}

private void loadActionPlans(Collector collector, DbSession dbSession, SearchResponseData result) {
if (collector.contains(ACTION_PLANS)) {
result.setActionPlans(dbClient.actionPlanDao().selectByKeys(dbSession, collector.<String>get(ACTION_PLANS)));
}
}

private void loadRules(Collector collector, DbSession dbSession, SearchResponseData result) {
if (collector.contains(RULES)) {
result.setRules(dbClient.ruleDao().selectByKeys(dbSession, collector.<RuleKey>get(RULES)));
}
}

private void loadComponents(Collector collector, DbSession dbSession, SearchResponseData result) {
// always load components and projects, because some issue fields still relate to component ids/keys.
// They should be dropped but are kept for backward-compatibility (see SearchResponseFormat)
result.addComponents(dbClient.componentDao().selectByUuids(dbSession, collector.getComponentUuids()));
result.addComponents(dbClient.componentDao().selectSubProjectsByComponentUuids(dbSession, collector.getComponentUuids()));
for (ComponentDto component : result.getComponents()) {
collector.addProjectUuid(component.projectUuid());
}
List<ComponentDto> projects = dbClient.componentDao().selectByUuids(dbSession, collector.getProjectUuids());
result.addComponents(projects);
}

private void loadActionsAndTransitions(Collector collector, SearchResponseData result) {
if (collector.contains(ACTIONS) || collector.contains(TRANSITIONS)) {
for (IssueDto dto : result.getIssues()) {
// so that IssueDto can be used.
if (collector.contains(ACTIONS)) {
result.addActions(dto.getKey(), actionService.listAvailableActions(dto));
}
if (collector.contains(TRANSITIONS)) {
// TODO workflow and action engines must not depend on org.sonar.api.issue.Issue but on a generic interface
DefaultIssue issue = dto.toDefaultIssue();
result.addTransitions(issue.key(), issueService.listTransitions(issue));
}
}
}
}

private void completeTotalDebtFromFacet(@Nullable Facets facets, SearchResponseData result) {
if (facets != null) {
Map<String, Long> debtFacet = facets.get(IssueIndex.DEBT_AGGREGATION_NAME);
if (debtFacet != null) {
result.setDebtTotal(debtFacet.get(Facets.TOTAL));
}
}
}

/**
* Collects the keys of all the data to be loaded (users, rules, ...)
*/
public static class Collector {
private final EnumSet<SearchAdditionalField> fields;
private final SetMultimap<SearchAdditionalField, Object> fieldValues = MultimapBuilder.enumKeys(SearchAdditionalField.class).hashSetValues().build();
private final Set<String> componentUuids = new HashSet<>();
private final Set<String> projectUuids = new HashSet<>();
private final List<String> issueKeys;

public Collector(EnumSet<SearchAdditionalField> fields, List<String> issueKeys) {
this.fields = fields;
this.issueKeys = issueKeys;
}

void collect(List<IssueDto> issues) {
for (IssueDto issue : issues) {
componentUuids.add(issue.getComponentUuid());
projectUuids.add(issue.getProjectUuid());
add(ACTION_PLANS, issue.getActionPlanKey());
add(RULES, issue.getRuleKey());
add(USERS, issue.getReporter());
add(USERS, issue.getAssignee());
}
}

public void add(SearchAdditionalField key, @Nullable Object value) {
if (value != null) {
fieldValues.put(key, value);
}
}

public void addComponentUuid(String uuid) {
this.componentUuids.add(uuid);
}

public void addComponentUuids(@Nullable Collection<String> uuids) {
if (uuids != null) {
this.componentUuids.addAll(uuids);
}
}

public void addProjectUuid(String uuid) {
this.projectUuids.add(uuid);
}

public void addProjectUuids(@Nullable Collection<String> uuids) {
if (uuids != null) {
this.projectUuids.addAll(uuids);
}
}

public void addAll(SearchAdditionalField key, @Nullable Iterable values) {
if (values != null) {
for (Object value : values) {
add(key, value);
}
}
}

<T> List<T> get(SearchAdditionalField key) {
return newArrayList((Set<T>) fieldValues.get(key));
}

boolean contains(SearchAdditionalField field) {
return fields.contains(field);
}

public List<String> getIssueKeys() {
return issueKeys;
}

public Set<String> getComponentUuids() {
return componentUuids;
}

public Set<String> getProjectUuids() {
return projectUuids;
}
}
}

+ 7
- 0
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java Datei anzeigen

@@ -158,6 +158,8 @@ import org.sonar.server.issue.ws.IssueActionsWriter;
import org.sonar.server.issue.ws.IssueComponentHelper;
import org.sonar.server.issue.ws.IssueJsonWriter;
import org.sonar.server.issue.ws.IssuesWs;
import org.sonar.server.issue.ws.SearchResponseFormat;
import org.sonar.server.issue.ws.SearchResponseLoader;
import org.sonar.server.issue.ws.SetTagsAction;
import org.sonar.server.language.ws.LanguageWs;
import org.sonar.server.measure.MeasureFilterEngine;
@@ -321,6 +323,7 @@ import org.sonar.server.view.index.ViewIndexDefinition;
import org.sonar.server.view.index.ViewIndexer;
import org.sonar.server.ws.ListingWs;
import org.sonar.server.ws.WebServiceEngine;
import org.sonar.server.ws.WsResponseCommonFormat;

public class PlatformLevel4 extends PlatformLevel {

@@ -602,8 +605,12 @@ public class PlatformLevel4 extends PlatformLevel {
IssuesWs.class,
IssueJsonWriter.class,
IssueComponentHelper.class,
WsResponseCommonFormat.class,
SearchResponseLoader.class,
SearchResponseFormat.class,
org.sonar.server.issue.ws.ShowAction.class,
org.sonar.server.issue.ws.SearchAction.class,
org.sonar.server.issue.ws.Search2Action.class,
org.sonar.server.issue.ws.TagsAction.class,
SetTagsAction.class,
ComponentTagsAction.class,

+ 5
- 5
server/sonar-server/src/main/java/org/sonar/server/rule/ws/ListAction.java Datei anzeigen

@@ -31,6 +31,8 @@ import org.sonar.db.rule.RuleDto;
import org.sonar.server.plugins.MimeTypes;
import org.sonarqube.ws.Rules.ListResponse;

import static com.google.common.base.Strings.nullToEmpty;

public class ListAction implements RulesWsAction {

private final DbClient dbClient;
@@ -62,11 +64,9 @@ public class ListAction implements RulesWsAction {
ruleBuilder
.clear()
.setRepository(dto.getRepositoryKey())
.setKey(dto.getRuleKey());
String internalKey = dto.getConfigKey();
if (!Strings.isNullOrEmpty(internalKey)) {
ruleBuilder.setInternalKey(internalKey);
}
.setKey(dto.getRuleKey())
.setName(nullToEmpty(dto.getName()))
.setInternalKey(nullToEmpty(dto.getConfigKey()));
listResponseBuilder.addRules(ruleBuilder.build());
}
});

+ 111
- 0
server/sonar-server/src/main/java/org/sonar/server/ws/WsResponseCommonFormat.java Datei anzeigen

@@ -0,0 +1,111 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.ws;

import com.google.common.base.Strings;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Languages;
import org.sonar.api.utils.Paging;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.user.UserDto;
import org.sonar.markdown.Markdown;
import org.sonarqube.ws.Common;

import static com.google.common.base.Strings.nullToEmpty;
import static java.lang.String.format;

public class WsResponseCommonFormat {

private final Languages languages;

public WsResponseCommonFormat(Languages languages) {
this.languages = languages;
}

public Common.Paging.Builder formatPaging(Paging paging) {
return Common.Paging.newBuilder()
.setPageIndex(paging.pageIndex())
.setPages(paging.pages())
.setPageSize(paging.pageSize())
.setTotal(paging.total());
}

public Common.Rule.Builder formatRule(RuleDto rule) {
Common.Rule.Builder builder = Common.Rule.newBuilder()
.setKey(rule.getKey().toString())
.setDesc(nullToEmpty(rule.getDescription()))
.setStatus(Common.RuleStatus.valueOf(rule.getStatus().name()));

builder.setLang(nullToEmpty(rule.getLanguage()));
Language lang = languages.get(rule.getLanguage());
if (lang != null) {
builder.setLangName(lang.getName());
}

String desc = rule.getDescription();
if (desc != null) {
switch (rule.getDescriptionFormat()) {
case HTML:
builder.setDesc(desc);
break;
case MARKDOWN:
builder.setDesc(Markdown.convertToHtml(desc));
break;
default:
throw new IllegalArgumentException(format("Unknown description format '%s' on rule '%s'", rule.getDescriptionFormat(), rule.getKey()));
}
}
return builder;
}

public Common.Component.Builder formatComponent(ComponentDto dto) {
Common.Component.Builder builder = Common.Component.newBuilder()
.setId(dto.uuid())
.setKey(dto.key())
.setQualifier(dto.qualifier())
.setName(nullToEmpty(dto.name()))
.setLongName(nullToEmpty(dto.longName()))
.setEnabled(dto.isEnabled());
String path = dto.path();
// path is not applicable to the components that are not files.
// Value must not be "" in this case.
if (!Strings.isNullOrEmpty(path)) {
builder.setPath(path);
}

// On a root project, parentProjectId is null but projectId is equal to itself, which make no sense.
if (dto.projectUuid() != null && dto.parentProjectId() != null) {
builder.setProject(dto.projectUuid());
}
if (dto.parentProjectId() != null) {
builder.setSubProject(dto.moduleUuid());
}
return builder;
}

public Common.User.Builder formatUser(UserDto user) {
return Common.User.newBuilder()
.setLogin(user.getLogin())
.setName(nullToEmpty(user.getName()))
.setEmail(nullToEmpty(user.getEmail()))
.setActive(user.isActive());
}
}

+ 54
- 0
server/sonar-server/src/main/java/org/sonar/server/ws/WsUtils.java Datei anzeigen

@@ -0,0 +1,54 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.ws;

import com.google.protobuf.Message;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import org.apache.commons.io.IOUtils;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.core.util.ProtobufJsonFormat;
import org.sonar.server.plugins.MimeTypes;

public class WsUtils {

private WsUtils() {
// only statics
}

public static void writeProtobuf(Message msg, Request request, Response response) throws Exception {
OutputStream output = response.stream().output();
try {
if (request.getMediaType().equals(MimeTypes.PROTOBUF)) {
response.stream().setMediaType(MimeTypes.PROTOBUF);
msg.writeTo(output);
} else {
response.stream().setMediaType(MimeTypes.JSON);
try (OutputStreamWriter writer = new OutputStreamWriter(output)) {
ProtobufJsonFormat.write(msg, JsonWriter.of(writer));
}
}
} finally {
IOUtils.closeQuietly(output);
}
}
}

+ 0
- 39
server/sonar-server/src/main/protobuf/ws-rules.proto Datei anzeigen

@@ -1,39 +0,0 @@
// SonarQube, open source software quality management tool.
// Copyright (C) 2008-2015 SonarSource
// mailto:contact AT sonarsource DOT com
//
// SonarQube 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.
//
// SonarQube 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.

syntax = "proto2";

package sonarqube.ws.rules;

option java_package = "org.sonarqube.ws";
option java_outer_classname = "Rules";
option optimize_for = SPEED;

// WS api/rules/list for internal use only
message ListResponse {

message Rule {
optional string repository = 1;
optional string key = 2;
optional string internal_key = 3;
optional string name = 4;
}

repeated Rule rules = 1;

}

+ 0
- 243
server/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java Datei anzeigen

@@ -1,243 +0,0 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.List;
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.component.Component;
import org.sonar.api.config.Settings;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.action.Actions;
import org.sonar.api.issue.action.Function;
import org.sonar.api.issue.condition.Condition;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.property.PropertiesDao;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.rule.RuleTesting;
import org.sonar.server.component.ComponentTesting;
import org.sonar.server.user.ThreadLocalUserSession;
import org.sonar.server.user.UserSession;

import static com.google.common.collect.Lists.newArrayList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

public class ActionServiceTest {

DbClient dbClient;
DbSession session;

ComponentDao componentDao;
IssueService issueService;
IssueStorage issueStorage;
IssueUpdater updater;
PropertiesDao propertiesDao;
Settings settings;
Actions actions;
ActionService actionService;

IssueDto issue;

@Before
public void before() {
dbClient = mock(DbClient.class);
session = mock(DbSession.class);
when(dbClient.openSession(false)).thenReturn(session);

componentDao = mock(ComponentDao.class);
when(dbClient.componentDao()).thenReturn(componentDao);
issueService = mock(IssueService.class);
issueStorage = mock(IssueStorage.class);
updater = mock(IssueUpdater.class);
propertiesDao = mock(PropertiesDao.class);
settings = new Settings();
actions = new Actions();

ComponentDto project = ComponentTesting.newProjectDto();
issue = IssueTesting.newDto(RuleTesting.newXooX1().setId(10), ComponentTesting.newFileDto(project), project).setKee("ABCD");

actionService = new ActionService(dbClient, issueService, issueStorage, updater, settings, propertiesDao, actions);
}

@Test
public void execute_functions() {
Function function1 = mock(Function.class);
Function function2 = mock(Function.class);

when(componentDao.selectOrFailByKey(eq(session), anyString())).thenReturn(mock(ComponentDto.class));
when(issueService.getByKeyForUpdate(session, "ABCD")).thenReturn(issue);

actions.add("link-to-jira").setConditions(new AlwaysMatch()).setFunctions(function1, function2);

assertThat(actionService.execute("ABCD", "link-to-jira", mock(ThreadLocalUserSession.class))).isNotNull();

verify(function1).execute(any(Function.Context.class));
verify(function2).execute(any(Function.Context.class));
verifyNoMoreInteractions(function1, function2);
}

@Test
public void modify_issue_when_executing_a_function() {
Function function = new TweetFunction();

UserSession userSession = mock(ThreadLocalUserSession.class);
when(userSession.getLogin()).thenReturn("arthur");

when(componentDao.selectOrFailByKey(eq(session), anyString())).thenReturn(mock(ComponentDto.class));
when(issueService.getByKeyForUpdate(session, "ABCD")).thenReturn(issue);

actions.add("link-to-jira").setConditions(new AlwaysMatch()).setFunctions(function);
assertThat(actionService.execute("ABCD", "link-to-jira", userSession)).isNotNull();

verify(updater).addComment(any(DefaultIssue.class), eq("New tweet on issue ABCD"), any(IssueChangeContext.class));
verify(updater).setAttribute(any(DefaultIssue.class), eq("tweet"), eq("tweet sent"), any(IssueChangeContext.class));
}

@Test
public void inject_project_settings_when_executing_a_function() {
Function function = new TweetFunction();

UserSession userSession = mock(ThreadLocalUserSession.class);
when(userSession.getLogin()).thenReturn("arthur");

when(componentDao.selectOrFailByKey(session, "struts")).thenReturn(new ComponentDto().setKey("struts"));
when(issueService.getByKeyForUpdate(session, "ABCD")).thenReturn(issue.setProjectKey("struts"));

actions.add("link-to-jira").setConditions(new AlwaysMatch()).setFunctions(function);
assertThat(actionService.execute("ABCD", "link-to-jira", userSession)).isNotNull();

verify(propertiesDao).selectProjectProperties(eq("struts"));
}

@Test
public void not_execute_function_if_action_not_found() {
Function function = mock(Function.class);

when(componentDao.selectOrFailByKey(eq(session), anyString())).thenReturn(mock(ComponentDto.class));
when(issueService.getByKeyForUpdate(session, "ABCD")).thenReturn(issue);

actions.add("link-to-jira").setConditions(new AlwaysMatch()).setFunctions(function);
try {
actionService.execute("ABCD", "tweet", mock(ThreadLocalUserSession.class));
fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Action is not found : tweet");
}
verifyZeroInteractions(function);
}

@Test
public void not_execute_function_if_action_is_not_supported() {
Function function = mock(Function.class);

when(componentDao.selectOrFailByKey(eq(session), anyString())).thenReturn(mock(ComponentDto.class));
when(issueService.getByKeyForUpdate(session, "ABCD")).thenReturn(issue);

actions.add("link-to-jira").setConditions(new NeverMatch()).setFunctions(function);
try {
actionService.execute("ABCD", "link-to-jira", mock(ThreadLocalUserSession.class));
fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("A condition is not respected");
}
verifyZeroInteractions(function);
}

@Test
public void list_available_supported_actions() {
when(componentDao.selectOrFailByKey(eq(session), anyString())).thenReturn(mock(ComponentDto.class));
when(issueService.getByKeyForUpdate(session, "ABCD")).thenReturn(issue);

actions.add("link-to-jira").setConditions(new AlwaysMatch());
actions.add("tweet").setConditions(new NeverMatch());
assertThat(actionService.listAvailableActions("ABCD")).hasSize(1);
}

@Test
public void return_no_action() {
when(componentDao.selectOrFailByKey(eq(session), anyString())).thenReturn(mock(ComponentDto.class));
when(issueService.getByKeyForUpdate(session, "ABCD")).thenReturn(issue);

assertThat(actionService.listAvailableActions("ABCD")).isEmpty();
}

@Test
public void get_project_settings() {
Component project = mock(Component.class);
when(project.key()).thenReturn("struts");

// Global property
settings.appendProperty("sonar.core.version", "3.6");

// Project property
List<PropertyDto> projectProperties = newArrayList(new PropertyDto().setKey("sonar.jira.project.key").setValue("STRUTS"));
when(propertiesDao.selectProjectProperties("struts")).thenReturn(projectProperties);

Settings result = actionService.getProjectSettings(project);
assertThat(result).isNotNull();
assertThat(result.hasKey("sonar.core.version")).isTrue();
assertThat(result.hasKey("sonar.jira.project.key")).isTrue();
}

@Test
public void list_all_actions() {
actions.add("link-to-jira").setConditions(new AlwaysMatch());
assertThat(actionService.listAllActions()).hasSize(1);
}

public class AlwaysMatch implements Condition {
@Override
public boolean matches(Issue issue) {
return true;
}
}

public class NeverMatch implements Condition {
@Override
public boolean matches(Issue issue) {
return false;
}
}

public class TweetFunction implements Function {
@Override
public void execute(Context context) {
context.addComment("New tweet on issue " + context.issue().key());
context.setAttribute("tweet", "tweet sent");
}
}
}

+ 0
- 10
server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java Datei anzeigen

@@ -158,16 +158,6 @@ public class InternalRubyIssueServiceTest {
assertThat(service.listResolutions()).isEqualTo(Issue.RESOLUTIONS);
}

@Test
public void list_plugin_actions() {
Action action = mock(Action.class);
when(action.key()).thenReturn("link-to-jira");

when(actionService.listAllActions()).thenReturn(newArrayList(action));

assertThat(service.listPluginActions()).containsOnly("link-to-jira");
}

@Test
public void find_comments_by_issue_key() {
service.findComments("ABCD");

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceMediumTest.java Datei anzeigen

@@ -114,7 +114,7 @@ public class IssueCommentServiceMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

service.addComment(issue.getKey(), "my comment", userSessionRule);
service.addComment(issue.getKey(), "my comment");

List<DefaultIssueComment> comments = service.findComments(issue.getKey());
assertThat(comments).hasSize(1);
@@ -131,7 +131,7 @@ public class IssueCommentServiceMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

service.addComment(issue.getKey(), "my comment", userSessionRule);
service.addComment(issue.getKey(), "my comment");

List<DefaultIssueComment> comments = service.findComments(issue.getKey());
assertThat(comments).hasSize(1);

+ 14
- 14
server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceTest.java Datei anzeigen

@@ -88,7 +88,7 @@ public class IssueCommentServiceTest {
when(dbClient.openSession(false)).thenReturn(session);
when(dbClient.issueChangeDao()).thenReturn(changeDao);

issueCommentService = new IssueCommentService(dbClient, issueService, updater);
issueCommentService = new IssueCommentService(dbClient, issueService, updater, userSessionRule);
}

@Test
@@ -109,7 +109,7 @@ public class IssueCommentServiceTest {
when(issueService.getByKeyForUpdate(session, "ABCD")).thenReturn(issueDto);
when(issueCommentService.findComments(session, "ABCD")).thenReturn(newArrayList(new DefaultIssueComment()));

issueCommentService.addComment("ABCD", "my comment", userSessionRule);
issueCommentService.addComment("ABCD", "my comment");

verify(updater).addComment(eq(issueDto.toDefaultIssue()), eq("my comment"), any(IssueChangeContext.class));
verify(issueService).saveIssue(eq(session), eq(issueDto.toDefaultIssue()), any(IssueChangeContext.class), eq("my comment"));
@@ -120,7 +120,7 @@ public class IssueCommentServiceTest {
throwable.expect(UnauthorizedException.class);
userSessionRule.anonymous();

issueCommentService.addComment("myIssue", "my comment", userSessionRule);
issueCommentService.addComment("myIssue", "my comment");

verify(updater, never()).addComment(any(DefaultIssue.class), anyString(), any(IssueChangeContext.class));
verifyZeroInteractions(issueService);
@@ -130,7 +130,7 @@ public class IssueCommentServiceTest {
public void should_prevent_adding_empty_comment() {
throwable.expect(BadRequestException.class);

issueCommentService.addComment("myIssue", " ", userSessionRule);
issueCommentService.addComment("myIssue", " ");

verify(updater, never()).addComment(any(DefaultIssue.class), anyString(), any(IssueChangeContext.class));
verifyZeroInteractions(issueService);
@@ -140,7 +140,7 @@ public class IssueCommentServiceTest {
public void should_prevent_adding_null_comment() {
throwable.expect(BadRequestException.class);

issueCommentService.addComment("myIssue", null, userSessionRule);
issueCommentService.addComment("myIssue", null);

verify(updater, never()).addComment(any(DefaultIssue.class), anyString(), any(IssueChangeContext.class));
verifyZeroInteractions(issueService);
@@ -154,7 +154,7 @@ public class IssueCommentServiceTest {
when(issueCommentService.findComments(session, "ABCD")).thenReturn(Collections.<DefaultIssueComment>emptyList());

try {
issueCommentService.addComment("ABCD", "my comment", userSessionRule);
issueCommentService.addComment("ABCD", "my comment");
fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("Fail to add a comment on issue ABCD");
@@ -165,7 +165,7 @@ public class IssueCommentServiceTest {
public void should_delete_comment() {
when(changeDao.selectCommentByKey("ABCD")).thenReturn(new DefaultIssueComment().setUserLogin("admin").setIssueKey("EFGH"));

issueCommentService.deleteComment("ABCD", userSessionRule);
issueCommentService.deleteComment("ABCD");

verify(changeDao).delete("ABCD");
verify(issueService).getByKey("EFGH");
@@ -177,7 +177,7 @@ public class IssueCommentServiceTest {

when(changeDao.selectCommentByKey("ABCD")).thenReturn(null);

issueCommentService.deleteComment("ABCD", userSessionRule);
issueCommentService.deleteComment("ABCD");

verify(changeDao, never()).delete(anyString());
}
@@ -188,7 +188,7 @@ public class IssueCommentServiceTest {

when(changeDao.selectCommentByKey("ABCD")).thenReturn(new DefaultIssueComment().setUserLogin("julien"));

issueCommentService.deleteComment("ABCD", userSessionRule);
issueCommentService.deleteComment("ABCD");

verify(changeDao, never()).delete(anyString());
}
@@ -197,7 +197,7 @@ public class IssueCommentServiceTest {
public void should_update_comment() {
when(changeDao.selectCommentByKey("ABCD")).thenReturn(new DefaultIssueComment().setIssueKey("EFGH").setUserLogin("admin"));

issueCommentService.editComment("ABCD", "updated comment", userSessionRule);
issueCommentService.editComment("ABCD", "updated comment");

verify(changeDao).update(any(IssueChangeDto.class));
verify(issueService).getByKey("EFGH");
@@ -209,7 +209,7 @@ public class IssueCommentServiceTest {

when(changeDao.selectCommentByKey("ABCD")).thenReturn(null);

issueCommentService.editComment("ABCD", "updated comment", userSessionRule);
issueCommentService.editComment("ABCD", "updated comment");

verify(changeDao, never()).update(any(IssueChangeDto.class));
}
@@ -218,7 +218,7 @@ public class IssueCommentServiceTest {
public void should_prevent_updating_empty_comment() {
throwable.expect(BadRequestException.class);

issueCommentService.editComment("ABCD", "", userSessionRule);
issueCommentService.editComment("ABCD", "");

verify(changeDao, never()).update(any(IssueChangeDto.class));
}
@@ -227,7 +227,7 @@ public class IssueCommentServiceTest {
public void should_prevent_updating_null_comment() {
throwable.expect(BadRequestException.class);

issueCommentService.editComment("ABCD", null, userSessionRule);
issueCommentService.editComment("ABCD", null);

verify(changeDao, never()).update(any(IssueChangeDto.class));
}
@@ -238,7 +238,7 @@ public class IssueCommentServiceTest {

when(changeDao.selectCommentByKey("ABCD")).thenReturn(new DefaultIssueComment().setUserLogin("julien"));

issueCommentService.editComment("ABCD", "updated comment", userSessionRule);
issueCommentService.editComment("ABCD", "updated comment");

verify(changeDao, never()).update(any(IssueChangeDto.class));
}

+ 2
- 1
server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java Datei anzeigen

@@ -59,6 +59,7 @@ import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.issue.index.IssueDoc;
import org.sonar.server.issue.index.IssueIndex;
import org.sonar.server.issue.index.IssueIndexDefinition;
import org.sonar.server.issue.index.IssueIndexer;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionChange;
@@ -478,7 +479,7 @@ public class IssueServiceMediumTest {
ComponentDto file = newFile(project);
saveIssue(IssueTesting.newDto(rule, file, project));

List<IssueDoc> result = service.search(IssueQuery.builder(userSessionRule).build(), new SearchOptions()).getDocs();
List<IssueDoc> result = service.search(IssueQuery.builder(userSessionRule).build(), new SearchOptions().addFields(IssueIndexDefinition.FIELD_ISSUE_KEY)).getDocs();
assertThat(result).hasSize(1);
}


+ 1
- 23
server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java Datei anzeigen

@@ -28,11 +28,10 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.action.Action;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.api.web.UserRole;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.workflow.Transition;
import org.sonar.server.issue.ActionService;
import org.sonar.server.issue.IssueService;
@@ -41,7 +40,6 @@ import org.sonar.test.JsonAssert;

import static com.google.common.collect.Lists.newArrayList;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
@@ -81,26 +79,6 @@ public class IssueActionsWriterTest {
"]}");
}

@Test
public void write_plugin_actions() {
Issue issue = new DefaultIssue()
.setKey("ABCD")
.setComponentKey("sample:src/main/xoo/sample/Sample.xoo")
.setProjectKey("sample")
.setRuleKey(RuleKey.of("squid", "AvoidCycle"));

userSessionRule.login("john");
Action action = mock(Action.class);
when(action.key()).thenReturn("link-to-jira");
when(actionService.listAvailableActions(eq(issue))).thenReturn(newArrayList(action));

testActions(issue,
"{\"actions\": " +
"[" +
"\"comment\", \"assign\", \"set_tags\", \"assign_to_me\", \"plan\", \"link-to-jira\"\n" +
"]}");
}

@Test
public void write_only_comment_action() {
Issue issue = new DefaultIssue()

+ 100
- 99
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java Datei anzeigen

@@ -43,8 +43,8 @@ import org.sonar.server.component.ComponentTesting;
import org.sonar.server.issue.IssueTesting;
import org.sonar.server.issue.filter.IssueFilterParameters;
import org.sonar.server.issue.index.IssueIndexer;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionChange;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.rule.db.RuleDao;
import org.sonar.server.tester.ServerTester;
import org.sonar.server.tester.UserSessionRule;
@@ -83,9 +83,9 @@ public class SearchActionComponentsMediumTest {
@Test
public void issues_on_different_projects() throws Exception {
RuleDto rule = newRule();
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent"));
IssueDto issue = IssueTesting.newDto(rule, file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setStatus("OPEN").setResolution("OPEN")
@@ -94,9 +94,9 @@ public class SearchActionComponentsMediumTest {
.setIssueUpdateDate(DateUtils.parseDateTime("2017-12-04T00:00:00+0100"));
db.issueDao().insert(session, issue);

ComponentDto project2 = insertComponent(ComponentTesting.newProjectDto("DBCA").setKey("MyProject2"));
ComponentDto project2 = insertComponent(ComponentTesting.newProjectDto("P2").setKey("MyProject2"));
setDefaultProjectPermission(project2);
ComponentDto file2 = insertComponent(ComponentTesting.newFileDto(project2, "EDCB").setKey("MyComponent2"));
ComponentDto file2 = insertComponent(ComponentTesting.newFileDto(project2, "F2").setKey("MyComponent2"));
IssueDto issue2 = IssueTesting.newDto(rule, file2, project2)
.setKee("92fd47d4-b650-4037-80bc-7b112bd4eac2")
.setStatus("OPEN").setResolution("OPEN")
@@ -107,36 +107,36 @@ public class SearchActionComponentsMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION).execute();
result.assertJson(this.getClass(), "issues_on_different_projects.json");
}

@Test
public void search_by_project_uuid() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
db.issueDao().insert(session, issue);
session.commit();
tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.PROJECT_UUIDS, project.uuid())
.execute()
.assertJson(this.getClass(), "search_by_project_uuid.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.PROJECT_UUIDS, "unknown")
.execute()
.assertJson(this.getClass(), "no_issue.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, project.uuid())
.execute()
.assertJson(this.getClass(), "search_by_project_uuid.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, "unknown")
.execute()
.assertJson(this.getClass(), "no_issue.json");
@@ -144,15 +144,15 @@ public class SearchActionComponentsMediumTest {

@Test
public void project_facet_is_sticky() throws Exception {
ComponentDto project1 = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject1"));
ComponentDto project2 = insertComponent(ComponentTesting.newProjectDto("BCDE").setKey("MyProject2"));
ComponentDto project3 = insertComponent(ComponentTesting.newProjectDto("CDEF").setKey("MyProject3"));
ComponentDto project1 = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject1"));
ComponentDto project2 = insertComponent(ComponentTesting.newProjectDto("P2").setKey("MyProject2"));
ComponentDto project3 = insertComponent(ComponentTesting.newProjectDto("P3").setKey("MyProject3"));
setDefaultProjectPermission(project1);
setDefaultProjectPermission(project2);
setDefaultProjectPermission(project3);
ComponentDto file1 = insertComponent(ComponentTesting.newFileDto(project1, "FEDC").setKey("MyComponent1"));
ComponentDto file2 = insertComponent(ComponentTesting.newFileDto(project2, "EDCB").setKey("MyComponent2"));
ComponentDto file3 = insertComponent(ComponentTesting.newFileDto(project3, "DCBA").setKey("MyComponent3"));
ComponentDto file1 = insertComponent(ComponentTesting.newFileDto(project1, "F1").setKey("MyComponent1"));
ComponentDto file2 = insertComponent(ComponentTesting.newFileDto(project2, "F2").setKey("MyComponent2"));
ComponentDto file3 = insertComponent(ComponentTesting.newFileDto(project3, "F3").setKey("MyComponent3"));
RuleDto rule = newRule();
IssueDto issue1 = IssueTesting.newDto(rule, file1, project1).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
IssueDto issue2 = IssueTesting.newDto(rule, file2, project2).setKee("2bd4eac2-b650-4037-80bc-7b1182fd47d4");
@@ -161,7 +161,7 @@ public class SearchActionComponentsMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.PROJECT_UUIDS, project1.uuid())
.setParam(WebService.Param.FACETS, "projectUuids")
.execute()
@@ -170,30 +170,30 @@ public class SearchActionComponentsMediumTest {

@Test
public void search_by_file_uuid() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
db.issueDao().insert(session, issue);
session.commit();
tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.FILE_UUIDS, file.uuid())
.execute()
.assertJson(this.getClass(), "search_by_file_uuid.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.FILE_UUIDS, "unknown")
.execute()
.assertJson(this.getClass(), "no_issue.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, file.uuid())
.execute()
.assertJson(this.getClass(), "search_by_file_uuid.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, "unknown")
.execute()
.assertJson(this.getClass(), "no_issue.json");
@@ -201,10 +201,10 @@ public class SearchActionComponentsMediumTest {

@Test
public void search_by_file_key() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto unitTest = insertComponent(ComponentTesting.newFileDto(project, "CDEF").setQualifier(Qualifiers.UNIT_TEST_FILE).setKey("MyComponentTest"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent"));
ComponentDto unitTest = insertComponent(ComponentTesting.newFileDto(project, "F2").setQualifier(Qualifiers.UNIT_TEST_FILE).setKey("MyComponentTest"));
RuleDto rule = newRule();
IssueDto issueOnFile = IssueTesting.newDto(rule, file, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
IssueDto issueOnTest = IssueTesting.newDto(rule, unitTest, project).setKee("2bd4eac2-b650-4037-80bc-7b1182fd47d4");
@@ -212,12 +212,12 @@ public class SearchActionComponentsMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENTS, file.key())
.execute()
.assertJson(this.getClass(), "search_by_file_key.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENTS, unitTest.key())
.execute()
.assertJson(this.getClass(), "search_by_test_key.json");
@@ -226,11 +226,11 @@ public class SearchActionComponentsMediumTest {

@Test
public void display_file_facet() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file1 = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent1"));
ComponentDto file2 = insertComponent(ComponentTesting.newFileDto(project, "CDEF").setKey("MyComponent2"));
ComponentDto file3 = insertComponent(ComponentTesting.newFileDto(project, "DEFA").setKey("MyComponent3"));
ComponentDto file1 = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent1"));
ComponentDto file2 = insertComponent(ComponentTesting.newFileDto(project, "F2").setKey("MyComponent2"));
ComponentDto file3 = insertComponent(ComponentTesting.newFileDto(project, "F3").setKey("MyComponent3"));
RuleDto newRule = newRule();
IssueDto issue1 = IssueTesting.newDto(newRule, file1, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
IssueDto issue2 = IssueTesting.newDto(newRule, file2, project).setKee("2bd4eac2-b650-4037-80bc-7b1182fd47d4");
@@ -238,7 +238,7 @@ public class SearchActionComponentsMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, project.uuid())
.setParam(IssueFilterParameters.FILE_UUIDS, file1.uuid() + "," + file3.uuid())
.setParam(WebService.Param.FACETS, "fileUuids")
@@ -248,31 +248,31 @@ public class SearchActionComponentsMediumTest {

@Test
public void search_by_directory_path() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto directory = insertComponent(ComponentTesting.newDirectory(project, "src/main/java/dir"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent").setPath(directory.path() + "/MyComponent.java"));
ComponentDto directory = insertComponent(ComponentTesting.newDirectory(project, "D1", "src/main/java/dir"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent").setPath(directory.path() + "/MyComponent.java"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
db.issueDao().insert(session, issue);
session.commit();
tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, directory.uuid())
.execute()
.assertJson(this.getClass(), "search_by_file_uuid.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, "unknown")
.execute()
.assertJson(this.getClass(), "no_issue.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.DIRECTORIES, "src/main/java/dir")
.execute()
.assertJson(this.getClass(), "search_by_file_uuid.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.DIRECTORIES, "src/main/java")
.execute()
.assertJson(this.getClass(), "no_issue.json");
@@ -280,14 +280,14 @@ public class SearchActionComponentsMediumTest {

@Test
public void search_by_directory_path_in_different_modules() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto module1 = insertComponent(ComponentTesting.newModuleDto(project).setKey("module1"));
ComponentDto module2 = insertComponent(ComponentTesting.newModuleDto(project).setKey("module2"));
ComponentDto directory1 = insertComponent(ComponentTesting.newDirectory(module1, "src/main/java/dir"));
ComponentDto directory2 = insertComponent(ComponentTesting.newDirectory(module2, "src/main/java/dir"));
ComponentDto file1 = insertComponent(ComponentTesting.newFileDto(module1, "BCDE").setKey("module1:MyComponent").setPath(directory1.path() + "/MyComponent.java"));
insertComponent(ComponentTesting.newFileDto(module2, "CDEF").setKey("module2:MyComponent").setPath(directory2.path() + "/MyComponent.java"));
ComponentDto module1 = insertComponent(ComponentTesting.newModuleDto("M1", project).setKey("module1"));
ComponentDto module2 = insertComponent(ComponentTesting.newModuleDto("M2", project).setKey("module2"));
ComponentDto directory1 = insertComponent(ComponentTesting.newDirectory(module1, "D1", "src/main/java/dir"));
ComponentDto directory2 = insertComponent(ComponentTesting.newDirectory(module2, "D2", "src/main/java/dir"));
ComponentDto file1 = insertComponent(ComponentTesting.newFileDto(module1, "F1").setKey("module1:MyComponent").setPath(directory1.path() + "/MyComponent.java"));
insertComponent(ComponentTesting.newFileDto(module2, "F2").setKey("module2:MyComponent").setPath(directory2.path() + "/MyComponent.java"));
RuleDto rule = newRule();
IssueDto issue1 = IssueTesting.newDto(rule, file1, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
db.issueDao().insert(session, issue1);
@@ -295,34 +295,34 @@ public class SearchActionComponentsMediumTest {

tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, directory1.uuid())
.execute()
.assertJson(this.getClass(), "search_by_directory_uuid.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, directory2.uuid())
.execute()
.assertJson(this.getClass(), "no_issue.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.MODULE_UUIDS, module1.uuid())
.setParam(IssueFilterParameters.DIRECTORIES, "src/main/java/dir")
.execute()
.assertJson(this.getClass(), "search_by_directory_uuid.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.MODULE_UUIDS, module2.uuid())
.setParam(IssueFilterParameters.DIRECTORIES, "src/main/java/dir")
.execute()
.assertJson(this.getClass(), "no_issue.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.DIRECTORIES, "src/main/java/dir")
.execute()
.assertJson(this.getClass(), "search_by_directory_uuid.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.DIRECTORIES, "src/main/java")
.execute()
.assertJson(this.getClass(), "no_issue.json");
@@ -330,14 +330,14 @@ public class SearchActionComponentsMediumTest {

@Test
public void display_module_facet() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto module = insertComponent(ComponentTesting.newModuleDto("BCDE", project).setKey("MyModule"));
ComponentDto subModule1 = insertComponent(ComponentTesting.newModuleDto("CDEF", module).setKey("MySubModule1"));
ComponentDto subModule2 = insertComponent(ComponentTesting.newModuleDto("DEFA", module).setKey("MySubModule2"));
ComponentDto subModule3 = insertComponent(ComponentTesting.newModuleDto("EFAB", module).setKey("MySubModule3"));
ComponentDto file1 = insertComponent(ComponentTesting.newFileDto(subModule1, "FEDC").setKey("MyComponent1"));
ComponentDto file2 = insertComponent(ComponentTesting.newFileDto(subModule2, "EDCB").setKey("MyComponent2"));
ComponentDto module = insertComponent(ComponentTesting.newModuleDto("M1", project).setKey("MyModule"));
ComponentDto subModule1 = insertComponent(ComponentTesting.newModuleDto("SUBM1", module).setKey("MySubModule1"));
ComponentDto subModule2 = insertComponent(ComponentTesting.newModuleDto("SUBM2", module).setKey("MySubModule2"));
ComponentDto subModule3 = insertComponent(ComponentTesting.newModuleDto("SUBM3", module).setKey("MySubModule3"));
ComponentDto file1 = insertComponent(ComponentTesting.newFileDto(subModule1, "F1").setKey("MyComponent1"));
ComponentDto file2 = insertComponent(ComponentTesting.newFileDto(subModule2, "F2").setKey("MyComponent2"));
RuleDto newRule = newRule();
IssueDto issue1 = IssueTesting.newDto(newRule, file1, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
IssueDto issue2 = IssueTesting.newDto(newRule, file2, project).setKee("2bd4eac2-b650-4037-80bc-7b1182fd47d4");
@@ -345,7 +345,7 @@ public class SearchActionComponentsMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, module.uuid())
.setParam(IssueFilterParameters.MODULE_UUIDS, subModule1.uuid() + "," + subModule3.uuid())
.setParam(WebService.Param.FACETS, "moduleUuids")
@@ -355,17 +355,17 @@ public class SearchActionComponentsMediumTest {

@Test
public void display_directory_facet() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto directory = insertComponent(ComponentTesting.newDirectory(project, "src/main/java/dir"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent").setPath(directory.path() + "/MyComponent.java"));
ComponentDto directory = insertComponent(ComponentTesting.newDirectory(project, "D1", "src/main/java/dir"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent").setPath(directory.path() + "/MyComponent.java"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
db.issueDao().insert(session, issue);
session.commit();
tester.get(IssueIndexer.class).indexAll();

userSessionRule.login("john");
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam("resolved", "false")
.setParam(WebService.Param.FACETS, "directories")
.execute();
@@ -374,18 +374,18 @@ public class SearchActionComponentsMediumTest {

@Test
public void search_by_view_uuid() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent"));
insertIssue(IssueTesting.newDto(newRule(), file, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2"));

ComponentDto view = insertComponent(ComponentTesting.newProjectDto("CDEF").setQualifier(Qualifiers.VIEW).setKey("MyView"));
ComponentDto view = insertComponent(ComponentTesting.newProjectDto("V1").setQualifier(Qualifiers.VIEW).setKey("MyView"));
indexView(view.uuid(), newArrayList(project.uuid()));

setAnyoneProjectPermission(view, UserRole.USER);
userSessionRule.login("john").addProjectUuidPermissions(UserRole.USER, view.uuid());

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, view.uuid())
.execute()
.assertJson(this.getClass(), "search_by_view_uuid.json");
@@ -393,19 +393,19 @@ public class SearchActionComponentsMediumTest {

@Test
public void search_by_view_uuid_return_only_authorized_view() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent"));
insertIssue(IssueTesting.newDto(newRule(), file, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2"));

ComponentDto view = insertComponent(ComponentTesting.newProjectDto("CDEF").setQualifier(Qualifiers.VIEW).setKey("MyView"));
ComponentDto view = insertComponent(ComponentTesting.newProjectDto("V1").setQualifier(Qualifiers.VIEW).setKey("MyView"));
indexView(view.uuid(), newArrayList(project.uuid()));

setAnyoneProjectPermission(view, UserRole.USER);
// User has wrong permission on the view, no issue will be returned
userSessionRule.login("john").addProjectUuidPermissions(UserRole.CODEVIEWER, view.uuid());

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, view.uuid())
.execute()
.assertJson(this.getClass(), "no_issue.json");
@@ -413,20 +413,20 @@ public class SearchActionComponentsMediumTest {

@Test
public void search_by_sub_view_uuid() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent"));
insertIssue(IssueTesting.newDto(newRule(), file, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2"));

ComponentDto view = insertComponent(ComponentTesting.newProjectDto("CDEF").setQualifier(Qualifiers.VIEW).setKey("MyView"));
ComponentDto view = insertComponent(ComponentTesting.newProjectDto("V1").setQualifier(Qualifiers.VIEW).setKey("MyView"));
indexView(view.uuid(), newArrayList(project.uuid()));
ComponentDto subView = insertComponent(ComponentTesting.newProjectDto("DEFG").setQualifier(Qualifiers.SUBVIEW).setKey("MySubView"));
ComponentDto subView = insertComponent(ComponentTesting.newProjectDto("SV1").setQualifier(Qualifiers.SUBVIEW).setKey("MySubView"));
indexView(subView.uuid(), newArrayList(project.uuid()));

setAnyoneProjectPermission(view, UserRole.USER);
userSessionRule.login("john").addComponentUuidPermission(UserRole.USER, view.uuid(), subView.uuid());

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, subView.uuid())
.execute()
.assertJson(this.getClass(), "search_by_view_uuid.json");
@@ -434,30 +434,31 @@ public class SearchActionComponentsMediumTest {

@Test
public void search_by_sub_view_uuid_return_only_authorized_view() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent"));
insertIssue(IssueTesting.newDto(newRule(), file, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2"));

ComponentDto view = insertComponent(ComponentTesting.newProjectDto("CDEF").setQualifier(Qualifiers.VIEW).setKey("MyView"));
ComponentDto view = insertComponent(ComponentTesting.newProjectDto("V1").setQualifier(Qualifiers.VIEW).setKey("MyView"));
indexView(view.uuid(), newArrayList(project.uuid()));
ComponentDto subView = insertComponent(ComponentTesting.newProjectDto("DEFG").setQualifier(Qualifiers.SUBVIEW).setKey("MySubView"));
ComponentDto subView = insertComponent(ComponentTesting.newProjectDto("SV1").setQualifier(Qualifiers.SUBVIEW).setKey("MySubView"));
indexView(subView.uuid(), newArrayList(project.uuid()));

setAnyoneProjectPermission(view, UserRole.USER);
// User has wrong permission on the view, no issue will be returned
userSessionRule.login("john").addComponentUuidPermission(UserRole.CODEVIEWER, view.uuid(), subView.uuid());

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, subView.uuid())
.execute()
.assertJson(this.getClass(), "no_issue.json");
}

@Test
public void search_by_author() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent"));
RuleDto newRule = newRule();
IssueDto issue1 = IssueTesting.newDto(newRule, file, project).setAuthorLogin("leia").setKee("2bd4eac2-b650-4037-80bc-7b112bd4eac2");
IssueDto issue2 = IssueTesting.newDto(newRule, file, project).setAuthorLogin("luke@skywalker.name").setKee("82fd47d4-b650-4037-80bc-7b1182fd47d4");
@@ -466,13 +467,13 @@ public class SearchActionComponentsMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.AUTHORS, "leia")
.setParam(WebService.Param.FACETS, "authors")
.execute()
.assertJson(this.getClass(), "search_by_authors.json");

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.AUTHORS, "unknown")
.execute()
.assertJson(this.getClass(), "no_issue.json");
@@ -481,9 +482,9 @@ public class SearchActionComponentsMediumTest {

@Test
public void search_by_developer() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent"));
ComponentDto developer = insertComponent(ComponentTesting.newDeveloper("Anakin Skywalker"));
db.authorDao().insertAuthor("vader", developer.getId());
db.authorDao().insertAuthor("anakin@skywalker.name", developer.getId());
@@ -495,7 +496,7 @@ public class SearchActionComponentsMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, developer.uuid())
.execute()
.assertJson(this.getClass(), "search_by_developer.json");
@@ -503,17 +504,17 @@ public class SearchActionComponentsMediumTest {

@Test
public void search_by_developer_technical_project() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("P1").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "F1").setKey("MyComponent"));

ComponentDto otherProject = insertComponent(ComponentTesting.newProjectDto("XXXX").setKey("OtherProject"));
ComponentDto otherProject = insertComponent(ComponentTesting.newProjectDto("P2").setKey("OtherProject"));
setDefaultProjectPermission(otherProject);
ComponentDto otherFile = insertComponent(ComponentTesting.newFileDto(otherProject, "YYYY").setKey("OtherComponent"));
ComponentDto otherFile = insertComponent(ComponentTesting.newFileDto(otherProject, "F2").setKey("OtherComponent"));

ComponentDto developer = insertComponent(ComponentTesting.newDeveloper("Anakin Skywalker"));
ComponentDto technicalProject = insertComponent(ComponentTesting.newDevProjectCopy("CDEF", project, developer));
insertComponent(ComponentTesting.newDevProjectCopy("DEFG", otherProject, developer));
ComponentDto technicalProject = insertComponent(ComponentTesting.newDevProjectCopy("COPY_P1", project, developer));
insertComponent(ComponentTesting.newDevProjectCopy("COPY_P2", otherProject, developer));

db.authorDao().insertAuthor("vader", developer.getId());
db.authorDao().insertAuthor("anakin@skywalker.name", developer.getId());
@@ -527,7 +528,7 @@ public class SearchActionComponentsMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, technicalProject.uuid())
.execute();
result

+ 92
- 99
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java Datei anzeigen

@@ -24,8 +24,10 @@ import com.google.common.collect.ImmutableMap;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.issue.Issue;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.security.DefaultGroups;
import org.sonar.api.server.ws.WebService;
@@ -50,14 +52,15 @@ import org.sonar.server.issue.IssueQuery;
import org.sonar.server.issue.IssueTesting;
import org.sonar.server.issue.filter.IssueFilterParameters;
import org.sonar.server.issue.index.IssueIndexer;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionChange;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.rule.db.RuleDao;
import org.sonar.server.search.QueryContext;
import org.sonar.server.tester.ServerTester;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.WsTester;

import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;

public class SearchActionMediumTest {
@@ -89,19 +92,19 @@ public class SearchActionMediumTest {
public void define_action() {
WebService.Controller controller = wsTester.controller("api/issues");

WebService.Action show = controller.action("search");
WebService.Action show = controller.action("search2");
assertThat(show).isNotNull();
assertThat(show.handler()).isNotNull();
assertThat(show.since()).isEqualTo("3.6");
assertThat(show.isPost()).isFalse();
assertThat(show.isInternal()).isFalse();
assertThat(show.responseExampleAsString()).isNotEmpty();
assertThat(show.params()).hasSize(40);
assertThat(show.params()).hasSize(37);
}

@Test
public void empty_search() throws Exception {
WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION);
WsTester.Result result = request.execute();

assertThat(result).isNotNull();
@@ -109,40 +112,43 @@ public class SearchActionMediumTest {
}

@Test
public void issue() throws Exception {
public void response_contains_all_fields_except_additional_fields() throws Exception {
db.userDao().insert(session, new UserDto().setLogin("simon").setName("Simon").setEmail("simon@email.com"));
db.userDao().insert(session, new UserDto().setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
.setDebt(10L)
.setStatus("OPEN").setResolution("OPEN")
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setDebt(10L)
.setMessage("the message")
.setStatus(Issue.STATUS_RESOLVED)
.setResolution(Issue.RESOLUTION_FIXED)
.setSeverity("MAJOR")
.setAuthorLogin("John")
.setAssignee("simon")
.setReporter("fabrice")
.setActionPlanKey("AP-ABCD")
.setTags(asList("bug", "owasp"))
.setIssueCreationDate(DateUtils.parseDateTime("2014-09-04T00:00:00+0100"))
.setIssueUpdateDate(DateUtils.parseDateTime("2017-12-04T00:00:00+0100"));
db.issueDao().insert(session, issue);
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
result.assertJson(this.getClass(), "issue.json");
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION).execute();
result.assertJson(this.getClass(), "response_contains_all_fields_except_additional_fields.json");
}

@Test
public void issue_with_comment() throws Exception {
public void issue_with_comments() throws Exception {
db.userDao().insert(session, new UserDto().setLogin("john").setName("John"));
db.userDao().insert(session, new UserDto().setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
db.issueDao().insert(session, issue);
@@ -153,20 +159,22 @@ public class SearchActionMediumTest {
.setChangeData("*My comment*")
.setChangeType(IssueChangeDto.TYPE_COMMENT)
.setUserLogin("john")
.setCreatedAt(DateUtils.parseDate("2014-09-09").getTime()));
.setCreatedAt(DateUtils.parseDateTime("2014-09-09T12:00:00+0000").getTime()));
tester.get(IssueChangeDao.class).insert(session,
new IssueChangeDto().setIssueKey(issue.getKey())
.setKey("COMMENT-ABCE")
.setChangeData("Another comment")
.setChangeType(IssueChangeDto.TYPE_COMMENT)
.setUserLogin("fabrice")
.setCreatedAt(DateUtils.parseDate("2014-09-10").getTime()));
.setCreatedAt(DateUtils.parseDateTime("2014-09-10T12:00:00+0000").getTime()));
session.commit();
tester.get(IssueIndexer.class).indexAll();

userSessionRule.login("john");
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
result.assertJson(this.getClass(), "issue_with_comment.json");
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam("additionalFields", "comments,users")
.execute();
result.assertJson(this.getClass(), "issue_with_comments.json");
}

@Test
@@ -174,9 +182,9 @@ public class SearchActionMediumTest {
db.userDao().insert(session, new UserDto().setLogin("john").setName("John").setEmail("john@email.com"));
db.userDao().insert(session, new UserDto().setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
db.issueDao().insert(session, issue);
@@ -187,28 +195,28 @@ public class SearchActionMediumTest {
.setChangeData("*My comment*")
.setChangeType(IssueChangeDto.TYPE_COMMENT)
.setUserLogin("john")
.setCreatedAt(DateUtils.parseDate("2014-09-09").getTime()));
.setCreatedAt(DateUtils.parseDateTime("2014-09-09T12:00:00+0000").getTime()));
tester.get(IssueChangeDao.class).insert(session,
new IssueChangeDto().setIssueKey(issue.getKey())
.setKey("COMMENT-ABCE")
.setChangeData("Another comment")
.setChangeType(IssueChangeDto.TYPE_COMMENT)
.setUserLogin("fabrice")
.setCreatedAt(DateUtils.parseDate("2014-09-10").getTime()));
.setCreatedAt(DateUtils.parseDateTime("2014-09-10T19:10:03+0000").getTime()));
session.commit();
tester.get(IssueIndexer.class).indexAll();

userSessionRule.login("john");
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).setParam(IssueFilterParameters.HIDE_COMMENTS, "true").execute();
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION).setParam(IssueFilterParameters.HIDE_COMMENTS, "true").execute();
result.assertJson(this.getClass(), "issue_with_comment_hidden.json");
assertThat(result.outputAsString()).doesNotContain("fabrice");
}

@Test
public void issue_with_action_plan() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));

tester.get(ActionPlanDao.class).save(new ActionPlanDto()
.setKey("AP-ABCD")
@@ -227,15 +235,18 @@ public class SearchActionMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam("additionalFields", "actionPlans")
.execute();
result.assertJson(this.getClass(), "issue_with_action_plan.json");
}

@Ignore("temporarily disabled")
@Test
public void issue_with_attributes() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setIssueAttributes(KeyValueFormat.format(ImmutableMap.of("jira-issue-key", "SONAR-1234")));
@@ -243,17 +254,18 @@ public class SearchActionMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.execute();
result.assertJson(this.getClass(), "issue_with_attributes.json");
}

@Test
public void issue_with_extra_fields() throws Exception {
public void load_additional_fields() throws Exception {
db.userDao().insert(session, new UserDto().setLogin("simon").setName("Simon").setEmail("simon@email.com"));
db.userDao().insert(session, new UserDto().setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));

tester.get(ActionPlanDao.class).save(new ActionPlanDto()
.setKey("AP-ABCD")
@@ -273,17 +285,17 @@ public class SearchActionMediumTest {
tester.get(IssueIndexer.class).indexAll();

userSessionRule.login("john");
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
.setParam("f", "assignee,reporter,actionPlan,actions,transitions,actionPlanName").execute();
result.assertJson(this.getClass(), "issue_with_extra_fields.json");
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam("additionalFields", "_all").execute();
result.assertJson(this.getClass(), "load_additional_fields.json");
}

@Test
public void issue_linked_on_removed_file() throws Exception {
public void issue_on_removed_file() throws Exception {
RuleDto rule = newRule();
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto removedFile = insertComponent(ComponentTesting.newFileDto(project).setUuid("EDCB")
ComponentDto removedFile = insertComponent(ComponentTesting.newFileDto(project).setUuid("REMOVED_FILE_ID")
.setEnabled(false)
.setKey("RemovedComponent"));

@@ -298,30 +310,31 @@ public class SearchActionMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
result.assertJson(this.getClass(), "issue_linked_on_removed_file.json");
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.execute();
result.assertJson(this.getClass(), "issue_on_removed_file.json");
}

@Test
public void issue_contains_component_id_for_eclipse() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project);
db.issueDao().insert(session, issue);
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION).execute();
assertThat(result.outputAsString()).contains("\"componentId\":" + file.getId() + ",");
}

@Test
public void apply_paging_with_one_component() throws Exception {
RuleDto rule = newRule();
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
for (int i = 0; i < QueryContext.MAX_LIMIT + 1; i++) {
IssueDto issue = IssueTesting.newDto(rule, file, project);
tester.get(IssueDao.class).insert(session, issue);
@@ -329,13 +342,13 @@ public class SearchActionMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).setParam(IssueFilterParameters.COMPONENTS, file.getKey()).execute();
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION).setParam(IssueFilterParameters.COMPONENTS, file.getKey()).execute();
result.assertJson(this.getClass(), "apply_paging_with_one_component.json");
}

@Test
public void components_contains_sub_projects() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("ProjectHavingModule"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("ProjectHavingModule"));
setDefaultProjectPermission(project);
ComponentDto module = insertComponent(ComponentTesting.newModuleDto(project).setKey("ModuleHavingFile"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(module, "BCDE").setKey("FileLinkedToModule"));
@@ -344,15 +357,15 @@ public class SearchActionMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).execute();
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION).setParam(Search2Action.ADDITIONAL_FIELDS, "_all").execute();
result.assertJson(this.getClass(), "components_contains_sub_projects.json");
}

@Test
public void display_facets() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
@@ -365,7 +378,7 @@ public class SearchActionMediumTest {
tester.get(IssueIndexer.class).indexAll();

userSessionRule.login("john");
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam("resolved", "false")
.setParam(WebService.Param.FACETS, "statuses,severities,resolutions,projectUuids,rules,fileUuids,assignees,languages,actionPlans")
.execute();
@@ -374,9 +387,9 @@ public class SearchActionMediumTest {

@Test
public void display_facets_in_debt_mode() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
@@ -389,7 +402,7 @@ public class SearchActionMediumTest {
tester.get(IssueIndexer.class).indexAll();

userSessionRule.login("john");
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam("resolved", "false")
.setParam(WebService.Param.FACETS, "statuses,severities,resolutions,projectUuids,rules,fileUuids,assignees,languages,actionPlans")
.setParam("facetMode", "debt")
@@ -399,9 +412,9 @@ public class SearchActionMediumTest {

@Test
public void display_zero_valued_facets_for_selected_items() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
@@ -414,7 +427,7 @@ public class SearchActionMediumTest {
tester.get(IssueIndexer.class).indexAll();

userSessionRule.login("john");
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam("resolved", "false")
.setParam("severities", "MAJOR,MINOR")
.setParam("languages", "xoo,polop,palap")
@@ -427,9 +440,9 @@ public class SearchActionMediumTest {
public void filter_by_assigned_to_me() throws Exception {
db.userDao().insert(session, new UserDto().setLogin("john").setName("John").setEmail("john@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
RuleDto rule = newRule();
IssueDto issue1 = IssueTesting.newDto(rule, file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
@@ -459,7 +472,7 @@ public class SearchActionMediumTest {
tester.get(IssueIndexer.class).indexAll();

userSessionRule.login("john");
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam("resolved", "false")
.setParam("assignees", "__me__")
.setParam(WebService.Param.FACETS, "assignees,assigned_to_me")
@@ -471,9 +484,9 @@ public class SearchActionMediumTest {
public void filter_by_assigned_to_me_unauthenticated() throws Exception {
userSessionRule.login();

ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
RuleDto rule = newRule();
IssueDto issue1 = IssueTesting.newDto(rule, file, project)
.setStatus("OPEN")
@@ -490,7 +503,7 @@ public class SearchActionMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam("resolved", "false")
.setParam("assignees", "__me__")
.execute()
@@ -501,9 +514,9 @@ public class SearchActionMediumTest {
public void assigned_to_me_facet_is_sticky_relative_to_assignees() throws Exception {
db.userDao().insert(session, new UserDto().setLogin("alice").setName("Alice").setEmail("alice@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyFile"));
RuleDto rule = newRule();
IssueDto issue1 = IssueTesting.newDto(rule, file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
@@ -533,7 +546,7 @@ public class SearchActionMediumTest {
tester.get(IssueIndexer.class).indexAll();

userSessionRule.login("john-bob.polop");
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam("resolved", "false")
.setParam("assignees", "alice")
.setParam(WebService.Param.FACETS, "assignees,assigned_to_me")
@@ -541,32 +554,12 @@ public class SearchActionMediumTest {
.assertJson(this.getClass(), "assigned_to_me_facet_sticky.json");
}

@Test
public void hide_rules() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
.setDebt(10L)
.setStatus("OPEN")
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setSeverity("MAJOR");
db.issueDao().insert(session, issue);
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION).setParam(IssueFilterParameters.HIDE_RULES, "true").execute();
result.assertJson(this.getClass(), "hide_rules.json");
}

@Test
public void sort_by_updated_at() throws Exception {
RuleDto rule = newRule();
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
db.issueDao().insert(session, IssueTesting.newDto(rule, file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac1")
.setIssueUpdateDate(DateUtils.parseDateTime("2014-11-02T00:00:00+0100")));
@@ -579,7 +572,7 @@ public class SearchActionMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION)
.setParam("sort", IssueQuery.SORT_BY_UPDATE_DATE)
.setParam("asc", "false")
.execute();
@@ -589,9 +582,9 @@ public class SearchActionMediumTest {
@Test
public void paging() throws Exception {
RuleDto rule = newRule();
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
for (int i = 0; i < 12; i++) {
IssueDto issue = IssueTesting.newDto(rule, file, project);
tester.get(IssueDao.class).insert(session, issue);
@@ -599,7 +592,7 @@ public class SearchActionMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION);
request.setParam(WebService.Param.PAGE, "2");
request.setParam(WebService.Param.PAGE_SIZE, "9");

@@ -610,9 +603,9 @@ public class SearchActionMediumTest {
@Test
public void paging_with_page_size_to_minus_one() throws Exception {
RuleDto rule = newRule();
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
for (int i = 0; i < 12; i++) {
IssueDto issue = IssueTesting.newDto(rule, file, project);
tester.get(IssueDao.class).insert(session, issue);
@@ -620,7 +613,7 @@ public class SearchActionMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION);
request.setParam(WebService.Param.PAGE, "1");
request.setParam(WebService.Param.PAGE_SIZE, "-1");

@@ -631,9 +624,9 @@ public class SearchActionMediumTest {
@Test
public void deprecated_paging() throws Exception {
RuleDto rule = newRule();
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "FILE_ID").setKey("MyComponent"));
for (int i = 0; i < 12; i++) {
IssueDto issue = IssueTesting.newDto(rule, file, project);
tester.get(IssueDao.class).insert(session, issue);
@@ -641,7 +634,7 @@ public class SearchActionMediumTest {
session.commit();
tester.get(IssueIndexer.class).indexAll();

WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION);
request.setParam(IssueFilterParameters.PAGE_INDEX, "2");
request.setParam(IssueFilterParameters.PAGE_SIZE, "9");

@@ -651,7 +644,7 @@ public class SearchActionMediumTest {

@Test
public void default_page_size_is_100() throws Exception {
WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION);
WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, Search2Action.SEARCH_ACTION);

WsTester.Result result = request.execute();
result.assertJson(this.getClass(), "default_page_size_is_100.json");

+ 3
- 3
server/sonar-server/src/test/java/org/sonar/server/rule/ws/ListActionTest.java Datei anzeigen

@@ -55,7 +55,7 @@ public class ListActionTest {
dbTester.getSession().commit();

TestResponse response = tester.newRequest()
.setMimeType(MimeTypes.PROTOBUF)
.setMediaType(MimeTypes.PROTOBUF)
.execute();

assertThat(response.getMediaType()).isEqualTo(MimeTypes.PROTOBUF);
@@ -63,8 +63,8 @@ public class ListActionTest {
assertThat(listResponse.getRulesCount()).isEqualTo(2);

assertThat(listResponse.getRules(0).getKey()).isEqualTo("S001");
assertThat(listResponse.getRules(0).hasInternalKey()).isFalse();
assertThat(listResponse.getRules(0).hasName()).isFalse();
assertThat(listResponse.getRules(0).getInternalKey()).isEqualTo("");
assertThat(listResponse.getRules(0).getName()).isEqualTo("");
assertThat(listResponse.getRules(1).getKey()).isEqualTo("S002");
assertThat(listResponse.getRules(1).getInternalKey()).isEqualTo("I002");
assertThat(listResponse.getRules(1).getName()).isEqualTo("Rule Two");

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/ws/TestRequest.java Datei anzeigen

@@ -64,7 +64,7 @@ public class TestRequest extends ValidatingRequest {
return mimeType;
}

public TestRequest setMimeType(String type) {
public TestRequest setMediaType(String type) {
checkNotNull(type);
this.mimeType = type;
return this;

+ 38
- 0
server/sonar-server/src/test/java/org/sonar/server/ws/WsUtilsTest.java Datei anzeigen

@@ -0,0 +1,38 @@
package org.sonar.server.ws;

import org.junit.Test;
import org.sonar.server.plugins.MimeTypes;
import org.sonarqube.ws.Issues;

import static org.assertj.core.api.Assertions.assertThat;

public class WsUtilsTest {

@Test
public void write_json_by_default() throws Exception {
TestRequest request = new TestRequest();
DumbResponse response = new DumbResponse();

Issues.Issue msg = Issues.Issue.newBuilder().setKey("I1").build();
WsUtils.writeProtobuf(msg, request, response);

assertThat(response.stream().mediaType()).isEqualTo(MimeTypes.JSON);
assertThat(response.outputAsString())
.startsWith("{")
.contains("\"key\":\"I1\"")
.endsWith("}");
}

@Test
public void write_protobuf() throws Exception {
TestRequest request = new TestRequest();
request.setMediaType(MimeTypes.PROTOBUF);
DumbResponse response = new DumbResponse();

Issues.Issue msg = Issues.Issue.newBuilder().setKey("I1").build();
WsUtils.writeProtobuf(msg, request, response);

assertThat(response.stream().mediaType()).isEqualTo(MimeTypes.PROTOBUF);
assertThat(Issues.Issue.parseFrom(response.getFlushedOutput()).getKey()).isEqualTo("I1");
}
}

+ 11
- 14
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/display_file_facet.json Datei anzeigen

@@ -1,37 +1,34 @@
{
"total": 1,
"p": 1,
"projects": [
{ "key": "MyProject" }
],
"components": [
{ "key": "MyProject" },
{ "key": "MyComponent1" },
{ "key": "MyComponent2" },
{ "key": "MyComponent3" }
],
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent1",
"project": "MyProject",
"component": "F1",
"project": "P1",
"rule": "xoo:x1"
}
],
"components": [
{ "id": "P1" },
{ "id": "F1" },
{ "id": "F2" },
{ "id": "F3" }
],
"facets": [
{
"property": "fileUuids",
"values": [
{
"val": "BCDE",
"val": "F1",
"count": 1
},
{
"val": "CDEF",
"val": "F2",
"count": 1
},
{
"val": "DEFA",
"val": "F3",
"count": 0
}
]

+ 13
- 18
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/display_module_facet.json Datei anzeigen

@@ -1,39 +1,34 @@
{
"total": 1,
"p": 1,
"projects": [
{ "key": "MyProject" }
],
"components": [
{ "key": "MyProject" },
{ "key": "MyModule" },
{ "key": "MySubModule1" },
{ "key": "MySubModule2" },
{ "key": "MySubModule3" },
{ "key": "MyComponent1" }
],
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent1",
"project": "MyProject",
"component": "F1",
"project": "P1",
"rule": "xoo:x1"
}
],
"components": [
{ "id": "P1" },
{ "id": "M1" },
{ "id": "SUBM1" },
{ "id": "SUBM2" },
{ "id": "SUBM3" },
{ "id": "F1" }
],
"facets": [
{
"property": "moduleUuids",
"values": [
{
"val": "CDEF",
"val": "SUBM1",
"count": 1
},
{
"val": "DEFA",
"val": "SUBM2",
"count": 1
},
{
"val": "EFAB",
"val": "SUBM3",
"count": 0
}
]

+ 4
- 7
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/display_non_sticky_project_facet.json Datei anzeigen

@@ -1,13 +1,6 @@
{
"total": 1,
"p": 1,
"projects": [
{ "key": "MyProject1" }
],
"components": [
{ "key": "MyProject1" },
{ "key": "MyComponent1" }
],
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
@@ -16,6 +9,10 @@
"rule": "xoo:x1"
}
],
"components": [
{ "key": "MyProject1" },
{ "key": "MyComponent1" }
],
"facets": [
{
"property": "projectUuids",

+ 11
- 18
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/display_sticky_project_facet.json Datei anzeigen

@@ -1,39 +1,32 @@
{
"total": 1,
"p": 1,
"projects": [
{ "key": "MyProject1" },
{ "key": "MyProject2" },
{ "key": "MyProject3" }
],
"components": [
{ "key": "MyProject1" },
{ "key": "MyProject2" },
{ "key": "MyProject3" },
{ "key": "MyComponent1" }
],
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent1",
"project": "MyProject1",
"component": "F1",
"project": "P1",
"rule": "xoo:x1"
}
],
"components": [
{ "id": "P1" },
{ "id": "P2" },
{ "id": "P3" },
{ "id": "F1" }
],
"facets": [
{
"property": "projectUuids",
"values": [
{
"val": "ABCD",
"val": "P1",
"count": 1
},
{
"val": "BCDE",
"val": "P2",
"count": 1
},
{
"val": "CDEF",
"val": "P3",
"count": 1
}
]

+ 8
- 18
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/issues_on_different_projects.json Datei anzeigen

@@ -2,8 +2,8 @@
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "F1",
"project": "P1",
"rule": "xoo:x1",
"status": "OPEN",
"resolution": "OPEN",
@@ -12,8 +12,8 @@
},
{
"key": "92fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent2",
"project": "MyProject2",
"component": "F2",
"project": "P2",
"rule": "xoo:x1",
"status": "OPEN",
"resolution": "OPEN",
@@ -23,34 +23,24 @@
],
"components": [
{
"uuid": "BCDE",
"id": "F1",
"key": "MyComponent",
"enabled" : true
},
{
"uuid": "ABCD",
"id": "P1",
"key": "MyProject",
"enabled" : true
},
{
"uuid": "EDCB",
"id": "F2",
"key": "MyComponent2",
"enabled" : true
},
{
"uuid": "DBCA",
"id": "P2",
"key": "MyProject2",
"enabled" : true
}
],
"projects": [
{
"uuid": "ABCD",
"key": "MyProject"
},
{
"uuid": "DBCA",
"key": "MyProject2"
}
]
}

+ 2
- 2
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_authors.json Datei anzeigen

@@ -4,8 +4,8 @@
"issues": [
{
"key": "2bd4eac2-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "F1",
"project": "P1",
"rule": "xoo:x1",
"author": "leia"
}

+ 4
- 6
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_developer.json Datei anzeigen

@@ -1,18 +1,16 @@
{
"total": 2,
"p": 1,
"issues": [
{
"key": "2bd4eac2-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "F1",
"project": "P1",
"rule": "xoo:x1",
"author": "vader"
},
{
"key": "82fd47d4-b650-4037-80bc-7b1182fd47d4",
"component": "MyComponent",
"project": "MyProject",
"component": "F1",
"project": "P1",
"rule": "xoo:x1",
"author": "anakin@skywalker.name"
}

+ 2
- 4
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_directory_uuid.json Datei anzeigen

@@ -1,11 +1,9 @@
{
"total": 1,
"p": 1,
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "module1:MyComponent",
"project": "MyProject",
"component": "F1",
"project": "P1",
"rule": "xoo:x1"
}
]

+ 2
- 4
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_file_key.json Datei anzeigen

@@ -1,11 +1,9 @@
{
"total": 1,
"p": 1,
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "F1",
"project": "P1",
"rule": "xoo:x1"
}
]

+ 2
- 4
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_file_uuid.json Datei anzeigen

@@ -1,11 +1,9 @@
{
"total": 1,
"p": 1,
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "F1",
"project": "P1",
"rule": "xoo:x1"
}
]

+ 2
- 5
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_project_uuid.json Datei anzeigen

@@ -1,12 +1,9 @@
{
"total": 1,
"p": 1,
"ps": 100,
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "F1",
"project": "P1",
"rule": "xoo:x1"
}
]

+ 2
- 4
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_test_key.json Datei anzeigen

@@ -1,11 +1,9 @@
{
"total": 1,
"p": 1,
"issues": [
{
"key": "2bd4eac2-b650-4037-80bc-7b1182fd47d4",
"component": "MyComponentTest",
"project": "MyProject",
"component": "F2",
"project": "P1",
"rule": "xoo:x1"
}
]

+ 2
- 5
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_view_uuid.json Datei anzeigen

@@ -1,12 +1,9 @@
{
"total": 1,
"p": 1,
"ps": 100,
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "F1",
"project": "P1",
"rule": "xoo:x1"
}
]

+ 1
- 2
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/assigned_to_me_facet_sticky.json Datei anzeigen

@@ -2,8 +2,7 @@
"issues": [
{
"key": "7b112bd4-b650-4037-80bc-82fd47d4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "FILE_ID",
"rule": "xoo:x1",
"status": "OPEN",
"severity": "MAJOR",

+ 0
- 5
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/components_contains_sub_projects.json Datei anzeigen

@@ -9,10 +9,5 @@
{
"key": "ProjectHavingModule"
}
],
"projects": [
{
"key": "ProjectHavingModule"
}
]
}

+ 0
- 1
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/default_page_size_is_100.json Datei anzeigen

@@ -6,7 +6,6 @@
"pageIndex": 1,
"pageSize": 100,
"total": 0,
"fTotal": "0",
"pages": 0
}
}

+ 0
- 1
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/deprecated_paging.json Datei anzeigen

@@ -3,7 +3,6 @@
"pageIndex": 2,
"pageSize": 9,
"total": 12,
"fTotal": "12",
"pages": 2
}
}

+ 3
- 4
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_facets.json Datei anzeigen

@@ -2,8 +2,7 @@
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "FILE_ID",
"rule": "xoo:x1",
"status": "OPEN",
"severity": "MAJOR",
@@ -90,7 +89,7 @@
"property": "projectUuids",
"values": [
{
"val": "ABCD",
"val": "PROJECT_ID",
"count": 1
}
]
@@ -108,7 +107,7 @@
"property": "fileUuids",
"values": [
{
"val": "BCDE",
"val": "FILE_ID",
"count": 1
}
]

+ 8
- 8
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_facets_debt.json Datei anzeigen

@@ -3,8 +3,8 @@
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "FILE_ID",
"project": "PROJECT_ID",
"rule": "xoo:x1",
"status": "OPEN",
"severity": "MAJOR",
@@ -40,6 +40,10 @@
{
"property": "severities",
"values": [
{
"val": "MAJOR",
"count": 10
},
{
"val": "INFO",
"count": 0
@@ -48,10 +52,6 @@
"val": "MINOR",
"count": 0
},
{
"val": "MAJOR",
"count": 10
},
{
"val": "CRITICAL",
"count": 0
@@ -91,7 +91,7 @@
"property": "projectUuids",
"values": [
{
"val": "ABCD",
"val": "PROJECT_ID",
"count": 10
}
]
@@ -109,7 +109,7 @@
"property": "fileUuids",
"values": [
{
"val": "BCDE",
"val": "FILE_ID",
"count": 10
}
]

+ 4
- 4
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_zero_facets.json Datei anzeigen

@@ -2,8 +2,8 @@
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "FILE_ID",
"project": "PROJECT_ID",
"rule": "xoo:x1",
"status": "OPEN",
"severity": "MAJOR",
@@ -90,7 +90,7 @@
"property": "projectUuids",
"values": [
{
"val": "ABCD",
"val": "PROJECT_ID",
"count": 1
}
]
@@ -108,7 +108,7 @@
"property": "fileUuids",
"values": [
{
"val": "BCDE",
"val": "FILE_ID",
"count": 1
}
]

+ 1
- 4
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/empty_result.json Datei anzeigen

@@ -6,10 +6,7 @@
"pageIndex": 1,
"pageSize": 100,
"total": 0,
"fTotal": "0",
"pages": 0
},
"issues": [],
"components": [],
"projects": []
"issues": []
}

+ 2
- 2
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/filter_by_assigned_to_me.json Datei anzeigen

@@ -2,8 +2,8 @@
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "FILE_ID",
"project": "PROJECT_ID",
"rule": "xoo:x1",
"status": "OPEN",
"severity": "MAJOR",

+ 2
- 2
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/hide_rules.json Datei anzeigen

@@ -2,8 +2,8 @@
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "FILE_ID",
"project": "PROJECT_ID",
"rule": "xoo:x1",
"status": "OPEN",
"severity": "MAJOR",

+ 0
- 62
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue.json Datei anzeigen

@@ -1,62 +0,0 @@
{
"total": 1,
"p": 1,
"ps": 100,
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"rule": "xoo:x1",
"status": "OPEN",
"resolution": "OPEN",
"severity": "MAJOR",
"debt": "10min",
"author": "John",
"assignee": "simon",
"reporter": "fabrice",
"actionPlan": "AP-ABCD",
"updateDate": "2017-12-04T00:00:00+0100"
}
],
"rules": [
{
"key": "xoo:x1",
"name": "Rule name",
"desc": "Rule desc",
"status": "READY"
}
],
"components": [
{
"uuid": "BCDE",
"key": "MyComponent",
"enabled" : true
},
{
"uuid": "ABCD",
"key": "MyProject",
"enabled" : true
}
],
"projects": [
{
"uuid": "ABCD",
"key": "MyProject"
}
],
"users": [
{
"login": "simon",
"name": "Simon",
"active": true,
"email": "simon@email.com"
},
{
"login": "fabrice",
"name": "Fabrice",
"active": true,
"email": "fabrice@email.com"
}
]
}

server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_linked_on_removed_file.json → server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_on_removed_file.json Datei anzeigen

@@ -2,8 +2,8 @@
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "RemovedComponent",
"project": "MyProject",
"component": "REMOVED_FILE_ID",
"project": "PROJECT_ID",
"rule": "xoo:x1",
"status": "OPEN",
"resolution": "OPEN",
@@ -13,12 +13,12 @@
],
"components": [
{
"uuid": "EDCB",
"id": "REMOVED_FILE_ID",
"key": "RemovedComponent",
"enabled" : false
},
{
"uuid": "ABCD",
"id": "PROJECT_ID",
"key": "MyProject",
"enabled" : true
}

+ 1
- 4
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_action_plan.json Datei anzeigen

@@ -10,11 +10,8 @@
"key": "AP-ABCD",
"name": "1.0",
"status": "OPEN",
"project": "MyProject",
"userLogin": "simon",
"deadLine": "2014-01-24T19:10:03+0000",
"createdAt": "2014-01-22T19:10:03+0000",
"updatedAt": "2014-01-23T19:10:03+0000"
"project": "PROJECT_ID"
}
]
}

+ 0
- 26
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_comment.json Datei anzeigen

@@ -1,26 +0,0 @@
{
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"comments": [
{
"key": "COMMENT-ABCD",
"login": "john",
"userName": "John",
"htmlText": "<em>My comment</em>",
"markdown": "*My comment*",
"updatable": true
},
{
"key": "COMMENT-ABCE",
"login": "fabrice",
"userName": "Fabrice",
"email": "fabrice@email.com",
"htmlText": "Another comment",
"markdown": "Another comment",
"updatable": false
}
]
}
]
}

+ 41
- 0
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_comments.json Datei anzeigen

@@ -0,0 +1,41 @@
{
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"rule": "xoo:x1",
"severity": "MAJOR",
"comments": [
{
"key": "COMMENT-ABCD",
"login": "john",
"htmlText": "\u003cem\u003eMy comment\u003c/em\u003e",
"markdown": "*My comment*",
"updatable": true,
"createdAt": "2014-09-09T12:00:00+0000"
},
{
"key": "COMMENT-ABCE",
"login": "fabrice",
"htmlText": "Another comment",
"markdown": "Another comment",
"updatable": false,
"createdAt": "2014-09-10T12:00:00+0000"
}
]
}
],
"users": [
{
"login": "fabrice",
"name": "Fabrice",
"email": "fabrice@email.com",
"active": true
},
{
"login": "john",
"name": "John",
"email": "",
"active": true
}
]
}

+ 0
- 58
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_snippet.json Datei anzeigen

@@ -1,58 +0,0 @@
{
"maxResultsReached": false,
"total": 1,
"p": 1,
"ps": 100,
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"line": 3,
"rule": "xoo:x1",
"status": "OPEN",
"resolution": "OPEN",
"severity": "MAJOR",
"debt": "10min",
"author": "John",
"assignee": "simon",
"reporter": "fabrice",
"updateDate": "2014-12-04T00:00:00+0100"
}
],
"rules": [
{
"key": "xoo:x1",
"name": "Rule name",
"desc": "Rule desc",
"status": "READY"
}
],
"components": [
{
"key": "MyComponent"
},
{
"key": "MyProject"
}
],
"projects": [
{
"key": "MyProject"
}
],
"users": [
{
"login": "simon",
"name": "Simon",
"active": true,
"email": "simon@email.com"
},
{
"login": "fabrice",
"name": "Fabrice",
"active": true,
"email": "fabrice@email.com"
}
]
}

server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue_with_extra_fields.json → server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/load_additional_fields.json Datei anzeigen

@@ -5,7 +5,6 @@
"assignee": "simon",
"reporter": "fabrice",
"actionPlan": "AP-ABCD",
"actionPlanName": "1.0",
"actions": [
"comment", "assign", "set_tags", "assign_to_me", "plan"
],
@@ -13,5 +12,12 @@
"confirm", "resolve"
]
}
],
"actionPlans": [
{
"key": "AP-ABCD",
"name": "1.0",
"status": "OPEN"
}
]
}

+ 24
- 0
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/response_contains_all_fields_except_additional_fields.json Datei anzeigen

@@ -0,0 +1,24 @@
{
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"rule": "xoo:x1",
"severity": "MAJOR",
"component": "FILE_ID",
"resolution": "FIXED",
"status": "RESOLVED",
"message": "the message",
"debt": "10min",
"assignee": "simon",
"reporter": "fabrice",
"author": "John",
"actionPlan": "AP-ABCD",
"tags": [
"bug",
"owasp"
],
"creationDate": "2014-09-04T01:00:00+0200",
"updateDate": "2017-12-04T00:00:00+0100"
}
]
}

+ 6
- 6
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/sort_by_updated_at.json Datei anzeigen

@@ -2,22 +2,22 @@
"issues": [
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"component": "FILE_ID",
"project": "PROJECT_ID",
"rule": "xoo:x1",
"updateDate": "2014-11-01T00:00:00+0100"
},
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac1",
"component": "MyComponent",
"project": "MyProject",
"component": "FILE_ID",
"project": "PROJECT_ID",
"rule": "xoo:x1",
"updateDate": "2014-11-02T00:00:00+0100"
},
{
"key": "82fd47d4-b650-4037-80bc-7b112bd4eac3",
"component": "MyComponent",
"project": "MyProject",
"component": "FILE_ID",
"project": "PROJECT_ID",
"rule": "xoo:x1",
"updateDate": "2014-11-03T00:00:00+0100"
}

+ 2
- 0
sonar-plugin-api/src/main/java/org/sonar/api/issue/action/Actions.java Datei anzeigen

@@ -45,4 +45,6 @@ public class Actions {
public List<Action> list() {
return actions;
}


}

+ 538
- 348
sonar-ws/src/main/gen-java/org/sonarqube/ws/Common.java
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 7814
- 3974
sonar-ws/src/main/gen-java/org/sonarqube/ws/Issues.java
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 18
- 11
sonar-ws/src/main/protobuf/ws-common.proto Datei anzeigen

@@ -25,8 +25,8 @@ option java_outer_classname = "Common";
option optimize_for = SPEED;

message Paging {
optional int32 page_index = 1;
optional int32 page_size = 2;
optional int32 pageIndex = 1;
optional int32 pageSize = 2;
optional int32 total = 3;
optional int32 pages = 4;
}
@@ -42,17 +42,24 @@ message FacetValue {
optional int64 count = 2;
}

enum Severity {
INFO = 0;
MINOR = 1;
MAJOR = 2;
CRITICAL = 3;
BLOCKER = 4;
}

message Component {
optional string uuid = 1;
optional string id = 1;
optional string key = 2;
optional int32 id = 3;
optional bool enabled = 4;
optional string qualifier = 5;
optional string name = 6;
optional string longName = 7;
optional string path = 8;
optional int32 projectId = 9;
optional int32 subProjectId = 10;
optional bool enabled = 3;
optional string qualifier = 4;
optional string name = 5;
optional string longName = 6;
optional string path = 7;
optional string project = 8;
optional string subProject = 9;
}

message Rule {

+ 66
- 19
sonar-ws/src/main/protobuf/ws-issues.proto Datei anzeigen

@@ -29,22 +29,39 @@ option optimize_for = SPEED;
// Response of URL api/issues/search
message Search {
// TODO errors
optional Paging paging = 1;
repeated Issue issues = 2;
repeated Facet facets = 3;
repeated Component projects = 4;
repeated Component components = 5;
repeated Rule rules = 6;
repeated User users = 7;

optional int64 total = 1;
optional int64 p = 2;
optional int32 ps = 3;
optional Paging paging = 4;

// Total amount of debt, only when the facet "total" is enabled
optional int64 debtTotal = 5;

repeated Issue issues = 6;
optional bool projectsPresentIfEmpty = 8;
repeated Component projects = 9;
optional bool componentsPresentIfEmpty = 10;
repeated Component components = 11;
optional bool rulesPresentIfEmpty = 12;
repeated Rule rules = 13;
optional bool usersPresentIfEmpty = 14;
repeated User users = 15;
optional bool actionPlansPresentIfEmpty = 16;
repeated ActionPlan actionPlans = 17;
optional bool languagesPresentIfEmpty = 18;
repeated Language languages = 19;
repeated Facet facets = 20;
optional bool facetsPresentIfEmpty = 21;
}

message Issue {
optional string key = 1;
optional string rule = 2;
optional string component = 3;
optional int32 componentId = 4;
optional string project = 5;
optional string subProject = 6;
optional Severity severity = 3;
optional string component = 4;
optional int64 componentId = 5;
optional string project = 6;
optional int32 line = 7;
optional string resolution = 8;
optional string status = 9;
@@ -52,27 +69,57 @@ message Issue {
optional string debt = 11;
optional string assignee = 12;
optional string reporter = 13;
optional string scmAuthor = 14;

// SCM login of the committer who introduced the issue
optional string author = 14;

optional string actionPlan = 15;
optional string actionPlanName = 16;
optional string attr = 17;
repeated string tags = 18;
repeated string transitions = 19;
repeated string actions = 20;
repeated Comment comments = 21;
optional string creationDate= 22;
optional string updateDate= 23;
optional string fUpdateAge= 24;
optional string closeDate= 25;

// the transitions allowed for the requesting user.
optional bool transitionsPresentIfEmpty=19;
repeated string transitions = 20;

// the actions allowed for the requesting user.
optional bool actionsPresentIfEmpty=21;
repeated string actions = 22;

optional bool commentsPresentIfEmpty = 23;
repeated Comment comments = 24;
optional string creationDate= 25;
optional string updateDate= 26;
optional string fUpdateAge= 27;
optional string closeDate= 28;
}

message Comment {
optional string key = 1;
optional string login = 2;
// TODO drop, it's already in field "users"
optional string email = 3;
// TODO drop, it's already in field "users"
optional string userName = 4;
optional string htmlText = 5;
// TODO rename markdownText ?
optional string markdown = 6;
optional bool updatable = 7;
optional string createdAt = 8;
}

message ActionPlan {
optional string key = 1;
optional string name = 2;

// TODO define enum
optional string status = 3;
optional string deadLine = 4;
// TODO to be renamed, is it id or key ?
optional string project = 5;
}

message Language {
optional string key = 1;
optional string name = 2;
}

Laden…
Abbrechen
Speichern