From b1704b601402b67294c19e115ab15335b5c35802 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 10 Apr 2013 16:16:02 +0200 Subject: [PATCH] SONAR-3755 Add Issue Web Service --- .../sonar/core/issue/DefaultIssueFinder.java | 27 +++- .../org/sonar/core/issue/IssueFilter.java | 101 +++++++++++++++ .../org/sonar/core/persistence/DaoUtils.java | 7 +- .../org/sonar/core/issue/IssueMapper.xml | 34 +++-- .../core/issue/DefaultIssueFinderTest.java | 20 ++- .../org/sonar/core/issue/IssueDaoTest.java | 35 ++++- .../org/sonar/core/issue/IssueFilterTest.java | 120 ++++++++++++++++++ .../sonar/core/issue/IssueDaoTest/select.xml | 14 ++ .../java/org/sonar/api/issue/IssueFinder.java | 2 + .../java/org/sonar/api/issue/IssueQuery.java | 116 ++++++++++++----- .../org/sonar/server/platform/Platform.java | 2 + .../java/org/sonar/server/ui/JRubyFacade.java | 29 ++--- .../app/controllers/api/issues_controller.rb | 108 ++++++++++++++++ .../src/main/webapp/WEB-INF/config/routes.rb | 3 + 14 files changed, 538 insertions(+), 80 deletions(-) create mode 100644 sonar-core/src/main/java/org/sonar/core/issue/IssueFilter.java create mode 100644 sonar-core/src/test/java/org/sonar/core/issue/IssueFilterTest.java create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueFinder.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueFinder.java index 3ed1f00c4b7..03874ba3cb9 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueFinder.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueFinder.java @@ -22,12 +22,15 @@ package org.sonar.core.issue; import com.google.common.base.Function; import com.google.common.collect.Iterables; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.sonar.api.issue.Issue; import org.sonar.api.issue.IssueFinder; import org.sonar.api.issue.IssueQuery; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.core.resource.ResourceDao; +import org.sonar.core.resource.ResourceDto; import java.util.Collection; import java.util.List; @@ -39,6 +42,8 @@ import static com.google.common.collect.Lists.newArrayList; */ public class DefaultIssueFinder implements IssueFinder { + private static final Logger LOG = LoggerFactory.getLogger(DefaultIssueFinder.class); + private final IssueDao issueDao; private final ResourceDao resourceDao; private final RuleFinder ruleFinder; @@ -50,6 +55,7 @@ public class DefaultIssueFinder implements IssueFinder { } public List find(IssueQuery issueQuery) { + LOG.debug("IssueQuery : {}", issueQuery); Collection dtoList = issueDao.select(issueQuery); return newArrayList(Iterables.transform(dtoList, new Function() { @Override @@ -59,21 +65,32 @@ public class DefaultIssueFinder implements IssueFinder { })); } - private Issue toIssue(IssueDto issueDto){ - Rule rule = ruleFinder.findById(issueDto.getRuleId()); + public Issue findByKey(String key){ + IssueDto issueDto = issueDao.findByUuid(key); + return issueDto != null ? toIssue(issueDto) : null; + } - Issue.Builder issueBuilder = new Issue.Builder(); + private Issue toIssue(IssueDto issueDto){ + Issue.Builder issueBuilder = new Issue.Builder(); issueBuilder.status(issueDto.getStatus()); issueBuilder.resolution(issueDto.getResolution()); issueBuilder.message(issueDto.getMessage()); + issueBuilder.title(issueDto.getTitle()); issueBuilder.cost(issueDto.getCost()); issueBuilder.line(issueDto.getLine()); - issueBuilder.line(issueDto.getLine()); + issueBuilder.severity(issueDto.getSeverity()); issueBuilder.userLogin(issueDto.getUserLogin()); issueBuilder.assigneeLogin(issueDto.getAssigneeLogin()); - issueBuilder.componentKey(resourceDao.getResource(issueDto.getResourceId()).getKey()); + + ResourceDto resource = resourceDao.getResource(issueDto.getResourceId()); + issueBuilder.componentKey(resource.getKey()); + + Rule rule = ruleFinder.findById(issueDto.getRuleId()); issueBuilder.ruleKey(rule.getKey()); issueBuilder.ruleRepositoryKey(rule.getRepositoryKey()); + + // TODO add key and dates + return issueBuilder.build(); } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueFilter.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueFilter.java new file mode 100644 index 00000000000..beb41817836 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/issue/IssueFilter.java @@ -0,0 +1,101 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ + +package org.sonar.core.issue; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import org.sonar.api.ServerComponent; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.IssueFinder; +import org.sonar.api.issue.IssueQuery; + +import java.util.List; +import java.util.Map; + +import static com.google.common.collect.Lists.newArrayList; + +/** + * @since 3.6 + */ +public class IssueFilter implements ServerComponent { + + private final IssueFinder issueFinder; + + public IssueFilter(IssueFinder issueFinder) { + this.issueFinder = issueFinder; + } + + public List execute(Map map) { + IssueQuery issueQueryBuilder = createIssueQuery(map); + return issueFinder.find(issueQueryBuilder); + } + + public Issue execute(String key) { + return !Strings.isNullOrEmpty(key) ? issueFinder.findByKey(key) : null; + } + + @VisibleForTesting + IssueQuery createIssueQuery(Map map) { + IssueQuery.Builder issueQueryBuilder = new IssueQuery.Builder(); + if (map != null && !map.isEmpty()) { + if (isPropertyNotEmpty("keys", map)) { + issueQueryBuilder.keys(getListProperties("keys", map)); + } + if (isPropertyNotEmpty("severities", map)) { + issueQueryBuilder.severities(getListProperties("severities", map)); + } + if (isPropertyNotEmpty("minSeverity", map)) { + issueQueryBuilder.minSeverity(map.get("minSeverity")); + } + if (isPropertyNotEmpty("status", map)) { + issueQueryBuilder.status(getListProperties("status", map)); + } + if (isPropertyNotEmpty("resolutions", map)) { + issueQueryBuilder.resolutions(getListProperties("resolutions", map)); + } + if (isPropertyNotEmpty("components", map)) { + issueQueryBuilder.componentKeys(getListProperties("components", map)); + } + if (isPropertyNotEmpty("rules", map)) { + issueQueryBuilder.rules(getListProperties("rules", map)); + } + if (isPropertyNotEmpty("userLogins", map)) { + issueQueryBuilder.userLogins(getListProperties("userLogins", map)); + } + if (isPropertyNotEmpty("assigneeLogins", map)) { + issueQueryBuilder.assigneeLogins(getListProperties("assigneeLogins", map)); + } + if (isPropertyNotEmpty("limit", map)) { + issueQueryBuilder.limit(Integer.parseInt(map.get("limit"))); + } + } + return issueQueryBuilder.build(); + } + + private boolean isPropertyNotEmpty(String property, Map map) { + return map.containsKey(property) && !Strings.isNullOrEmpty(map.get(property)); + } + + private List getListProperties(String property, Map map) { + return newArrayList(Splitter.on(',').split(map.get(property))); + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java index 0c875048316..0952787013d 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java @@ -20,10 +20,11 @@ package org.sonar.core.persistence; import com.google.common.collect.ImmutableList; -import org.sonar.core.graph.jdbc.GraphDao; import org.sonar.core.dashboard.ActiveDashboardDao; import org.sonar.core.dashboard.DashboardDao; import org.sonar.core.duplication.DuplicationDao; +import org.sonar.core.graph.jdbc.GraphDao; +import org.sonar.core.issue.IssueDao; import org.sonar.core.measure.MeasureFilterDao; import org.sonar.core.properties.PropertiesDao; import org.sonar.core.purge.PurgeDao; @@ -63,6 +64,8 @@ public final class DaoUtils { ReviewDao.class, RuleDao.class, SemaphoreDao.class, - UserDao.class); + UserDao.class, + IssueDao.class + ); } } diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/IssueMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/IssueMapper.xml index 4473441af88..dde4f0c2cac 100644 --- a/sonar-core/src/main/resources/org/sonar/core/issue/IssueMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/issue/IssueMapper.xml @@ -109,21 +109,35 @@ and s.islast=${_true} and i.resource_id=s.project_id - - - and i.severity=#{severity} + + and i.uuid in + #{key} + + + + and i.severity in + #{severity} + - and i.status=#{status} + and i.status in + #{statu} + - - and i.resolution=#{resolution} + + and i.resolution in + #{resolution} + - - and i.user_login=#{userLogin} + + and i.user_login in + #{userLogin} + - - and i.assignee_login=#{assigneeLogin} + + and i.assignee_login in + #{assigneeLogin} + diff --git a/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueFinderTest.java b/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueFinderTest.java index 2cd260df5e0..0f35300aaa0 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueFinderTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueFinderTest.java @@ -56,18 +56,32 @@ public class DefaultIssueFinderTest { public void should_find_issues() { IssueQuery issueQuery = mock(IssueQuery.class); - IssueDto issueDto = new IssueDto().setId(1L).setRuleId(1).setResourceId(1); - Collection dtoList = newArrayList(issueDto); + IssueDto issue1 = new IssueDto().setId(1L).setRuleId(1).setResourceId(1); + IssueDto issue2 = new IssueDto().setId(2L).setRuleId(1).setResourceId(1); + Collection dtoList = newArrayList(issue1, issue2); when(issueDao.select(issueQuery)).thenReturn(dtoList); when(ruleFinder.findById(anyInt())).thenReturn(Rule.create("repo", "key")); when(resourceDao.getResource(anyInt())).thenReturn(new ResourceDto().setKey("componentKey")); Collection issues = finder.find(issueQuery); - assertThat(issues).hasSize(1); + assertThat(issues).hasSize(2); Issue issue = issues.iterator().next(); assertThat(issue.componentKey()).isEqualTo("componentKey"); assertThat(issue.ruleKey()).isEqualTo("key"); assertThat(issue.ruleRepositoryKey()).isEqualTo("repo"); + } + + @Test + public void should_find_by_key() { + IssueDto issueDto = new IssueDto().setId(1L).setRuleId(1).setResourceId(1); + when(issueDao.findByUuid("key")).thenReturn(issueDto); + when(ruleFinder.findById(anyInt())).thenReturn(Rule.create("repo", "key")); + when(resourceDao.getResource(anyInt())).thenReturn(new ResourceDto().setKey("componentKey")); + Issue issue = finder.findByKey("key"); + assertThat(issue).isNotNull(); + assertThat(issue.componentKey()).isEqualTo("componentKey"); + assertThat(issue.ruleKey()).isEqualTo("key"); + assertThat(issue.ruleRepositoryKey()).isEqualTo("repo"); } } diff --git a/sonar-core/src/test/java/org/sonar/core/issue/IssueDaoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/IssueDaoTest.java index 1f82e281262..3cf6e80e3d8 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/IssueDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/IssueDaoTest.java @@ -129,28 +129,51 @@ public class IssueDaoTest extends AbstractDaoTestCase { } @Test - public void should_select() { + public void should_select_by_parameter() { setupData("select"); - IssueQuery issueQuery = new IssueQuery.Builder().resolution("FALSE-POSITIVE").build(); + IssueQuery issueQuery = new IssueQuery.Builder().keys(newArrayList("100")).build(); assertThat(dao.select(issueQuery)).hasSize(1); - issueQuery = new IssueQuery.Builder().userLogin("user").build(); + issueQuery = new IssueQuery.Builder().componentKeys(newArrayList("key")).build(); + assertThat(dao.select(issueQuery)).hasSize(2); + + issueQuery = new IssueQuery.Builder().resolutions(newArrayList("FALSE-POSITIVE")).build(); assertThat(dao.select(issueQuery)).hasSize(1); - issueQuery = new IssueQuery.Builder().assigneeLogin("user").build(); + issueQuery = new IssueQuery.Builder().status(newArrayList("OPEN")).build(); + assertThat(dao.select(issueQuery)).hasSize(2); + + issueQuery = new IssueQuery.Builder().severities(newArrayList("BLOCKER")).build(); + assertThat(dao.select(issueQuery)).hasSize(4); + + issueQuery = new IssueQuery.Builder().userLogins(newArrayList("user")).build(); + assertThat(dao.select(issueQuery)).hasSize(1); + + issueQuery = new IssueQuery.Builder().assigneeLogins(newArrayList("user")).build(); assertThat(dao.select(issueQuery)).hasSize(5); + + issueQuery = new IssueQuery.Builder().userLogins(newArrayList("user")).status(newArrayList("OPEN")).build(); + assertThat(dao.select(issueQuery)).hasSize(1); } @Test - public void should_select_by_components() { + public void should_return_issues_from_resource_tree() { setupData("select-with-component-children"); - IssueQuery issueQuery = new IssueQuery.Builder().componentKeys("key").build(); + IssueQuery issueQuery = new IssueQuery.Builder().componentKeys(newArrayList("key")).build(); List issues = newArrayList(dao.select(issueQuery)); assertThat(issues).hasSize(2); assertThat(issues.get(0).getId()).isEqualTo(100); assertThat(issues.get(1).getId()).isEqualTo(101); } + @Test + public void should_select_without_parameter_return_all_issues() { + setupData("select"); + + IssueQuery issueQuery = new IssueQuery.Builder().build(); + assertThat(dao.select(issueQuery)).hasSize(5); + } + } diff --git a/sonar-core/src/test/java/org/sonar/core/issue/IssueFilterTest.java b/sonar-core/src/test/java/org/sonar/core/issue/IssueFilterTest.java new file mode 100644 index 00000000000..814938fbe0c --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/issue/IssueFilterTest.java @@ -0,0 +1,120 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ + +package org.sonar.core.issue; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.IssueFinder; +import org.sonar.api.issue.IssueQuery; + +import java.util.List; +import java.util.Map; + +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.anyString; +import static org.mockito.Mockito.*; + +public class IssueFilterTest { + + private IssueFilter issueFilter; + private IssueFinder issueFinder; + + @Before + public void before() { + issueFinder = mock(IssueFinder.class); + issueFilter = new IssueFilter(issueFinder); + } + + @Test + public void should_call_find() { + Map map = newHashMap(); + issueFilter.execute(map); + verify(issueFinder).find(any(IssueQuery.class)); + } + + @Test + public void should_call_find_by_key() { + issueFilter.execute("key"); + verify(issueFinder).findByKey("key"); + } + + @Test + public void should_not_call_find_by_key_with_empty_key() { + Issue issue = issueFilter.execute(""); + assertThat(issue).isNull(); + verify(issueFinder, never()).findByKey(anyString()); + } + + @Test + public void should_create_empty_issue_query() { + Map map = newHashMap(); + IssueQuery issueQuery = issueFilter.createIssueQuery(map); + assertThat(issueQuery.componentKeys()).isEmpty(); + } + + @Test + public void should_create_empty_issue_query_if_value_is_null() { + Map map = newHashMap(); + map.put("components", null); + IssueQuery issueQuery = issueFilter.createIssueQuery(map); + assertThat(issueQuery.componentKeys()).isEmpty(); + } + + @Test + public void should_create_issue_query() { + Map map = newHashMap(); + map.put("keys", "keys"); + map.put("severities", "severities"); + map.put("minSeverity", "MINOR"); + map.put("status", "status"); + map.put("resolutions", "resolutions"); + map.put("components", "key"); + map.put("rules", "rules"); + map.put("userLogins", "userLogins"); + map.put("assigneeLogins", "assigneeLogins"); + map.put("limit", "1"); + + IssueQuery issueQuery = issueFilter.createIssueQuery(map); + assertThat(issueQuery.keys()).isNotEmpty(); + assertThat(issueQuery.severities()).isNotEmpty(); + assertThat(issueQuery.minSeverity()).isEqualTo("MINOR"); + assertThat(issueQuery.status()).isNotEmpty(); + assertThat(issueQuery.resolutions()).isNotEmpty(); + assertThat(issueQuery.rules()).isNotEmpty(); + assertThat(issueQuery.userLogins()).isNotEmpty(); + assertThat(issueQuery.assigneeLogins()).isNotEmpty(); + assertThat(issueQuery.limit()).isEqualTo(1); + } + + @Test + public void should_split_property_list() { + Map map = newHashMap(); + map.put("components", "key1,key2"); + IssueQuery issueQuery = issueFilter.createIssueQuery(map); + List components = issueQuery.componentKeys(); + assertThat(components).hasSize(2); + assertThat(components).containsOnly("key1", "key2"); + } + +} diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/select.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/select.xml index df0ebd1473d..1bec8e838e8 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/select.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/select.xml @@ -143,4 +143,18 @@ closed_at="[null]" /> + + + + diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java index c944d8813b9..525973fa11b 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java @@ -32,4 +32,6 @@ public interface IssueFinder extends TaskComponent, ServerComponent { List find(IssueQuery issueQuery); + Issue findByKey(String key); + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java index 887dd022ad6..1cf127d28d1 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java @@ -31,44 +31,68 @@ import static com.google.common.collect.Lists.newArrayList; */ public class IssueQuery { - private String severity; - private String status; - private String resolution; + private List keys; + private List severities; + private String minSeverity; + private List status; + private List resolutions; private List componentKeys; - private String userLogin; - private String assigneeLogin; + private List rules; + private List userLogins; + private List assigneeLogins; + private Integer limit; private IssueQuery(Builder builder) { - this.severity = builder.severity; + this.keys = builder.keys; + this.severities = builder.severities; + this.minSeverity = builder.minSeverity; this.status = builder.status; - this.resolution = builder.resolution; + this.resolutions = builder.resolutions; this.componentKeys = builder.componentKeys; - this.userLogin = builder.userLogin; - this.assigneeLogin = builder.assigneeLogin; + this.rules = builder.rules; + this.userLogins = builder.userLogins; + this.assigneeLogins = builder.assigneeLogins; + this.limit = builder.limit; } - public String severity() { - return severity; + public List keys() { + return keys; } - public String status() { + public List severities() { + return severities; + } + + public String minSeverity() { + return minSeverity; + } + + public List status() { return status; } - public String resolution() { - return resolution; + public List resolutions() { + return resolutions; } public List componentKeys() { return componentKeys; } - public String userLogin() { - return userLogin; + public List rules() { + return rules; + } + + public List userLogins() { + return userLogins; + } + + public List assigneeLogins() { + return assigneeLogins; } - public String assigneeLogin() { - return assigneeLogin; + public Integer limit() { + return limit; } @Override @@ -80,29 +104,43 @@ public class IssueQuery { * @since 3.6 */ public static class Builder { - private String severity; - private String status; - private String resolution; + private List keys; + private List severities; + private String minSeverity; + private List status; + private List resolutions; private List componentKeys; - private String userLogin; - private String assigneeLogin; + private List rules; + private List userLogins; + private List assigneeLogins; + private Integer limit; public Builder() { componentKeys = newArrayList(); } - public Builder severity(String severity) { - this.severity = severity; + public Builder keys(List keys) { + this.keys = keys; return this; } - public Builder status(String status) { + public Builder severities(List severities) { + this.severities = severities; + return this; + } + + public Builder minSeverity(String minSeverity) { + this.minSeverity = minSeverity; + return this; + } + + public Builder status(List status) { this.status = status; return this; } - public Builder resolution(String resolution) { - this.resolution = resolution; + public Builder resolutions(List resolutions) { + this.resolutions = resolutions; return this; } @@ -111,23 +149,33 @@ public class IssueQuery { return this; } - public Builder componentKeys(String... componentKeys) { - this.componentKeys.addAll(newArrayList(componentKeys)); + public Builder rules(List rules) { + this.rules = rules; return this; } - public Builder userLogin(String userLogin) { - this.userLogin = userLogin; + public Builder userLogins(List userLogins) { + this.userLogins = userLogins; return this; } - public Builder assigneeLogin(String assigneeLogin) { - this.assigneeLogin = assigneeLogin; + public Builder assigneeLogins(List assigneeLogins) { + this.assigneeLogins = assigneeLogins; + return this; + } + + public Builder limit(Integer limit) { + this.limit = limit; return this; } public IssueQuery build() { return new IssueQuery(this); } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this); + } } } diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index d47ffc3c948..816523db20f 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -41,6 +41,7 @@ import org.sonar.core.i18n.GwtI18n; import org.sonar.core.i18n.I18nManager; import org.sonar.core.i18n.RuleI18nManager; import org.sonar.core.issue.DefaultIssueFinder; +import org.sonar.core.issue.IssueFilter; import org.sonar.core.measure.MeasureFilterEngine; import org.sonar.core.measure.MeasureFilterExecutor; import org.sonar.core.measure.MeasureFilterFactory; @@ -232,6 +233,7 @@ public final class Platform { servicesContainer.addSingleton(DefaultResourcePermissions.class); servicesContainer.addSingleton(Periods.class); servicesContainer.addSingleton(DefaultIssueFinder.class); + servicesContainer.addSingleton(IssueFilter.class); // Notifications servicesContainer.addSingleton(EmailSettings.class); diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index 5ba1a4c81f7..e7929b33dfa 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -41,17 +41,14 @@ import org.sonar.api.test.MutableTestable; import org.sonar.api.test.TestPlan; import org.sonar.api.test.Testable; import org.sonar.api.utils.ValidationMessages; -import org.sonar.api.web.Footer; -import org.sonar.api.web.NavigationSection; -import org.sonar.api.web.Page; -import org.sonar.api.web.RubyRailsWebservice; -import org.sonar.api.web.Widget; +import org.sonar.api.web.*; import org.sonar.api.workflow.Review; import org.sonar.api.workflow.internal.DefaultReview; import org.sonar.api.workflow.internal.DefaultWorkflowContext; import org.sonar.api.workflow.screen.Screen; import org.sonar.core.component.SnapshotPerspectives; import org.sonar.core.i18n.RuleI18nManager; +import org.sonar.core.issue.IssueFilter; import org.sonar.core.measure.MeasureFilterEngine; import org.sonar.core.measure.MeasureFilterResult; import org.sonar.core.persistence.Database; @@ -68,16 +65,8 @@ import org.sonar.server.configuration.Backup; import org.sonar.server.configuration.ProfilesManager; import org.sonar.server.macro.MacroInterpreter; import org.sonar.server.notifications.reviews.ReviewsNotificationManager; -import org.sonar.server.platform.NewUserNotifier; -import org.sonar.server.platform.Platform; -import org.sonar.server.platform.ServerIdGenerator; -import org.sonar.server.platform.ServerSettings; -import org.sonar.server.platform.SettingsChangeNotifier; -import org.sonar.server.plugins.DefaultServerPluginRepository; -import org.sonar.server.plugins.InstalledPluginReferentialFactory; -import org.sonar.server.plugins.PluginDeployer; -import org.sonar.server.plugins.PluginDownloader; -import org.sonar.server.plugins.UpdateCenterMatrixFactory; +import org.sonar.server.platform.*; +import org.sonar.server.plugins.*; import org.sonar.server.rules.ProfilesConsole; import org.sonar.server.rules.RulesConsole; import org.sonar.updatecenter.common.PluginReferential; @@ -88,11 +77,7 @@ import javax.annotation.Nullable; import java.net.InetAddress; import java.sql.Connection; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import static com.google.common.collect.Lists.newArrayList; @@ -579,4 +564,8 @@ public final class JRubyFacade { public Collection getHighlightedSourceLines(long snapshotId) { return get(SyntaxHighlighter.class).getHighlightedSourceAsHtml(snapshotId); } + + public IssueFilter getIssueFilter() { + return get(IssueFilter.class); + } } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb new file mode 100644 index 00000000000..a83fc4e1800 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb @@ -0,0 +1,108 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2008-2012 SonarSource +# mailto:contact AT sonarsource DOT com +# +# Sonar 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. +# +# Sonar 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 Sonar; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 +# + +require "json" + +class Api::IssuesController < Api::ApiController + + # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) + verify :method => :put, :only => [ :update ] + verify :method => :post, :only => [ :create ] + verify :method => :delete, :only => [ :destroy ] + + before_filter :admin_required, :only => [ :create, :update, :destroy ] + + # GET /api/issues + def index + map={} + map['keys'] = params[:keys] + map['severities'] = params[:severities] + map['severityMin'] = params[:severityMin] + map['status'] = params[:status] + map['resolutions'] = params[:resolutions] + map['components'] = params[:components] + map['authors'] = params[:authors] + map['assignees'] = params[:assignees] + map['rules'] = params[:rules] + map['limit'] = params[:limit] + + respond_to do |format| + format.json { render :json => jsonp(issues_to_json(find_issues(map))) } + format.xml { render :xml => xml_not_supported } + end + end + + #GET /api/issues/foo + def show + respond_to do |format| + format.json { render :json => jsonp(issues_to_json([find_issue(params[:key])])) } + format.xml { render :xml => xml_not_supported } + end + end + + + protected + + def find_issues(map) + issues_query.execute(map) + end + + def find_issue(key) + issues_query.execute(key) + end + + def issues_query + java_facade.getIssueFilter() + end + + def issues_to_json(issues) + json = [] + issues.each do |issue| + json << issue_to_json(issue) if issue + end + json + end + + def issue_to_json(issue) + { + :key => issue.key, + :component => issue.componentKey, + :ruleKey => issue.ruleKey, + :ruleRepositoryKey => issue.ruleRepositoryKey, + :severity => issue.severity, + :title => issue.title, + :message => issue.message, + :line => issue.line, + :cost => issue.cost, + :status => issue.status, + :resolution => issue.resolution, + :userLogin => issue.userLogin, + :assigneeLogin => issue.assigneeLogin, + :createdAt => to_date(issue.createdAt), + :updatedAt => to_date(issue.updatedAt), + :closedAt => to_date(issue.closedAt), + } + end + + def to_date(java_date) + java_date ? Api::Utils.format_datetime(Time.at(java_date.time/1000)) : nil + end + +end diff --git a/sonar-server/src/main/webapp/WEB-INF/config/routes.rb b/sonar-server/src/main/webapp/WEB-INF/config/routes.rb index 6cfed6caea7..70d2d26d103 100644 --- a/sonar-server/src/main/webapp/WEB-INF/config/routes.rb +++ b/sonar-server/src/main/webapp/WEB-INF/config/routes.rb @@ -26,6 +26,9 @@ ActionController::Routing::Routes.draw do |map| map.connect 'api/resoures', :controller => 'api/resources', :action => 'index' map.connect 'api/sources', :controller => 'api/sources', :action => 'index' map.connect 'api/violations', :controller => 'api/violations', :action => 'index' + map.connect 'api/issues', :controller => 'api/issues', :action => 'index', :conditions => { :method => :get } + map.connect 'api/issues/:key', :controller => 'api/issues', :action => 'show', :conditions => { :method => :get } + map.resources 'rules', :path_prefix => 'api', :controller => 'api/rules' map.resources 'properties', :path_prefix => 'api', :controller => 'api/properties', :requirements => { :id => /.*/ } -- 2.39.5