@@ -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<String> 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; | |||
} | |||
} |
@@ -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(); |
@@ -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<Transition> 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<String> actions(DefaultIssue issue) { | |||
List<String> 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; | |||
} | |||
} |
@@ -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 <repository>:<rule>") | |||
.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<String> 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<String, String> entry : issue.attributes().entrySet()) { | |||
json.prop(entry.getKey(), entry.getValue()); | |||
} | |||
json.endObject(); | |||
} | |||
} | |||
private void writeIssueExtraFields(IssueQueryResult result, Issue issue, @Nullable List<String> 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<Transition> 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<String> actions(DefaultIssue issue) { | |||
// List<String> 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<RuleKey> stringsToRules(@Nullable Collection<String> rules) { | |||
if (rules != null) { | |||
return newArrayList(Iterables.transform(rules, new Function<String, RuleKey>() { | |||
@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(); | |||
} | |||
} |
@@ -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<Transition> 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<String> actions(DefaultIssue issue) { | |||
List<String> 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 |
@@ -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 <repository>:<rule>") | |||
.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") |
@@ -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); |
@@ -29,6 +29,9 @@ | |||
"createdAt": "2013-05-13T18:08:34+0200" | |||
} | |||
], | |||
"attr": { | |||
"jira-issue-key": "SONAR-1234" | |||
}, | |||
"actions": ["link-to-jira"], | |||
"transitions": [ | |||
"unconfirm", |
@@ -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); | |||
} | |||
} |
@@ -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<Issue> issues; | |||
DefaultIssueQueryResult result; | |||
Date issueCreationDate; | |||
WsTester tester; | |||
@Before | |||
public void setUp() throws Exception { | |||
issues = new ArrayList<Issue>(); | |||
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.<Component>newArrayList(component, project)); | |||
result.addProjects(Lists.<Component>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.<User>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.<User>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<String, String> 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<IssueQuery> 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); | |||
} | |||
} |
@@ -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<Issue> 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.<Component>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.<User>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() { |
@@ -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 |
@@ -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": [] | |||
} |
@@ -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": [] | |||
} |
@@ -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": [] | |||
} |
@@ -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": [] | |||
} |
@@ -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": [] | |||
} |
@@ -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": [] | |||
} |
@@ -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 | |||
} | |||
] | |||
} |
@@ -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": [] | |||
} |
@@ -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" | |||
} | |||
] | |||
} |
@@ -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"] | |||
} | |||
] | |||
} | |||
} |
@@ -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"] | |||
} | |||
] | |||
} | |||
} |
@@ -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"] | |||
} | |||
] | |||
} | |||
} |
@@ -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"] | |||
} | |||
] | |||
} | |||
} |