From 19397ac0aaf022afabbbdde4686edc550c8a5d36 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 28 May 2014 12:43:13 +0200 Subject: SONAR-5341 In /issues/search WS, add parameter 'extra_fields' --- .../main/java/org/sonar/api/server/ws/Request.java | 20 +- .../java/org/sonar/api/server/ws/RequestTest.java | 9 + .../sonar/server/issue/ws/IssueActionsWriter.java | 90 +++++ .../sonar/server/issue/ws/IssueSearchAction.java | 420 +++++++++++++++++++++ .../org/sonar/server/issue/ws/IssueShowAction.java | 66 +--- .../java/org/sonar/server/issue/ws/IssuesWs.java | 88 +---- .../sonar/server/platform/ServerComponents.java | 6 +- .../org/sonar/server/issue/ws/example-search.json | 3 + .../server/issue/ws/IssueActionsWriterTest.java | 209 ++++++++++ .../server/issue/ws/IssueSearchActionTest.java | 377 ++++++++++++++++++ .../sonar/server/issue/ws/IssueShowActionTest.java | 77 +--- .../org/sonar/server/issue/ws/IssuesWsTest.java | 21 +- .../issue/ws/IssueSearchActionTest/issues.json | 32 ++ .../issues_with_action_plan.json | 24 ++ .../issues_with_attributes.json | 26 ++ .../issues_with_components.json | 58 +++ .../IssueSearchActionTest/issues_with_dates.json | 26 ++ .../ws/IssueSearchActionTest/issues_with_debt.json | 24 ++ .../issues_with_extra_fields.json | 39 ++ .../IssueSearchActionTest/issues_with_rules.json | 39 ++ .../IssueSearchActionTest/issues_with_users.json | 39 ++ ...show_issue_with_actions_defined_by_plugins.json | 27 -- .../show_issue_with_assign_to_me_action.json | 27 -- .../show_issue_with_set_severity_action.json | 27 -- .../show_issue_without_assign_to_me_action.json | 29 -- 25 files changed, 1478 insertions(+), 325 deletions(-) create mode 100644 sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java create mode 100644 sonar-server/src/main/java/org/sonar/server/issue/ws/IssueSearchAction.java create mode 100644 sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java create mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues.json create mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_action_plan.json create mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_attributes.json create mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_components.json create mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_dates.json create mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_debt.json create mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_extra_fields.json create mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_rules.json create mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_users.json delete mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions_defined_by_plugins.json delete mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_assign_to_me_action.json delete mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_set_severity_action.json delete mode 100644 sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_without_assign_to_me_action.json diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java index ee422a76070..f4c0a40d3f4 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java @@ -22,9 +22,11 @@ package org.sonar.api.server.ws; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import org.apache.commons.lang.StringUtils; +import org.sonar.api.utils.DateUtils; import javax.annotation.CheckForNull; +import java.util.Date; import java.util.List; /** @@ -93,6 +95,7 @@ public abstract class Request { return values; } + @CheckForNull public List paramAsStrings(String key) { String value = param(key); if (value == null) { @@ -182,9 +185,16 @@ public abstract class Request { return result; } -// @CheckForNull -// public Date paramAsDate(String key) { -// String s = param(key); -// return s == null ? null : Long.parseLong(s); -// } + @CheckForNull + public Date paramAsDate(String key) { + String s = param(key); + if (s != null) { + Date date = DateUtils.parseDateTimeQuietly(s); + if (date != null) { + return date; + } + return DateUtils.parseDate(s); + } + return null; + } } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java index 9d2926460f6..a4b34acd653 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java @@ -24,8 +24,10 @@ import org.junit.Before; import org.junit.Test; import org.sonar.api.rule.RuleStatus; import org.sonar.api.server.ws.internal.ValidatingRequest; +import org.sonar.api.utils.DateUtils; import javax.annotation.Nullable; + import java.util.Map; import static org.fest.assertions.Assertions.assertThat; @@ -71,6 +73,7 @@ public class RequestTest { action.createParam("a_boolean"); action.createParam("a_number"); action.createParam("a_enum"); + action.createParam("a_date"); action.createParam("a_required_string").setRequired(true); action.createParam("a_required_boolean").setRequired(true); @@ -177,6 +180,12 @@ public class RequestTest { RuleStatus.BETA, RuleStatus.READY); } + @Test + public void param_as_date() throws Exception { + assertThat(request.setParam("a_date", "2014-05-27").paramAsDate("a_date")).isEqualTo(DateUtils.parseDate("2014-05-27")); + assertThat(request.setParam("a_date", "2014-05-27T15:50:45+0100").paramAsDate("a_date")).isEqualTo(DateUtils.parseDateTime("2014-05-27T15:50:45+0100")); + } + @Test public void param_as_strings() throws Exception { assertThat(request.paramAsStrings("a_string")).isNull(); diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java new file mode 100644 index 00000000000..e4d7cfa04da --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java @@ -0,0 +1,90 @@ +/* + * 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 org.sonar.api.ServerComponent; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.action.Action; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.api.web.UserRole; +import org.sonar.core.issue.workflow.Transition; +import org.sonar.server.issue.ActionService; +import org.sonar.server.issue.IssueService; +import org.sonar.server.user.UserSession; + +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; + +public class IssueActionsWriter implements ServerComponent { + + private final IssueService issueService; + private final ActionService actionService; + + public IssueActionsWriter(IssueService issueService, ActionService actionService) { + this.issueService = issueService; + this.actionService = actionService; + } + + public void writeTransitions(Issue issue, JsonWriter json) { + json.name("transitions").beginArray(); + if (UserSession.get().isLoggedIn()) { + List transitions = issueService.listTransitions(issue, UserSession.get()); + for (Transition transition : transitions) { + json.value(transition.key()); + } + } + json.endArray(); + } + + public void writeActions(Issue issue, JsonWriter json) { + json.name("actions").beginArray(); + for (String action : actions((DefaultIssue) issue)) { + json.value(action); + } + json.endArray(); + } + + private List actions(DefaultIssue issue) { + List actions = newArrayList(); + String login = UserSession.get().login(); + if (login != null) { + actions.add("comment"); + if (issue.resolution() == null) { + actions.add("assign"); + if (!login.equals(issue.assignee())) { + actions.add("assign_to_me"); + } + actions.add("plan"); + String projectKey = issue.projectKey(); + if (projectKey != null && UserSession.get().hasProjectPermission(UserRole.ISSUE_ADMIN, projectKey)) { + actions.add("set_severity"); + } + for (Action action : actionService.listAvailableActions(issue)) { + actions.add(action.key()); + } + } + } + return actions; + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueSearchAction.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueSearchAction.java new file mode 100644 index 00000000000..36d98974739 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueSearchAction.java @@ -0,0 +1,420 @@ +/* + * 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.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Strings; +import com.google.common.collect.Iterables; +import com.google.common.io.Resources; +import org.sonar.api.component.Component; +import org.sonar.api.i18n.I18n; +import org.sonar.api.issue.*; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.Rule; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.RequestHandler; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.user.User; +import org.sonar.api.utils.DateUtils; +import org.sonar.api.utils.Duration; +import org.sonar.api.utils.Durations; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; +import org.sonar.server.issue.filter.IssueFilterParameters; +import org.sonar.server.user.UserSession; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static com.google.common.collect.Lists.newArrayList; + +public class IssueSearchAction implements RequestHandler { + + private static final String EXTRA_FIELDS_PARAM = "extra_fields"; + + private final IssueFinder issueFinder; + private final IssueActionsWriter actionsWriter; + private final I18n i18n; + private final Durations durations; + + public IssueSearchAction(IssueFinder issueFinder, IssueActionsWriter actionsWriter, I18n i18n, Durations durations) { + this.issueFinder = issueFinder; + this.actionsWriter = actionsWriter; + this.i18n = i18n; + this.durations = durations; + } + + void define(WebService.NewController controller) { + WebService.NewAction action = controller.createAction("search") + .setDescription("Get a list of issues. If the number of issues is greater than 10,000, only the first 10,000 ones are returned by the web service. " + + "Requires Browse permission on project(s)") + .setSince("3.6") + .setHandler(this) + .setResponseExample(Resources.getResource(this.getClass(), "example-search.json")); + + 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.COMPONENTS) + .setDescription("To retrieve issues associated to a specific list of components (comma-separated list of component keys). " + + "Note that if you set the value to a project key, only issues associated to this project are retrieved. " + + "Issues associated to its sub-components (such as files, packages, etc.) are not retrieved. See also componentRoots") + .setExampleValue("org.apache.struts:struts:org.apache.struts.Action"); + action.createParam(IssueFilterParameters.COMPONENT_ROOTS) + .setDescription("To retrieve issues associated to a specific list of components and their sub-components (comma-separated list of component keys). " + + "Views are not supported") + .setExampleValue("org.apache.struts:struts"); + action.createParam(IssueFilterParameters.RULES) + .setDescription("Comma-separated list of coding rule keys. Format is :") + .setExampleValue("squid:AvoidCycles"); + action.createParam(IssueFilterParameters.HIDE_RULES) + .setDescription("To not return rules") + .setBooleanPossibleValues(); + 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.ASSIGNEES) + .setDescription("Comma-separated list of assignee logins") + .setExampleValue("admin,usera"); + 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(EXTRA_FIELDS_PARAM) + .setDescription("Add some extra fields on each issue. Available since 4.4") + .setPossibleValues("actions", "transitions", "assigneeName", "actionPlanName"); + 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 (inclusive). Format: date or datetime ISO formats") + .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.PAGE_SIZE) + .setDescription("Maximum number of results per page. " + + "Default value: 100 (except when the 'components' parameter is set, value is set to \"-1\" in this case). " + + "If set to \"-1\", the max possible value is used") + .setExampleValue("50"); + action.createParam(IssueFilterParameters.PAGE_INDEX) + .setDescription("Index of the selected page") + .setDefaultValue("1") + .setExampleValue("2"); + action.createParam(IssueFilterParameters.SORT) + .setDescription("Sort field") + .setPossibleValues(IssueQuery.SORTS); + action.createParam(IssueFilterParameters.ASC) + .setDescription("Ascending sort") + .setBooleanPossibleValues(); + } + + @Override + public void handle(Request request, Response response) { + IssueQueryResult queryResult = issueFinder.find(createQuery(request)); + + JsonWriter json = response.newJsonWriter(); + json.beginObject(); + + writePaging(queryResult, json); + writeIssues(queryResult, request.paramAsStrings(EXTRA_FIELDS_PARAM), json); + writeComponents(queryResult, json); + writeProjects(queryResult, json); + writeRules(queryResult, json); + writeUsers(queryResult, json); + + json.endObject().close(); + } + + private void writePaging(IssueQueryResult result, JsonWriter json) { + json.prop("maxResultsReached", result.maxResultsReached()); + json.name("paging").beginObject() + .prop("pageIndex", result.paging().pageIndex()) + .prop("pageSize", result.paging().pageSize()) + .prop("total", result.paging().total()) + .prop("fTotal", i18n.formatInteger(UserSession.get().locale(), result.paging().total())) + .prop("pages", result.paging().pages()) + .endObject(); + } + + private void writeIssues(IssueQueryResult result, @Nullable List extraFields, JsonWriter json) { + json.name("issues").beginArray(); + + for (Issue i : result.issues()) { + json.beginObject(); + + DefaultIssue issue = (DefaultIssue) i; + String actionPlanKey = issue.actionPlanKey(); + Duration debt = issue.debt(); + Date updateDate = issue.updateDate(); + Date closeDate = issue.closeDate(); + + json + .prop("key", issue.key()) + .prop("component", issue.componentKey()) + .prop("project", issue.projectKey()) + .prop("rule", issue.ruleKey().toString()) + .prop("status", issue.status()) + .prop("resolution", issue.resolution()) + .prop("severity", issue.severity()) + .prop("message", issue.message()) + .prop("line", issue.line()) + .prop("debt", debt != null ? durations.format(UserSession.get().locale(), debt, Durations.DurationFormat.SHORT) : null) + .prop("reporter", issue.reporter()) + .prop("assignee", issue.assignee()) + .prop("author", issue.authorLogin()) + .prop("actionPlan", actionPlanKey) + .prop("creationDate", DateUtils.formatDateTime(issue.creationDate())) + .prop("updateDate", updateDate != null ? DateUtils.formatDateTime(updateDate) : null) + .prop("fUpdateAge", formatAgeDate(updateDate)) + .prop("closeDate", closeDate != null ? DateUtils.formatDateTime(closeDate) : null); + + writeIssueAttributes(issue, json); + writeIssueExtraFields(result, issue, extraFields, json); + json.endObject(); + } + + json.endArray(); + } + + private void writeIssueAttributes(Issue issue, JsonWriter json) { + if (!issue.attributes().isEmpty()) { + json.name("attr").beginObject(); + for (Map.Entry entry : issue.attributes().entrySet()) { + json.prop(entry.getKey(), entry.getValue()); + } + json.endObject(); + } + } + + private void writeIssueExtraFields(IssueQueryResult result, Issue issue, @Nullable List extraFields, JsonWriter json) { + if (extraFields != null) { + if (extraFields.contains("actions")) { + actionsWriter.writeActions(issue, json); + } + if (extraFields.contains("transitions")) { + actionsWriter.writeTransitions(issue, json); + } + String assignee = issue.assignee(); + if (extraFields.contains("assigneeName") && assignee != null) { + User user = result.user(assignee); + json.prop("assigneeName", user != null ? user.name() : null); + } + String actionPlanKey = issue.actionPlanKey(); + if (extraFields.contains("actionPlanName") && actionPlanKey != null) { + ActionPlan actionPlan = result.actionPlan(issue); + json.prop("actionPlanName", actionPlan != null ? actionPlan.name() : null); + } + } + } + + private void writeComponents(IssueQueryResult result, JsonWriter json) { + json.name("components").beginArray(); + for (Component component : result.components()) { + ComponentDto componentDto = (ComponentDto) component; + json.beginObject() + .prop("key", component.key()) + .prop("id", componentDto.getId()) + .prop("qualifier", component.qualifier()) + .prop("name", component.name()) + .prop("longName", component.longName()) + .prop("path", component.path()) + // On a root project, subProjectId is null but projectId is equal to itself, which make no sense. + .prop("projectId", (componentDto.projectId() != null && componentDto.subProjectId() != null) ? componentDto.projectId() : null) + .prop("subProjectId", componentDto.subProjectId()) + .endObject(); + } + json.endArray(); + } + + private void writeProjects(IssueQueryResult result, JsonWriter json) { + json.name("projects").beginArray(); + for (Component project : result.projects()) { + ComponentDto componentDto = (ComponentDto) project; + json.beginObject() + .prop("key", project.key()) + .prop("id", componentDto.getId()) + .prop("qualifier", project.qualifier()) + .prop("name", project.name()) + .prop("longName", project.longName()) + .endObject(); + } + json.endArray(); + } + + private void writeRules(IssueQueryResult result, JsonWriter json) { + json.name("rules").beginArray(); + for (Rule rule : result.rules()) { + json.beginObject() + .prop("key", rule.ruleKey().toString()) + .prop("name", rule.getName()) + .prop("desc", rule.getDescription()) + .prop("status", rule.getStatus()) + .endObject(); + } + json.endArray(); + } + + private void writeUsers(IssueQueryResult result, JsonWriter json) { + json.name("users").beginArray(); + for (User user : result.users()) { + json.beginObject() + .prop("login", user.login()) + .prop("name", user.name()) + .prop("active", user.active()) + .prop("email", user.email()) + .endObject(); + } + json.endArray(); + } + + +// +// private void writeTransitions(Issue issue, JsonWriter json) { +// json.name("transitions").beginArray(); +// if (UserSession.get().isLoggedIn()) { +// List transitions = issueService.listTransitions(issue, UserSession.get()); +// for (Transition transition : transitions) { +// json.value(transition.key()); +// } +// } +// json.endArray(); +// } +// +// private void writeActions(DefaultIssue issue, JsonWriter json) { +// json.name("actions").beginArray(); +// for (String action : actions(issue)) { +// json.value(action); +// } +// json.endArray(); +// } +// +// private List actions(DefaultIssue issue) { +// List actions = newArrayList(); +// String login = UserSession.get().login(); +// if (login != null) { +// actions.add("comment"); +// if (issue.resolution() == null) { +// actions.add("assign"); +// if (!login.equals(issue.assignee())) { +// actions.add("assign_to_me"); +// } +// actions.add("plan"); +// String projectKey = issue.projectKey(); +// if (projectKey != null && UserSession.get().hasProjectPermission(UserRole.ISSUE_ADMIN, projectKey)) { +// actions.add("set_severity"); +// } +// for (Action action : actionService.listAvailableActions(issue)) { +// actions.add(action.key()); +// } +// } +// } +// return actions; +// } +// + + @CheckForNull + private String formatAgeDate(@Nullable Date date) { + if (date != null) { + return i18n.ageFromNow(UserSession.get().locale(), date); + } + return null; + } + + @CheckForNull + private static Collection stringsToRules(@Nullable Collection rules) { + if (rules != null) { + return newArrayList(Iterables.transform(rules, new Function() { + @Override + public RuleKey apply(@Nullable String s) { + return s != null ? RuleKey.parse(s) : null; + } + })); + } + return null; + } + + @VisibleForTesting + static IssueQuery createQuery(Request request) { + IssueQuery.Builder builder = IssueQuery.builder() + .requiredRole(UserRole.USER) + .issueKeys(request.paramAsStrings(IssueFilterParameters.ISSUES)) + .severities(request.paramAsStrings(IssueFilterParameters.SEVERITIES)) + .statuses(request.paramAsStrings(IssueFilterParameters.STATUSES)) + .resolutions(request.paramAsStrings(IssueFilterParameters.RESOLUTIONS)) + .resolved(request.paramAsBoolean(IssueFilterParameters.RESOLVED)) + .components(request.paramAsStrings(IssueFilterParameters.COMPONENTS)) + .componentRoots(request.paramAsStrings(IssueFilterParameters.COMPONENT_ROOTS)) + .rules(stringsToRules(request.paramAsStrings(IssueFilterParameters.RULES))) + .actionPlans(request.paramAsStrings(IssueFilterParameters.ACTION_PLANS)) + .reporters(request.paramAsStrings(IssueFilterParameters.REPORTERS)) + .assignees(request.paramAsStrings(IssueFilterParameters.ASSIGNEES)) + .languages(request.paramAsStrings(IssueFilterParameters.LANGUAGES)) + .assigned(request.paramAsBoolean(IssueFilterParameters.ASSIGNED)) + .planned(request.paramAsBoolean(IssueFilterParameters.PLANNED)) + .hideRules(request.paramAsBoolean(IssueFilterParameters.HIDE_RULES)) + .createdAt(request.paramAsDate(IssueFilterParameters.CREATED_AT)) + .createdAfter(request.paramAsDate(IssueFilterParameters.CREATED_AFTER)) + .createdBefore(request.paramAsDate(IssueFilterParameters.CREATED_BEFORE)) + .pageSize(request.paramAsInt(IssueFilterParameters.PAGE_SIZE)) + .pageIndex(request.paramAsInt(IssueFilterParameters.PAGE_INDEX)); + String sort = request.param(IssueFilterParameters.SORT); + if (!Strings.isNullOrEmpty(sort)) { + builder.sort(sort); + builder.asc(request.paramAsBoolean(IssueFilterParameters.ASC)); + } + return builder.build(); + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java index ec121b7c206..2a94bd9b2ac 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java @@ -25,7 +25,6 @@ import com.google.common.io.Resources; import org.sonar.api.component.Component; import org.sonar.api.i18n.I18n; import org.sonar.api.issue.*; -import org.sonar.api.issue.action.Action; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.FieldDiffs; import org.sonar.api.server.debt.DebtCharacteristic; @@ -41,14 +40,11 @@ import org.sonar.api.utils.Durations; import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; -import org.sonar.core.issue.workflow.Transition; import org.sonar.markdown.Markdown; import org.sonar.server.debt.DebtModelService; import org.sonar.server.exceptions.NotFoundException; -import org.sonar.server.issue.ActionService; import org.sonar.server.issue.IssueChangelog; import org.sonar.server.issue.IssueChangelogService; -import org.sonar.server.issue.IssueService; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; @@ -58,24 +54,20 @@ import java.util.Arrays; import java.util.Date; import java.util.List; -import static com.google.common.collect.Lists.newArrayList; - public class IssueShowAction implements RequestHandler { private final IssueFinder issueFinder; - private final IssueService issueService; private final IssueChangelogService issueChangelogService; - private final ActionService actionService; + private final IssueActionsWriter actionsWriter; private final DebtModelService debtModel; private final I18n i18n; private final Durations durations; - public IssueShowAction(IssueFinder issueFinder, IssueService issueService, IssueChangelogService issueChangelogService, ActionService actionService, + public IssueShowAction(IssueFinder issueFinder, IssueChangelogService issueChangelogService, IssueActionsWriter actionsWriter, DebtModelService debtModel, I18n i18n, Durations durations) { this.issueFinder = issueFinder; - this.issueService = issueService; this.issueChangelogService = issueChangelogService; - this.actionService = actionService; + this.actionsWriter = actionsWriter; this.debtModel = debtModel; this.i18n = i18n; this.durations = durations; @@ -96,7 +88,9 @@ public class IssueShowAction implements RequestHandler { @Override public void handle(Request request, Response response) { String issueKey = request.mandatoryParam("key"); - IssueQueryResult queryResult = issueFinder.find(IssueQuery.builder().issueKeys(Arrays.asList(issueKey)).build()); + IssueQueryResult queryResult = issueFinder.find(IssueQuery.builder() + .requiredRole(UserRole.USER) + .issueKeys(Arrays.asList(issueKey)).build()); if (queryResult.issues().size() != 1) { throw new NotFoundException("Issue not found: " + issueKey); } @@ -106,8 +100,8 @@ public class IssueShowAction implements RequestHandler { json.beginObject().name("issue").beginObject(); writeIssue(queryResult, issue, json); - writeTransitions(issue, json); - writeActions(issue, json); + actionsWriter.writeActions(issue, json); + actionsWriter.writeTransitions(issue, json); writeComments(queryResult, issue, json); writeChangelog(issue, json); @@ -220,48 +214,6 @@ public class IssueShowAction implements RequestHandler { return null; } - private void writeTransitions(Issue issue, JsonWriter json) { - json.name("transitions").beginArray(); - if (UserSession.get().isLoggedIn()) { - List transitions = issueService.listTransitions(issue, UserSession.get()); - for (Transition transition : transitions) { - json.value(transition.key()); - } - } - json.endArray(); - } - - private void writeActions(DefaultIssue issue, JsonWriter json) { - json.name("actions").beginArray(); - for (String action : actions(issue)) { - json.value(action); - } - json.endArray(); - } - - private List actions(DefaultIssue issue) { - List actions = newArrayList(); - String login = UserSession.get().login(); - if (login != null) { - actions.add("comment"); - if (issue.resolution() == null) { - actions.add("assign"); - if (!login.equals(issue.assignee())) { - actions.add("assign_to_me"); - } - actions.add("plan"); - String projectKey = issue.projectKey(); - if (projectKey != null && UserSession.get().hasProjectPermission(UserRole.ISSUE_ADMIN, projectKey)) { - actions.add("set_severity"); - } - for (Action action : actionService.listAvailableActions(issue)) { - actions.add(action.key()); - } - } - } - return actions; - } - private void writeComments(IssueQueryResult queryResult, Issue issue, JsonWriter json) { json.name("comments").beginArray(); String login = UserSession.get().login(); @@ -311,7 +263,7 @@ public class IssueShowAction implements RequestHandler { json.endArray(); } - private void addUserWithLabel(IssueQueryResult result, @Nullable String value, String field, JsonWriter json) { + private static void addUserWithLabel(IssueQueryResult result, @Nullable String value, String field, JsonWriter json) { if (value != null) { User user = result.user(value); json diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java index 302d5a0c487..d7de0ebe9db 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java @@ -21,17 +21,18 @@ package org.sonar.server.issue.ws; import com.google.common.io.Resources; import org.sonar.api.issue.DefaultTransitions; -import org.sonar.api.issue.Issue; import org.sonar.api.rule.Severity; import org.sonar.api.server.ws.RailsHandler; import org.sonar.api.server.ws.WebService; public class IssuesWs implements WebService { - private final IssueShowAction showHandler; + private final IssueShowAction showAction; + private final IssueSearchAction searchAction; - public IssuesWs(IssueShowAction showHandler) { - this.showHandler = showHandler; + public IssuesWs(IssueShowAction showAction, IssueSearchAction searchAction) { + this.showAction = showAction; + this.searchAction = searchAction; } @Override @@ -40,8 +41,9 @@ public class IssuesWs implements WebService { controller.setDescription("Coding rule issues"); controller.setSince("3.6"); - showHandler.define(controller); - defineSearchAction(controller); + showAction.define(controller); + searchAction.define(controller); + defineChangelogAction(controller); defineAssignAction(controller); defineAddCommentAction(controller); @@ -58,80 +60,6 @@ public class IssuesWs implements WebService { controller.done(); } - private void defineSearchAction(NewController controller) { - WebService.NewAction action = controller.createAction("search") - .setDescription("Get a list of issues. If the number of issues is greater than 10,000, only the first 10,000 ones are returned by the web service. " + - "Requires Browse permission on project(s)") - .setSince("3.6") - .setHandler(RailsHandler.INSTANCE) - .setResponseExample(Resources.getResource(this.getClass(), "example-search.json")); - - action.createParam("issues") - .setDescription("Comma-separated list of issue keys") - .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef"); - action.createParam("severities") - .setDescription("Comma-separated list of severities") - .setExampleValue(Severity.BLOCKER + "," + Severity.CRITICAL) - .setPossibleValues(Severity.ALL); - action.createParam("statuses") - .setDescription("Comma-separated list of statuses") - .setExampleValue(Issue.STATUS_OPEN + "," + Issue.STATUS_REOPENED) - .setPossibleValues(Issue.STATUSES); - action.createParam("resolutions") - .setDescription("Comma-separated list of resolutions") - .setExampleValue(Issue.RESOLUTION_FIXED + "," + Issue.RESOLUTION_REMOVED) - .setPossibleValues(Issue.RESOLUTIONS); - action.createParam("resolved") - .setDescription("To match resolved or unresolved issues") - .setBooleanPossibleValues(); - action.createParam("components") - .setDescription("To retrieve issues associated to a specific list of components (comma-separated list of component keys). " + - "Note that if you set the value to a project key, only issues associated to this project are retrieved. " + - "Issues associated to its sub-components (such as files, packages, etc.) are not retrieved. See also componentRoots") - .setExampleValue("org.apache.struts:struts:org.apache.struts.Action"); - action.createParam("componentRoots") - .setDescription("To retrieve issues associated to a specific list of components and their sub-components (comma-separated list of component keys). " + - "Views are not supported") - .setExampleValue("org.apache.struts:struts"); - action.createParam("rules") - .setDescription("Comma-separated list of coding rule keys. Format is :") - .setExampleValue("squid:AvoidCycles"); - action.createParam("actionPlans") - .setDescription("Comma-separated list of action plan keys (not names)") - .setExampleValue("3f19de90-1521-4482-a737-a311758ff513"); - action.createParam("planned") - .setDescription("To retrieve issues associated to an action plan or not") - .setBooleanPossibleValues(); - action.createParam("reporters") - .setDescription("Comma-separated list of reporter logins") - .setExampleValue("admin"); - action.createParam("assignees") - .setDescription("Comma-separated list of assignee logins") - .setExampleValue("admin,usera"); - action.createParam("assigned") - .setDescription("To retrieve assigned or unassigned issues") - .setBooleanPossibleValues(); - action.createParam("extra_fields") - .setDescription("Add some extra fields on each issue. Available since 4.4") - .setPossibleValues("actions", "transitions"); - action.createParam("createdAfter") - .setDescription("To retrieve issues created after the given date (inclusive). Format: date or datetime ISO formats") - .setExampleValue("2013-05-01 (or 2013-05-01T13:00:00+0100)"); - action.createParam("createdBefore") - .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("pageSize") - .setDescription("Maximum number of results per page. " + - "Default value: 100 (except when the 'components' parameter is set, value is set to \"-1\" in this case). " + - "If set to \"-1\", the max possible value is used") - .setExampleValue("50"); - action.createParam("pageIndex") - .setDescription("Index of the selected page") - .setDefaultValue("1") - .setExampleValue("2"); - RailsHandler.addFormatParam(action); - } - private void defineChangelogAction(NewController controller) { WebService.NewAction action = controller.createAction("changelog") .setDescription("Display changelog of an issue") diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 180cbcfdf31..99fed3068f4 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -96,6 +96,8 @@ import org.sonar.server.issue.actionplan.ActionPlanWs; import org.sonar.server.issue.filter.IssueFilterService; import org.sonar.server.issue.filter.IssueFilterWriter; import org.sonar.server.issue.filter.IssueFilterWs; +import org.sonar.server.issue.ws.IssueActionsWriter; +import org.sonar.server.issue.ws.IssueSearchAction; import org.sonar.server.issue.ws.IssueShowAction; import org.sonar.server.issue.ws.IssuesWs; import org.sonar.server.measure.MeasureFilterEngine; @@ -421,8 +423,10 @@ class ServerComponents { pico.addSingleton(Actions.class); pico.addSingleton(IssueBulkChangeService.class); pico.addSingleton(IssueChangelogFormatter.class); - pico.addSingleton(IssueShowAction.class); pico.addSingleton(IssuesWs.class); + pico.addSingleton(IssueShowAction.class); + pico.addSingleton(IssueSearchAction.class); + pico.addSingleton(IssueActionsWriter.class); // issue filters pico.addSingleton(IssueFilterService.class); diff --git a/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json b/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json index d22e748ea58..8c248e73108 100644 --- a/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json +++ b/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json @@ -29,6 +29,9 @@ "createdAt": "2013-05-13T18:08:34+0200" } ], + "attr": { + "jira-issue-key": "SONAR-1234" + }, "actions": ["link-to-jira"], "transitions": [ "unconfirm", diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java new file mode 100644 index 00000000000..bb8d97e87b0 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java @@ -0,0 +1,209 @@ +/* + * 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 org.json.JSONException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.skyscreamer.jsonassert.JSONAssert; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.action.Action; +import org.sonar.api.issue.internal.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.workflow.Transition; +import org.sonar.server.issue.ActionService; +import org.sonar.server.issue.IssueService; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.user.UserSession; + +import java.io.StringWriter; + +import static com.google.common.collect.Lists.newArrayList; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class IssueActionsWriterTest { + + @Mock + IssueService issueService; + + @Mock + ActionService actionService; + + IssueActionsWriter writer; + + @Before + public void setUp() throws Exception { + writer = new IssueActionsWriter(issueService, actionService); + } + + @Test + public void write_all_standard_actions() throws Exception { + Issue issue = new DefaultIssue() + .setKey("ABCD") + .setComponentKey("sample:src/main/xoo/sample/Sample.xoo") + .setProjectKey("sample") + .setRuleKey(RuleKey.of("squid", "AvoidCycle")); + + MockUserSession.set().setLogin("john").addProjectPermissions(UserRole.ISSUE_ADMIN, "sample"); + + testActions(issue, + "{\"actions\": " + + "[" + + "\"comment\", \"assign\", \"assign_to_me\", \"plan\", \"set_severity\"\n" + + "]}" + ); + } + + @Test + public void write_plugin_actions() throws Exception { + Issue issue = new DefaultIssue() + .setKey("ABCD") + .setComponentKey("sample:src/main/xoo/sample/Sample.xoo") + .setProjectKey("sample") + .setRuleKey(RuleKey.of("squid", "AvoidCycle")); + + MockUserSession.set().setLogin("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\", \"assign_to_me\", \"plan\", \"link-to-jira\"\n" + + "]}" + ); + } + + @Test + public void write_only_comment_action() throws Exception { + Issue issue = new DefaultIssue() + .setKey("ABCD") + .setComponentKey("sample:src/main/xoo/sample/Sample.xoo") + .setProjectKey("sample") + .setRuleKey(RuleKey.of("squid", "AvoidCycle")) + .setResolution("CLOSED"); + + MockUserSession.set().setLogin("john"); + + testActions(issue, + "{\"actions\": " + + "[" + + "\"comment\"" + + "]}" + ); + } + + @Test + public void write_no_action_if_not_logged() throws Exception { + Issue issue = new DefaultIssue() + .setKey("ABCD") + .setComponentKey("sample:src/main/xoo/sample/Sample.xoo") + .setProjectKey("sample") + .setRuleKey(RuleKey.of("squid", "AvoidCycle")); + + MockUserSession.set(); + + testActions(issue, + "{\"actions\": []}" + ); + } + + @Test + public void write_actions_without_assign_to_me() throws Exception { + Issue issue = new DefaultIssue() + .setKey("ABCD") + .setComponentKey("sample:src/main/xoo/sample/Sample.xoo") + .setProjectKey("sample") + .setRuleKey(RuleKey.of("squid", "AvoidCycle")) + .setAssignee("john"); + + MockUserSession.set().setLogin("john"); + + testActions(issue, + "{\"actions\": " + + "[" + + "\"comment\", \"assign\", \"plan\"\n" + + "]}" + ); + } + + @Test + public void write_transitions() throws Exception { + Issue issue = new DefaultIssue() + .setKey("ABCD") + .setComponentKey("sample:src/main/xoo/sample/Sample.xoo") + .setProjectKey("sample") + .setRuleKey(RuleKey.of("squid", "AvoidCycle")); + + when(issueService.listTransitions(eq(issue), any(UserSession.class))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); + MockUserSession.set().setLogin("john"); + + testTransitions(issue, + "{\"transitions\": [\n" + + " \"reopen\"\n" + + " ]}" + ); + } + + @Test + public void write_no_transitions() throws Exception { + Issue issue = new DefaultIssue() + .setKey("ABCD") + .setComponentKey("sample:src/main/xoo/sample/Sample.xoo") + .setProjectKey("sample") + .setRuleKey(RuleKey.of("squid", "AvoidCycle")); + + MockUserSession.set().setLogin("john"); + + testTransitions(issue, + "{\"transitions\": []}" + ); + } + + private void testActions(Issue issue, String expected) throws JSONException { + StringWriter output = new StringWriter(); + JsonWriter jsonWriter = JsonWriter.of(output); + jsonWriter.beginObject(); + writer.writeActions(issue, jsonWriter); + jsonWriter.endObject(); + JSONAssert.assertEquals(output.toString(), expected, true); + } + + private void testTransitions(Issue issue, String expected) throws JSONException { + StringWriter output = new StringWriter(); + JsonWriter jsonWriter = JsonWriter.of(output); + jsonWriter.beginObject(); + writer.writeTransitions(issue, jsonWriter); + jsonWriter.endObject(); + JSONAssert.assertEquals(output.toString(), expected, true); + } + +} diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java new file mode 100644 index 00000000000..5eecc61fbef --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java @@ -0,0 +1,377 @@ +/* + * 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 org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.component.Component; +import org.sonar.api.i18n.I18n; +import org.sonar.api.issue.ActionPlan; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.IssueFinder; +import org.sonar.api.issue.IssueQuery; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.Rule; +import org.sonar.api.user.User; +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.component.ComponentDto; +import org.sonar.core.issue.DefaultActionPlan; +import org.sonar.core.issue.DefaultIssueQueryResult; +import org.sonar.core.issue.workflow.Transition; +import org.sonar.core.user.DefaultUser; +import org.sonar.server.issue.ActionService; +import org.sonar.server.issue.IssueService; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.user.UserSession; +import org.sonar.server.ws.WsTester; + +import java.util.*; + +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Maps.newHashMap; +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class IssueSearchActionTest { + + @Mock + IssueFinder issueFinder; + + @Mock + IssueService issueService; + + @Mock + ActionService actionService; + + @Mock + I18n i18n; + + @Mock + Durations durations; + + List issues; + DefaultIssueQueryResult result; + + Date issueCreationDate; + + WsTester tester; + + @Before + public void setUp() throws Exception { + issues = new ArrayList(); + result = new DefaultIssueQueryResult(issues); + when(issueFinder.find(any(IssueQuery.class))).thenReturn(result); + + issueCreationDate = DateUtils.parseDateTime("2014-01-22T19:10:03+0100"); + when(i18n.formatDateTime(any(Locale.class), eq(issueCreationDate))).thenReturn("Jan 22, 2014 10:03 AM"); + + result.setMaxResultsReached(true); + result.setPaging(Paging.create(100, 1, 2)); + when(i18n.formatInteger(any(Locale.class), eq(2))).thenReturn("2"); + + tester = new WsTester(new IssuesWs(mock(IssueShowAction.class), new IssueSearchAction(issueFinder, new IssueActionsWriter(issueService, actionService), i18n, durations))); + } + + @Test + public void issues() throws Exception { + String issueKey = "ABCD"; + Issue issue = new DefaultIssue() + .setKey(issueKey) + .setComponentKey("sample:src/main/xoo/sample/Sample.xoo") + .setProjectKey("sample") + .setRuleKey(RuleKey.of("squid", "AvoidCycle")) + .setActionPlanKey("AP-ABCD") + .setLine(12) + .setEffortToFix(2.0) + .setMessage("Fix it") + .setResolution("FIXED") + .setStatus("CLOSED") + .setSeverity("MAJOR") + .setAssignee("john") + .setReporter("steven") + .setAuthorLogin("Henry") + .setCreationDate(issueCreationDate); + issues.add(issue); + + WsTester.TestRequest request = tester.newGetRequest("api/issues", "search"); + request.execute().assertJson(getClass(), "issues.json"); + } + + @Test + public void issues_with_components() throws Exception { + String issueKey = "ABCD"; + Issue issue = new DefaultIssue() + .setKey(issueKey) + .setComponentKey("sample:src/main/xoo/sample/Sample.xoo") + .setProjectKey("sample") + .setRuleKey(RuleKey.of("squid", "AvoidCycle")) + .setActionPlanKey("AP-ABCD") + .setLine(12) + .setEffortToFix(2.0) + .setMessage("Fix it") + .setResolution("FIXED") + .setStatus("CLOSED") + .setSeverity("MAJOR") + .setAssignee("john") + .setReporter("steven") + .setAuthorLogin("Henry") + .setCreationDate(issueCreationDate); + issues.add(issue); + + ComponentDto component = new ComponentDto() + .setId(10L) + .setKey("sample:src/main/xoo/sample/Sample.xoo") + .setLongName("src/main/xoo/sample/Sample.xoo") + .setName("Sample.xoo") + .setQualifier("FIL") + .setPath("src/main/xoo/sample/Sample.xoo") + .setSubProjectId(7L) + .setProjectId(7L); + + ComponentDto project = new ComponentDto() + .setId(7L) + .setKey("sample") + .setLongName("Sample") + .setName("Sample") + .setQualifier("TRK") + .setProjectId(7L); + + result.addComponents(Lists.newArrayList(component, project)); + result.addProjects(Lists.newArrayList(project)); + + WsTester.TestRequest request = tester.newGetRequest("api/issues", "search"); + request.execute().assertJson(getClass(), "issues_with_components.json"); + } + + @Test + public void issues_with_rules() throws Exception { + String issueKey = "ABCD"; + Issue issue = new DefaultIssue() + .setKey(issueKey) + .setComponentKey("sample:src/main/xoo/sample/Sample.xoo") + .setProjectKey("sample") + .setRuleKey(RuleKey.of("squid", "AvoidCycle")) + .setActionPlanKey("AP-ABCD") + .setLine(12) + .setEffortToFix(2.0) + .setMessage("Fix it") + .setResolution("FIXED") + .setStatus("CLOSED") + .setSeverity("MAJOR") + .setAssignee("john") + .setReporter("steven") + .setAuthorLogin("Henry") + .setCreationDate(issueCreationDate); + issues.add(issue); + + result.addRules(newArrayList( + Rule.create("squid", "AvoidCycle").setName("Avoid cycle").setDescription("Avoid cycle description") + )); + + WsTester.TestRequest request = tester.newGetRequest("api/issues", "search"); + request.execute().assertJson(getClass(), "issues_with_rules.json"); + } + + @Test + public void issues_with_users() throws Exception { + String issueKey = "ABCD"; + Issue issue = new DefaultIssue() + .setKey(issueKey) + .setComponentKey("sample:src/main/xoo/sample/Sample.xoo") + .setProjectKey("sample") + .setRuleKey(RuleKey.of("squid", "AvoidCycle")) + .setActionPlanKey("AP-ABCD") + .setLine(12) + .setEffortToFix(2.0) + .setMessage("Fix it") + .setResolution("FIXED") + .setStatus("CLOSED") + .setSeverity("MAJOR") + .setAssignee("john") + .setReporter("steven") + .setAuthorLogin("Henry") + .setCreationDate(issueCreationDate); + issues.add(issue); + + result.addUsers(Lists.newArrayList( + new DefaultUser().setName("John").setLogin("john").setActive(true).setEmail("john@email.com") + )); + + WsTester.TestRequest request = tester.newGetRequest("api/issues", "search"); + request.execute().assertJson(getClass(), "issues_with_users.json"); + } + + @Test + public void issues_with_dates() throws Exception { + Date creationDate = DateUtils.parseDateTime("2014-01-22T19:10:03+0100"); + Date updateDate = DateUtils.parseDateTime("2014-01-23T19:10:03+0100"); + Date closedDate = DateUtils.parseDateTime("2014-01-24T19:10:03+0100"); + + Issue issue = createStandardIssue() + .setCreationDate(creationDate) + .setUpdateDate(updateDate) + .setCloseDate(closedDate); + issues.add(issue); + + when(i18n.formatDateTime(any(Locale.class), eq(creationDate))).thenReturn("Jan 22, 2014 10:03 AM"); + when(i18n.formatDateTime(any(Locale.class), eq(updateDate))).thenReturn("Jan 23, 2014 10:03 AM"); + when(i18n.ageFromNow(any(Locale.class), eq(updateDate))).thenReturn("9 days"); + when(i18n.formatDateTime(any(Locale.class), eq(closedDate))).thenReturn("Jan 24, 2014 10:03 AM"); + + WsTester.TestRequest request = tester.newGetRequest("api/issues", "search"); + request.execute().assertJson(getClass(), "issues_with_dates.json"); + } + + @Test + public void issues_with_debt() throws Exception { + Duration debt = (Duration.create(7260L)); + Issue issue = createStandardIssue().setDebt(debt); + issues.add(issue); + + when(durations.format(any(Locale.class), eq(debt), eq(Durations.DurationFormat.SHORT))).thenReturn("2 hours 1 minutes"); + + WsTester.TestRequest request = tester.newGetRequest("api/issues", "search"); + request.execute().assertJson(getClass(), "issues_with_debt.json"); + } + + @Test + public void issues_with_action_plan() throws Exception { + Issue issue = createStandardIssue() + .setActionPlanKey("AP-ABCD"); + issues.add(issue); + + MockUserSession.set(); + WsTester.TestRequest request = tester.newGetRequest("api/issues", "search"); + request.execute().assertJson(getClass(), "issues_with_action_plan.json"); + } + + @Test + public void issues_with_attributes() throws Exception { + Issue issue = createStandardIssue() + .setAttribute("jira-issue-key", "SONAR-1234"); + issues.add(issue); + + WsTester.TestRequest request = tester.newGetRequest("api/issues", "search"); + request.execute().assertJson(getClass(), "issues_with_attributes.json"); + } + + @Test + public void issues_with_extra_fields() throws Exception { + Issue issue = createStandardIssue() + .setActionPlanKey("AP-ABCD") + .setAssignee("john"); + issues.add(issue); + + MockUserSession.set().setLogin("john"); + when(issueService.listTransitions(eq(issue), any(UserSession.class))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); + + result.addActionPlans(newArrayList((ActionPlan) new DefaultActionPlan().setKey("AP-ABCD").setName("1.0"))); + + result.addUsers(Lists.newArrayList( + new DefaultUser().setName("John").setLogin("john") + )); + + WsTester.TestRequest request = tester.newGetRequest("api/issues", "search").setParam("extra_fields", "actions,transitions,assigneeName,actionPlanName"); + request.execute().assertJson(getClass(), "issues_with_extra_fields.json"); + } + + @Test + public void verify_issue_query_parameters() throws Exception { + Map map = newHashMap(); + map.put("issues", "ABCDE1234"); + map.put("severities", "MAJOR,MINOR"); + map.put("statuses", "CLOSED"); + map.put("resolutions", "FALSE-POSITIVE"); + map.put("resolved", "true"); + map.put("components", "org.apache"); + map.put("componentRoots", "org.sonar"); + map.put("reporters", "marilyn"); + map.put("assignees", "joanna"); + map.put("languages", "xoo"); + map.put("assigned", "true"); + map.put("planned", "true"); + map.put("hideRules", "true"); + map.put("createdAt", "2013-04-15T09:08:24+0200"); + map.put("createdAfter", "2013-04-16T09:08:24+0200"); + map.put("createdBefore", "2013-04-17T09:08:24+0200"); + map.put("rules", "squid:AvoidCycle,findbugs:NullReference"); + map.put("pageSize", "10"); + map.put("pageIndex", "50"); + map.put("sort", "CREATION_DATE"); + map.put("asc", "true"); + + WsTester.TestRequest request = tester.newGetRequest("api/issues", "search").setParams(map); + request.execute(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(IssueQuery.class); + verify(issueFinder).find(captor.capture()); + + IssueQuery query = captor.getValue(); + assertThat(query.requiredRole()).isEqualTo("user"); + assertThat(query.issueKeys()).containsOnly("ABCDE1234"); + assertThat(query.severities()).containsOnly("MAJOR", "MINOR"); + assertThat(query.statuses()).containsOnly("CLOSED"); + assertThat(query.resolutions()).containsOnly("FALSE-POSITIVE"); + assertThat(query.resolved()).isTrue(); + assertThat(query.components()).containsOnly("org.apache"); + assertThat(query.componentRoots()).containsOnly("org.sonar"); + assertThat(query.reporters()).containsOnly("marilyn"); + assertThat(query.assignees()).containsOnly("joanna"); + assertThat(query.languages()).containsOnly("xoo"); + assertThat(query.assigned()).isTrue(); + assertThat(query.planned()).isTrue(); + assertThat(query.hideRules()).isTrue(); + assertThat(query.createdAt()).isEqualTo(DateUtils.parseDateTime("2013-04-15T09:08:24+0200")); + assertThat(query.createdAfter()).isEqualTo(DateUtils.parseDateTime("2013-04-16T09:08:24+0200")); + assertThat(query.createdBefore()).isEqualTo(DateUtils.parseDateTime("2013-04-17T09:08:24+0200")); + assertThat(query.rules()).containsOnly(RuleKey.of("squid", "AvoidCycle"), RuleKey.of("findbugs", "NullReference")); + assertThat(query.pageSize()).isEqualTo(10); + assertThat(query.pageIndex()).isEqualTo(50); + assertThat(query.sort()).isEqualTo("CREATION_DATE"); + assertThat(query.asc()).isTrue(); + } + + private DefaultIssue createStandardIssue() { + return createIssue(); + } + + private DefaultIssue createIssue() { + return new DefaultIssue() + .setKey("ABCD") + .setComponentKey("sample:src/main/xoo/sample/Sample.xoo") + .setProjectKey("sample") + .setRuleKey(RuleKey.of("squid", "AvoidCycle")) + .setCreationDate(issueCreationDate); + } + +} diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java index fde5db793d7..35a83cb0ce7 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java @@ -17,6 +17,7 @@ * 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; @@ -31,7 +32,6 @@ import org.sonar.api.issue.ActionPlan; import org.sonar.api.issue.Issue; import org.sonar.api.issue.IssueFinder; import org.sonar.api.issue.IssueQuery; -import org.sonar.api.issue.action.Action; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.DefaultIssueComment; import org.sonar.api.issue.internal.FieldDiffs; @@ -42,7 +42,6 @@ import org.sonar.api.user.User; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; -import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; import org.sonar.core.issue.DefaultActionPlan; import org.sonar.core.issue.DefaultIssueQueryResult; @@ -95,7 +94,7 @@ public class IssueShowActionTest { List issues; DefaultIssueQueryResult result; - private Date issue_creation_date; + Date issueCreationDate; WsTester tester; @@ -108,12 +107,13 @@ public class IssueShowActionTest { when(issueChangelogService.changelog(any(Issue.class))).thenReturn(mock(IssueChangelog.class)); - issue_creation_date = DateUtils.parseDateTime("2014-01-22T19:10:03+0100"); - when(i18n.formatDateTime(any(Locale.class), eq(issue_creation_date))).thenReturn("Jan 22, 2014 10:03 AM"); + issueCreationDate = DateUtils.parseDateTime("2014-01-22T19:10:03+0100"); + when(i18n.formatDateTime(any(Locale.class), eq(issueCreationDate))).thenReturn("Jan 22, 2014 10:03 AM"); when(i18n.message(any(Locale.class), eq("created"), eq((String) null))).thenReturn("Created"); - tester = new WsTester(new IssuesWs(new IssueShowAction(issueFinder, issueService, issueChangelogService, actionService, debtModel, i18n, durations))); + tester = new WsTester(new IssuesWs(new IssueShowAction(issueFinder, issueChangelogService, new IssueActionsWriter(issueService, actionService), debtModel, i18n, durations), + mock(IssueSearchAction.class))); } @Test @@ -125,18 +125,18 @@ public class IssueShowActionTest { .setProjectKey("org.sonar.Sonar") .setRuleKey(RuleKey.of("squid", "AvoidCycle")) .setLine(12) - .setEffortToFix(2.0) .setMessage("Fix it") .setResolution("FIXED") .setStatus("CLOSED") .setSeverity("MAJOR") - .setCreationDate(issue_creation_date); + .setCreationDate(issueCreationDate); issues.add(issue); result.addComponents(Lists.newArrayList(new ComponentDto() .setId(10L) .setKey("org.sonar.server.issue.IssueClient") .setLongName("SonarQube :: Issue Client") + .setName("SonarQube :: Issue Client") .setQualifier("FIL") .setSubProjectId(1L) .setProjectId(1L) @@ -146,6 +146,7 @@ public class IssueShowActionTest { .setId(1L) .setKey("org.sonar.Sonar") .setLongName("SonarQube") + .setName("SonarQube") .setProjectId(1L) )); @@ -163,12 +164,11 @@ public class IssueShowActionTest { .setProjectKey("org.sonar.Sonar") .setRuleKey(RuleKey.of("squid", "AvoidCycle")) .setLine(12) - .setEffortToFix(2.0) .setMessage("Fix it") .setResolution("FIXED") .setStatus("CLOSED") .setSeverity("MAJOR") - .setCreationDate(issue_creation_date); + .setCreationDate(issueCreationDate); issues.add(issue); // File @@ -214,7 +214,7 @@ public class IssueShowActionTest { .setResolution("FIXED") .setStatus("CLOSED") .setSeverity("MAJOR") - .setCreationDate(issue_creation_date); + .setCreationDate(issueCreationDate); issues.add(issue); // File @@ -444,59 +444,6 @@ public class IssueShowActionTest { request.execute().assertJson(getClass(), "show_issue_with_actions.json"); } - @Test - public void show_issue_with_set_severity_action() throws Exception { - DefaultIssue issue = createStandardIssue() - .setStatus("OPEN"); - issues.add(issue); - - MockUserSession.set().setLogin("john").addProjectPermissions(UserRole.ISSUE_ADMIN, issue.projectKey()); - WsTester.TestRequest request = tester.newGetRequest("api/issues", "show").setParam("key", issue.key()); - request.execute().assertJson(getClass(), "show_issue_with_set_severity_action.json"); - } - - @Test - public void show_issue_with_assign_to_me_action() throws Exception { - DefaultIssue issue = createStandardIssue() - .setStatus("OPEN"); - issues.add(issue); - - MockUserSession.set().setLogin("john"); - WsTester.TestRequest request = tester.newGetRequest("api/issues", "show").setParam("key", issue.key()); - request.execute().assertJson(getClass(), "show_issue_with_assign_to_me_action.json"); - } - - @Test - public void show_issue_without_assign_to_me_action() throws Exception { - DefaultIssue issue = createStandardIssue() - .setStatus("OPEN") - .setAssignee("john"); - issues.add(issue); - - result.addUsers(Lists.newArrayList( - new DefaultUser().setLogin("john").setName("John") - )); - - MockUserSession.set().setLogin("john"); - WsTester.TestRequest request = tester.newGetRequest("api/issues", "show").setParam("key", issue.key()); - request.execute().assertJson(getClass(), "show_issue_without_assign_to_me_action.json"); - } - - @Test - public void show_issue_with_actions_defined_by_plugins() throws Exception { - Issue issue = createStandardIssue() - .setStatus("OPEN"); - issues.add(issue); - - Action action = mock(Action.class); - when(action.key()).thenReturn("link-to-jira"); - when(actionService.listAvailableActions(issue)).thenReturn(newArrayList(action)); - - MockUserSession.set().setLogin("john"); - WsTester.TestRequest request = tester.newGetRequest("api/issues", "show").setParam("key", issue.key()); - request.execute().assertJson(getClass(), "show_issue_with_actions_defined_by_plugins.json"); - } - @Test public void show_issue_with_changelog() throws Exception { Issue issue = createStandardIssue(); @@ -538,7 +485,7 @@ public class IssueShowActionTest { .setComponentKey("org.sonar.server.issue.IssueClient") .setProjectKey("org.sonar.Sonar") .setRuleKey(RuleKey.of("squid", "AvoidCycle")) - .setCreationDate(issue_creation_date); + .setCreationDate(issueCreationDate); } private void addComponentAndProject() { diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java index 30d6babddc3..c425e7328eb 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java @@ -27,9 +27,7 @@ import org.sonar.api.server.ws.RailsHandler; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.Durations; import org.sonar.server.debt.DebtModelService; -import org.sonar.server.issue.ActionService; import org.sonar.server.issue.IssueChangelogService; -import org.sonar.server.issue.IssueService; import org.sonar.server.ws.WsTester; import static org.fest.assertions.Assertions.assertThat; @@ -39,13 +37,22 @@ public class IssuesWsTest { IssueShowAction showAction; + IssueSearchAction searchAction; + WsTester tester; @Before public void setUp() throws Exception { - showAction = new IssueShowAction(mock(IssueFinder.class), mock(IssueService.class), mock(IssueChangelogService.class), mock(ActionService.class), - mock(DebtModelService.class), mock(I18n.class), mock(Durations.class)); - tester = new WsTester(new IssuesWs(showAction)); + IssueFinder issueFinder = mock(IssueFinder.class); + IssueChangelogService issueChangelogService = mock(IssueChangelogService.class); + IssueActionsWriter actionsWriter = mock(IssueActionsWriter.class); + DebtModelService debtModelService = mock(DebtModelService.class); + I18n i18n = mock(I18n.class); + Durations durations = mock(Durations.class); + + showAction = new IssueShowAction(issueFinder, issueChangelogService, actionsWriter, debtModelService, i18n, durations); + searchAction = new IssueSearchAction(issueFinder, actionsWriter, i18n, durations); + tester = new WsTester(new IssuesWs(showAction, searchAction)); } @Test @@ -87,9 +94,9 @@ public class IssuesWsTest { assertThat(show.since()).isEqualTo("3.6"); assertThat(show.isPost()).isFalse(); assertThat(show.isInternal()).isFalse(); - assertThat(show.handler()).isInstanceOf(RailsHandler.class); + assertThat(show.handler()).isSameAs(searchAction); assertThat(show.responseExampleAsString()).isNotEmpty(); - assertThat(show.params()).hasSize(19); + assertThat(show.params()).hasSize(23); } @Test diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues.json new file mode 100644 index 00000000000..8d14e1cd9b4 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues.json @@ -0,0 +1,32 @@ +{ + "maxResultsReached": true, + "paging": { + "pageIndex": 1, + "pageSize": 100, + "total": 2, + "fTotal": "2", + "pages": 1 + }, + "issues": [ + { + "key": "ABCD", + "component": "sample:src/main/xoo/sample/Sample.xoo", + "project": "sample", + "rule": "squid:AvoidCycle", + "resolution": "FIXED", + "status": "CLOSED", + "severity": "MAJOR", + "message": "Fix it", + "line": 12, + "reporter": "steven", + "assignee": "john", + "author": "Henry", + "actionPlan": "AP-ABCD", + "creationDate": "2014-01-22T19:10:03+0100" + } + ], + "components": [], + "projects": [], + "rules": [], + "users": [] +} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_action_plan.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_action_plan.json new file mode 100644 index 00000000000..6804fb0dcab --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_action_plan.json @@ -0,0 +1,24 @@ +{ + "maxResultsReached": true, + "paging": { + "pageIndex": 1, + "pageSize": 100, + "total": 2, + "fTotal": "2", + "pages": 1 + }, + "issues": [ + { + "key": "ABCD", + "component": "sample:src/main/xoo/sample/Sample.xoo", + "project": "sample", + "rule": "squid:AvoidCycle", + "actionPlan" : "AP-ABCD", + "creationDate": "2014-01-22T19:10:03+0100" + } + ], + "components": [], + "projects": [], + "rules": [], + "users": [] +} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_attributes.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_attributes.json new file mode 100644 index 00000000000..9d03dcd4389 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_attributes.json @@ -0,0 +1,26 @@ +{ + "maxResultsReached": true, + "paging": { + "pageIndex": 1, + "pageSize": 100, + "total": 2, + "fTotal": "2", + "pages": 1 + }, + "issues": [ + { + "key": "ABCD", + "component": "sample:src/main/xoo/sample/Sample.xoo", + "project": "sample", + "rule": "squid:AvoidCycle", + "creationDate": "2014-01-22T19:10:03+0100", + "attr": { + "jira-issue-key": "SONAR-1234" + } + } + ], + "components": [], + "projects": [], + "rules": [], + "users": [] +} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_components.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_components.json new file mode 100644 index 00000000000..77991667809 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_components.json @@ -0,0 +1,58 @@ +{ + "maxResultsReached": true, + "paging": { + "pageIndex": 1, + "pageSize": 100, + "total": 2, + "fTotal": "2", + "pages": 1 + }, + "issues": [ + { + "key": "ABCD", + "component": "sample:src/main/xoo/sample/Sample.xoo", + "project": "sample", + "rule": "squid:AvoidCycle", + "resolution": "FIXED", + "status": "CLOSED", + "severity": "MAJOR", + "message": "Fix it", + "line": 12, + "reporter": "steven", + "assignee": "john", + "author": "Henry", + "actionPlan": "AP-ABCD", + "creationDate": "2014-01-22T19:10:03+0100" + } + ], + "components": [ + { + "key": "sample:src/main/xoo/sample/Sample.xoo", + "id": 10, + "qualifier": "FIL", + "name": "Sample.xoo", + "longName": "src/main/xoo/sample/Sample.xoo", + "path": "src/main/xoo/sample/Sample.xoo", + "projectId": 7, + "subProjectId": 7 + }, + { + "key": "sample", + "id": 7, + "qualifier": "TRK", + "name": "Sample", + "longName": "Sample" + } + ], + "projects": [ + { + "key": "sample", + "id": 7, + "qualifier": "TRK", + "name": "Sample", + "longName": "Sample" + } + ], + "rules": [], + "users": [] +} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_dates.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_dates.json new file mode 100644 index 00000000000..814789bc44c --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_dates.json @@ -0,0 +1,26 @@ +{ + "maxResultsReached": true, + "paging": { + "pageIndex": 1, + "pageSize": 100, + "total": 2, + "fTotal": "2", + "pages": 1 + }, + "issues": [ + { + "key": "ABCD", + "component": "sample:src/main/xoo/sample/Sample.xoo", + "project": "sample", + "rule": "squid:AvoidCycle", + "creationDate": "2014-01-22T19:10:03+0100", + "updateDate": "2014-01-23T19:10:03+0100", + "fUpdateAge": "9 days", + "closeDate": "2014-01-24T19:10:03+0100" + } + ], + "components": [], + "projects": [], + "rules": [], + "users": [] +} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_debt.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_debt.json new file mode 100644 index 00000000000..e5054e61d64 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_debt.json @@ -0,0 +1,24 @@ +{ + "maxResultsReached": true, + "paging": { + "pageIndex": 1, + "pageSize": 100, + "total": 2, + "fTotal": "2", + "pages": 1 + }, + "issues": [ + { + "key": "ABCD", + "component": "sample:src/main/xoo/sample/Sample.xoo", + "project": "sample", + "rule": "squid:AvoidCycle", + "debt": "2 hours 1 minutes", + "creationDate": "2014-01-22T19:10:03+0100" + } + ], + "components": [], + "projects": [], + "rules": [], + "users": [] +} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_extra_fields.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_extra_fields.json new file mode 100644 index 00000000000..399a702bb2e --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_extra_fields.json @@ -0,0 +1,39 @@ +{ + "maxResultsReached": true, + "paging": { + "pageIndex": 1, + "pageSize": 100, + "total": 2, + "fTotal": "2", + "pages": 1 + }, + "issues": [ + { + "key": "ABCD", + "component": "sample:src/main/xoo/sample/Sample.xoo", + "project": "sample", + "rule": "squid:AvoidCycle", + "creationDate": "2014-01-22T19:10:03+0100", + "assignee": "john", + "assigneeName" : "John", + "actionPlan" : "AP-ABCD", + "actionPlanName" : "1.0", + "actions": [ + "comment", "assign", "plan" + ], + "transitions": [ + "reopen" + ] + } + ], + "components": [], + "projects": [], + "rules": [], + "users": [ + { + "login": "john", + "name": "John", + "active": false + } + ] +} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_rules.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_rules.json new file mode 100644 index 00000000000..58568a53b53 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_rules.json @@ -0,0 +1,39 @@ +{ + "maxResultsReached": true, + "paging": { + "pageIndex": 1, + "pageSize": 100, + "total": 2, + "fTotal": "2", + "pages": 1 + }, + "issues": [ + { + "key": "ABCD", + "component": "sample:src/main/xoo/sample/Sample.xoo", + "project": "sample", + "rule": "squid:AvoidCycle", + "resolution": "FIXED", + "status": "CLOSED", + "severity": "MAJOR", + "message": "Fix it", + "line": 12, + "reporter": "steven", + "assignee": "john", + "author": "Henry", + "actionPlan": "AP-ABCD", + "creationDate": "2014-01-22T19:10:03+0100" + } + ], + "components": [], + "projects": [], + "rules": [ + { + "key": "squid:AvoidCycle", + "name": "Avoid cycle", + "desc": "Avoid cycle description", + "status": "READY" + } + ], + "users": [] +} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_users.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_users.json new file mode 100644 index 00000000000..2d8c3ac6260 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueSearchActionTest/issues_with_users.json @@ -0,0 +1,39 @@ +{ + "maxResultsReached": true, + "paging": { + "pageIndex": 1, + "pageSize": 100, + "total": 2, + "fTotal": "2", + "pages": 1 + }, + "issues": [ + { + "key": "ABCD", + "component": "sample:src/main/xoo/sample/Sample.xoo", + "project": "sample", + "rule": "squid:AvoidCycle", + "resolution": "FIXED", + "status": "CLOSED", + "severity": "MAJOR", + "message": "Fix it", + "line": 12, + "reporter": "steven", + "assignee": "john", + "author": "Henry", + "actionPlan": "AP-ABCD", + "creationDate": "2014-01-22T19:10:03+0100" + } + ], + "components": [], + "projects": [], + "rules": [], + "users": [ + { + "login": "john", + "name": "John", + "active": true, + "email": "john@email.com" + } + ] +} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions_defined_by_plugins.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions_defined_by_plugins.json deleted file mode 100644 index 9f83417b32a..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_actions_defined_by_plugins.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "issue": { - "key": "ABCD", - "component": "org.sonar.server.issue.IssueClient", - "componentLongName": "SonarQube :: Issue Client", - "componentQualifier": "FIL", - "project": "org.sonar.Sonar", - "projectName": "SonarQube", - "rule": "squid:AvoidCycle", - "ruleName": "Avoid cycle", - "status": "OPEN", - "creationDate": "2014-01-22T19:10:03+0100", - "fCreationDate": "Jan 22, 2014 10:03 AM", - "transitions": [], - "actions": [ - "comment", "assign", "assign_to_me", "plan", "link-to-jira" - ], - "comments": [], - "changelog": [ - { - "creationDate": "2014-01-22T19:10:03+0100", - "fCreationDate": "Jan 22, 2014 10:03 AM", - "diffs": ["Created"] - } - ] - } -} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_assign_to_me_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_assign_to_me_action.json deleted file mode 100644 index eb7f97febca..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_assign_to_me_action.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "issue": { - "key": "ABCD", - "component": "org.sonar.server.issue.IssueClient", - "componentLongName": "SonarQube :: Issue Client", - "componentQualifier": "FIL", - "project": "org.sonar.Sonar", - "projectName": "SonarQube", - "rule": "squid:AvoidCycle", - "ruleName": "Avoid cycle", - "status": "OPEN", - "creationDate": "2014-01-22T19:10:03+0100", - "fCreationDate": "Jan 22, 2014 10:03 AM", - "transitions": [], - "actions": [ - "comment", "assign", "assign_to_me", "plan" - ], - "comments": [], - "changelog": [ - { - "creationDate": "2014-01-22T19:10:03+0100", - "fCreationDate": "Jan 22, 2014 10:03 AM", - "diffs": ["Created"] - } - ] - } -} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_set_severity_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_set_severity_action.json deleted file mode 100644 index 242d99c6a3e..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_with_set_severity_action.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "issue": { - "key": "ABCD", - "component": "org.sonar.server.issue.IssueClient", - "componentLongName": "SonarQube :: Issue Client", - "componentQualifier": "FIL", - "project": "org.sonar.Sonar", - "projectName": "SonarQube", - "rule": "squid:AvoidCycle", - "ruleName": "Avoid cycle", - "status": "OPEN", - "creationDate": "2014-01-22T19:10:03+0100", - "fCreationDate": "Jan 22, 2014 10:03 AM", - "transitions": [], - "actions": [ - "comment", "assign", "assign_to_me", "plan", "set_severity" - ], - "comments": [], - "changelog": [ - { - "creationDate": "2014-01-22T19:10:03+0100", - "fCreationDate": "Jan 22, 2014 10:03 AM", - "diffs": ["Created"] - } - ] - } -} diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_without_assign_to_me_action.json b/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_without_assign_to_me_action.json deleted file mode 100644 index ffde3996c13..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/issue/ws/IssueShowActionTest/show_issue_without_assign_to_me_action.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "issue": { - "key": "ABCD", - "component": "org.sonar.server.issue.IssueClient", - "componentLongName": "SonarQube :: Issue Client", - "componentQualifier": "FIL", - "project": "org.sonar.Sonar", - "projectName": "SonarQube", - "rule": "squid:AvoidCycle", - "ruleName": "Avoid cycle", - "assignee": "john", - "assigneeName": "John", - "status": "OPEN", - "creationDate": "2014-01-22T19:10:03+0100", - "fCreationDate": "Jan 22, 2014 10:03 AM", - "transitions": [], - "actions": [ - "comment", "assign", "plan" - ], - "comments": [], - "changelog": [ - { - "creationDate": "2014-01-22T19:10:03+0100", - "fCreationDate": "Jan 22, 2014 10:03 AM", - "diffs": ["Created"] - } - ] - } -} -- cgit v1.2.3