From 5846e6c2a88a0fa46d999689d9eb51ef0921ab94 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Tue, 23 Apr 2013 13:58:37 +0200 Subject: [PATCH] SONAR-3755 Add methods in IssueFinder Results to get rule by issue and component by issue and remove title from Issue --- .../should_insert_new_issue-result.xml | 2 +- .../should_update_existing_issue-result.xml | 2 +- .../should_update_existing_issue.xml | 2 +- .../sonar/core/component/ComponentDto.java | 71 ++++++++++++++++ .../org/sonar/core/issue/DefaultIssue.java | 11 --- .../sonar/core/issue/DefaultIssueBuilder.java | 1 - .../java/org/sonar/core/issue/IssueDto.java | 12 --- .../sonar/core/issue/UpdateIssueFields.java | 3 - .../core/issue/workflow/IssueWorkflow.java | 3 - .../org/sonar/core/resource/ResourceDao.java | 30 ++++++- .../sonar/core/resource/ResourceMapper.java | 7 ++ .../sonar/core/rule/DefaultRuleFinder.java | 16 +++- .../org/sonar/core/issue/IssueMapper.xml | 12 ++- .../org/sonar/core/persistence/schema-h2.ddl | 1 - .../sonar/core/resource/ResourceMapper.xml | 6 ++ .../org/sonar/core/issue/IssueDaoTest.java | 2 - .../org/sonar/core/issue/IssueDtoTest.java | 2 - .../core/issue/UpdateIssueFieldsTest.java | 4 - .../sonar/core/resource/ResourceDaoTest.java | 44 ++++++---- .../core/rule/DefaultRuleFinderTest.java | 48 ++++++----- .../core/issue/IssueDaoTest/insert-result.xml | 1 - .../issue/IssueDaoTest/should_select_all.xml | 3 - .../should_select_by_component_root.xml | 2 - .../should_select_by_date_creation.xml | 1 - .../IssueDaoTest/should_select_by_id.xml | 1 - .../IssueDaoTest/should_select_by_key.xml | 1 - .../IssueDaoTest/should_select_by_query.xml | 1 - .../IssueDaoTest/should_select_by_rules.xml | 3 - .../should_select_open_issues.xml | 3 - .../core/issue/IssueDaoTest/update-result.xml | 1 - .../sonar/core/issue/IssueDaoTest/update.xml | 1 - .../main/java/org/sonar/api/issue/Issue.java | 2 - .../java/org/sonar/api/issue/IssueFinder.java | 15 +++- .../java/org/sonar/api/rule/JRubyRules.java | 43 ++++++++++ .../main/java/org/sonar/api/rules/Rule.java | 17 +--- .../server/issue/DefaultJRubyIssues.java | 2 + .../sonar/server/issue/ServerIssueFinder.java | 82 +++++++++++++++++-- .../org/sonar/server/platform/Platform.java | 6 +- .../sonar/server/rule/DefaultJRubyRules.java | 53 ++++++++++++ .../org/sonar/server/ui/JRubyFacades.java | 11 +++ .../java/org/sonar/server/ui/JRubyI18n.java | 2 +- .../app/controllers/issues_controller.rb | 11 +-- .../src/main/webapp/WEB-INF/app/models/api.rb | 5 ++ .../WEB-INF/app/views/issues/_issue.html.erb | 18 ++-- .../WEB-INF/app/views/issues/_view.html.erb | 2 +- .../WEB-INF/db/migrate/385_create_issues.rb | 1 - .../server/issue/ServerIssueFinderTest.java | 60 +++++++++++++- .../server/rule/DefaultJRubyRulesTest.java | 60 ++++++++++++++ 48 files changed, 538 insertions(+), 149 deletions(-) create mode 100644 sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/rule/JRubyRules.java create mode 100644 sonar-server/src/main/java/org/sonar/server/rule/DefaultJRubyRules.java create mode 100644 sonar-server/src/test/java/org/sonar/server/rule/DefaultJRubyRulesTest.java diff --git a/sonar-batch/src/test/resources/org/sonar/batch/issue/IssuePersisterTest/should_insert_new_issue-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/issue/IssuePersisterTest/should_insert_new_issue-result.xml index 1a6c188b6c7..f038f923694 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/issue/IssuePersisterTest/should_insert_new_issue-result.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/issue/IssuePersisterTest/should_insert_new_issue-result.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/sonar-batch/src/test/resources/org/sonar/batch/issue/IssuePersisterTest/should_update_existing_issue-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/issue/IssuePersisterTest/should_update_existing_issue-result.xml index 4ad7e693016..b2f02754977 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/issue/IssuePersisterTest/should_update_existing_issue-result.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/issue/IssuePersisterTest/should_update_existing_issue-result.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/sonar-batch/src/test/resources/org/sonar/batch/issue/IssuePersisterTest/should_update_existing_issue.xml b/sonar-batch/src/test/resources/org/sonar/batch/issue/IssuePersisterTest/should_update_existing_issue.xml index 7e7c68964a6..d737d163c52 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/issue/IssuePersisterTest/should_update_existing_issue.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/issue/IssuePersisterTest/should_update_existing_issue.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java b/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java new file mode 100644 index 00000000000..a886424f247 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java @@ -0,0 +1,71 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.core.component; + +import org.sonar.api.component.Component; + +public class ComponentDto implements Component { + + private String key; + private String name; + private String longName; + private String qualifier; + + @Override + public String key() { + return key; + } + + public ComponentDto setKey(String key) { + this.key = key; + return this; + } + + @Override + public String name() { + return name; + } + + public ComponentDto setName(String name) { + this.name = name; + return this; + } + + @Override + public String longName() { + return longName; + } + + + public ComponentDto setLongName(String longName) { + this.longName = longName; + return this; + } + + @Override + public String qualifier() { + return qualifier; + } + + public ComponentDto setQualifier(String qualifier) { + this.qualifier = qualifier; + return this; + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java index 0fe18b0261e..629dd76f5ee 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java @@ -42,13 +42,11 @@ public class DefaultIssue implements Issue, Serializable { private static final Set RESOLUTIONS = ImmutableSet.of(RESOLUTION_OPEN, RESOLUTION_FALSE_POSITIVE, RESOLUTION_FIXED); private static final Set STATUSES = ImmutableSet.of(STATUS_OPEN, STATUS_CLOSED, STATUS_REOPENED, STATUS_RESOLVED); - private String key; private String componentKey; private RuleKey ruleKey; private String severity; private boolean manualSeverity = false; - private String title; private String description; private Integer line; private Double cost; @@ -111,15 +109,6 @@ public class DefaultIssue implements Issue, Serializable { return this; } - public String title() { - return title; - } - - public DefaultIssue setTitle(@Nullable String title) { - this.title = title; - return this; - } - public String description() { return description; } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java index b096f52ee82..51170faae43 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java @@ -92,7 +92,6 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder { issue.setComponentKey(componentKey); issue.setRuleKey(ruleKey); issue.setDescription(description); - issue.setTitle(title); issue.setSeverity(severity); issue.setCost(cost); diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueDto.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueDto.java index 5d9f0a16ece..47554fe215c 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/IssueDto.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/IssueDto.java @@ -42,7 +42,6 @@ public final class IssueDto { private String severity; private boolean manualSeverity; private boolean manualIssue; - private String title; private String description; private Integer line; private Double cost; @@ -125,15 +124,6 @@ public final class IssueDto { return this; } - public String getTitle() { - return title; - } - - public IssueDto setTitle(String title) { - this.title = title; - return this; - } - public String getDescription() { return description; } @@ -310,7 +300,6 @@ public final class IssueDto { return new IssueDto() .setKey(issue.key()) .setLine(issue.line()) - .setTitle(issue.title()) .setDescription(issue.description()) .setCost(issue.cost()) .setResolution(issue.resolution()) @@ -336,7 +325,6 @@ public final class IssueDto { issue.setStatus(status); issue.setResolution(resolution); issue.setDescription(description); - issue.setTitle(title); issue.setCost(cost); issue.setLine(line); issue.setSeverity(severity); diff --git a/sonar-core/src/main/java/org/sonar/core/issue/UpdateIssueFields.java b/sonar-core/src/main/java/org/sonar/core/issue/UpdateIssueFields.java index ba6a26d7d12..b207567906a 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/UpdateIssueFields.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/UpdateIssueFields.java @@ -35,9 +35,6 @@ public class UpdateIssueFields { if (change.severity() != null) { issue.setSeverity(change.severity()); } - if (change.title() != null) { - issue.setTitle(change.title()); - } if (change.isAssigneeChanged()) { issue.setAssignee(change.assignee()); } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java b/sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java index 455fba67ec3..b3444cf73d0 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java @@ -101,9 +101,6 @@ public class IssueWorkflow implements BatchComponent, ServerComponent, Startable if (change.severity() != null) { issue.setSeverity(change.severity()); } - if (change.title() != null) { - issue.setTitle(change.title()); - } if (change.isAssigneeChanged()) { issue.setAssignee(change.assignee()); } diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java index fbcba218fac..5e5d0f8d435 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java @@ -19,13 +19,17 @@ */ package org.sonar.core.resource; -import com.google.common.collect.Lists; import org.apache.ibatis.session.SqlSession; +import org.sonar.api.component.Component; +import org.sonar.core.component.ComponentDto; import org.sonar.core.persistence.MyBatis; +import java.util.Collection; import java.util.Date; import java.util.List; +import static com.google.common.collect.Lists.newArrayList; + public class ResourceDao { private MyBatis mybatis; @@ -95,7 +99,7 @@ public class ResourceDao { public List getDescendantProjects(long projectId, SqlSession session) { ResourceMapper mapper = session.getMapper(ResourceMapper.class); - List resources = Lists.newArrayList(); + List resources = newArrayList(); appendChildProjects(projectId, mapper, resources); return resources; } @@ -127,4 +131,26 @@ public class ResourceDao { } return this; } + + public Collection findByIds(Collection ids) { + SqlSession session = mybatis.openSession(); + try { + Collection resources = session.getMapper(ResourceMapper.class).selectResourcesById(ids); + Collection components = newArrayList(); + for (ResourceDto resourceDto : resources) { + components.add(toComponent(resourceDto)); + } + return components; + } finally { + MyBatis.closeQuietly(session); + } + } + + public ComponentDto toComponent(ResourceDto resourceDto){ + return new ComponentDto() + .setKey(resourceDto.getKey()) + .setLongName(resourceDto.getLongName()) + .setName(resourceDto.getName()) + .setQualifier(resourceDto.getQualifier()); + } } diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java index cf4d13374b5..a5a1e603f7b 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java @@ -19,8 +19,10 @@ */ package org.sonar.core.resource; +import org.apache.ibatis.annotations.Param; import org.apache.ibatis.session.ResultHandler; +import java.util.Collection; import java.util.List; public interface ResourceMapper { @@ -49,6 +51,11 @@ public interface ResourceMapper { */ void selectResources(ResourceQuery query, ResultHandler resultHandler); + /** + * @since3.6 + */ + List selectResourcesById(@Param("ids") Collection ids); + void insert(ResourceDto resource); void update(ResourceDto resource); diff --git a/sonar-core/src/main/java/org/sonar/core/rule/DefaultRuleFinder.java b/sonar-core/src/main/java/org/sonar/core/rule/DefaultRuleFinder.java index 59cf5b21e11..50996b16219 100644 --- a/sonar-core/src/main/java/org/sonar/core/rule/DefaultRuleFinder.java +++ b/sonar-core/src/main/java/org/sonar/core/rule/DefaultRuleFinder.java @@ -48,7 +48,7 @@ public class DefaultRuleFinder implements RuleFinder { protected final Rule doFindById(int ruleId) { DatabaseSession session = sessionFactory.getSession(); - return (Rule) session.getSingleResult( + return session.getSingleResult( session.createQuery("FROM " + Rule.class.getSimpleName() + " r WHERE r.id=:id and r.status<>:status") .setParameter("id", ruleId) .setParameter("status", Rule.STATUS_REMOVED @@ -56,6 +56,15 @@ public class DefaultRuleFinder implements RuleFinder { null); } + public Collection findByIds(Collection ruleIds) { + DatabaseSession session = sessionFactory.getSession(); + StringBuilder hql = new StringBuilder().append("from ").append(Rule.class.getSimpleName()).append(" r where r.id in (:ids) and status<>:status "); + Query hqlQuery = session.createQuery(hql.toString()) + .setParameter("status", Rule.STATUS_REMOVED) + .setParameter("ids", ruleIds); + return hqlQuery.getResultList(); + } + public Rule findByKey(RuleKey key) { return findByKey(key.repository(), key.rule()); } @@ -66,7 +75,7 @@ public class DefaultRuleFinder implements RuleFinder { protected final Rule doFindByKey(String repositoryKey, String key) { DatabaseSession session = sessionFactory.getSession(); - return (Rule) session.getSingleResult( + return session.getSingleResult( session.createQuery("FROM " + Rule.class.getSimpleName() + " r WHERE r.key=:key and r.pluginName=:pluginName and r.status<>:status") .setParameter("key", key) .setParameter("pluginName", repositoryKey) @@ -77,8 +86,7 @@ public class DefaultRuleFinder implements RuleFinder { public final Rule find(RuleQuery query) { DatabaseSession session = sessionFactory.getSession(); - return (Rule) session.getSingleResult(createHqlQuery(session, query), null); - + return session.getSingleResult(createHqlQuery(session, query), null); } public final Collection findAll(RuleQuery query) { 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 76bb9d86b42..fe2181c4f6d 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 @@ -12,7 +12,6 @@ i.severity as severity, i.manual_severity as manualSeverity, i.manual_issue as manualIssue, - i.title as title, i.description as description, i.line as line, i.cost as cost, @@ -32,9 +31,9 @@ - INSERT INTO issues (kee, resource_id, rule_id, severity, manual_severity, manual_issue, title, description, line, cost, status, + INSERT INTO issues (kee, resource_id, rule_id, severity, manual_severity, manual_issue, description, line, cost, status, resolution, checksum, user_login, assignee_login, author_login, attributes, created_at, updated_at, closed_at) - VALUES (#{kee}, #{resourceId}, #{ruleId}, #{severity}, #{manualSeverity}, #{manualIssue}, #{title}, #{description}, #{line}, #{cost}, #{status}, + VALUES (#{kee}, #{resourceId}, #{ruleId}, #{severity}, #{manualSeverity}, #{manualIssue}, #{description}, #{line}, #{cost}, #{status}, #{resolution}, #{checksum}, #{userLogin}, #{assignee}, #{authorLogin}, #{attributes}, #{createdAt}, #{updatedAt}, #{closedAt}) @@ -43,9 +42,9 @@ select issues_seq.NEXTVAL from DUAL - INSERT INTO issues (id, kee, resource_id, rule_id, severity, manual_severity, manual_issue, title, description, line, cost, status, + INSERT INTO issues (id, kee, resource_id, rule_id, severity, manual_severity, manual_issue, description, line, cost, status, resolution, checksum, user_login, assignee_login, author_login, attributes, created_at, updated_at, closed_at) - VALUES (#{id}, #{kee}, #{resourceId}, #{ruleId}, #{severity}, #{manualSeverity}, #{manualIssue}, #{title}, #{description}, #{line}, #{cost}, #{status}, + VALUES (#{id}, #{kee}, #{resourceId}, #{ruleId}, #{severity}, #{manualSeverity}, #{manualIssue}, #{description}, #{line}, #{cost}, #{status}, #{resolution}, #{checksum}, #{userLogin}, #{assignee}, #{authorLogin}, #{attributes}, #{createdAt}, #{updatedAt}, #{closedAt}) @@ -56,8 +55,7 @@ severity=#{severity}, manual_severity=#{manualSeverity}, manual_issue=#{manualIssue}, - title=#{title}, - description=#{description}, + description=#{description}, line=#{line}, cost=#{cost}, status=#{status}, diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl index ceeca30bf22..805f80fe806 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl @@ -523,7 +523,6 @@ CREATE TABLE "ISSUES" ( "SEVERITY" VARCHAR(10), "MANUAL_SEVERITY" BOOLEAN NOT NULL, "MANUAL_ISSUE" BOOLEAN NOT NULL, - "TITLE" VARCHAR(500), "DESCRIPTION" VARCHAR(4000), "LINE" INTEGER, "COST" DOUBLE, diff --git a/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml b/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml index 3d14bcf1d62..24482461298 100644 --- a/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml @@ -75,6 +75,12 @@ select * from projects where id=#{id} + + 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 451998b31b7..ddd4d552e6d 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 @@ -83,7 +83,6 @@ public class IssueDaoTest extends AbstractDaoTestCase { issue.setAssignee("new_user"); issue.setManualSeverity(true); issue.setManualIssue(false); - issue.setTitle("NEW_TITLE"); issue.setCreatedAt(DateUtils.parseDate("2012-05-18")); issue.setUpdatedAt(DateUtils.parseDate("2012-07-01")); issue.setAttributes("big=bang"); @@ -104,7 +103,6 @@ public class IssueDaoTest extends AbstractDaoTestCase { assertThat(issue.getSeverity()).isEqualTo("BLOCKER"); assertThat(issue.isManualSeverity()).isFalse(); assertThat(issue.isManualIssue()).isFalse(); - assertThat(issue.getTitle()).isNull(); assertThat(issue.getDescription()).isNull(); assertThat(issue.getLine()).isEqualTo(200); assertThat(issue.getCost()).isEqualTo(4.2); diff --git a/sonar-core/src/test/java/org/sonar/core/issue/IssueDtoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/IssueDtoTest.java index d7f5fcfce9c..b60d47d223b 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/IssueDtoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/IssueDtoTest.java @@ -63,7 +63,6 @@ public class IssueDtoTest { .setCost(15.0) .setLine(6) .setSeverity("BLOCKER") - .setTitle("title") .setDescription("message") .setManualSeverity(true) .setManualIssue(true) @@ -84,7 +83,6 @@ public class IssueDtoTest { assertThat(issue.cost()).isEqualTo(15.0); assertThat(issue.line()).isEqualTo(6); assertThat(issue.severity()).isEqualTo("BLOCKER"); - assertThat(issue.title()).isEqualTo("title"); assertThat(issue.description()).isEqualTo("message"); assertThat(issue.isManualSeverity()).isTrue(); assertThat(issue.manual()).isTrue(); diff --git a/sonar-core/src/test/java/org/sonar/core/issue/UpdateIssueFieldsTest.java b/sonar-core/src/test/java/org/sonar/core/issue/UpdateIssueFieldsTest.java index 4e17798bdb3..b211019fab3 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/UpdateIssueFieldsTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/UpdateIssueFieldsTest.java @@ -20,7 +20,6 @@ package org.sonar.core.issue; import org.junit.Test; -import org.sonar.api.issue.Issue; import org.sonar.api.issue.IssueChange; import org.sonar.api.rule.Severity; @@ -42,7 +41,6 @@ public class UpdateIssueFieldsTest { .setCost(4.2) ); assertThat(issue.line()).isEqualTo(200); - assertThat(issue.title()).isEqualTo("new title"); assertThat(issue.description()).isEqualTo("new desc"); assertThat(issue.attribute("JIRA")).isEqualTo("FOO-123"); assertThat(issue.severity()).isEqualTo(Severity.CRITICAL); @@ -56,7 +54,6 @@ public class UpdateIssueFieldsTest { .setComponentKey("org/struts/Action.java") .setKey("ABCDE") .setLine(123) - .setTitle("the title") .setDescription("the desc") .setAssignee("karadoc") .setCost(4.2) @@ -77,6 +74,5 @@ public class UpdateIssueFieldsTest { assertThat(issue.cost()).isEqualTo(4.2); assertThat(issue.isManualSeverity()).isTrue(); assertThat(issue.description()).isEqualTo("the desc"); - assertThat(issue.title()).isEqualTo("the title"); } } diff --git a/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java b/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java index 530fac41c24..961467b4177 100644 --- a/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java @@ -21,12 +21,15 @@ package org.sonar.core.resource; import org.junit.Before; import org.junit.Test; +import org.sonar.api.component.Component; import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Scopes; import org.sonar.core.persistence.AbstractDaoTestCase; +import java.util.Collection; import java.util.List; +import static com.google.common.collect.Lists.newArrayList; import static org.fest.assertions.Assertions.assertThat; public class ResourceDaoTest extends AbstractDaoTestCase { @@ -90,13 +93,13 @@ public class ResourceDaoTest extends AbstractDaoTestCase { public void getResources_filter_by_qualifier() { setupData("fixture"); - List resources = dao.getResources(ResourceQuery.create().setQualifiers(new String[] {"TRK", "BRC"})); + List resources = dao.getResources(ResourceQuery.create().setQualifiers(new String[]{"TRK", "BRC"})); assertThat(resources).onProperty("qualifier").containsOnly("TRK", "BRC"); - resources = dao.getResources(ResourceQuery.create().setQualifiers(new String[] {"XXX"})); + resources = dao.getResources(ResourceQuery.create().setQualifiers(new String[]{"XXX"})); assertThat(resources).isEmpty(); - resources = dao.getResources(ResourceQuery.create().setQualifiers(new String[] {})); + resources = dao.getResources(ResourceQuery.create().setQualifiers(new String[]{})); assertThat(resources).hasSize(4); } @@ -125,13 +128,13 @@ public class ResourceDaoTest extends AbstractDaoTestCase { public void getResourceIds_filter_by_qualifier() { setupData("fixture"); - List ids = dao.getResourceIds(ResourceQuery.create().setQualifiers(new String[] {"TRK", "BRC"})); + List ids = dao.getResourceIds(ResourceQuery.create().setQualifiers(new String[]{"TRK", "BRC"})); assertThat(ids).containsOnly(1L, 2L); - ids = dao.getResourceIds(ResourceQuery.create().setQualifiers(new String[] {"XXX"})); + ids = dao.getResourceIds(ResourceQuery.create().setQualifiers(new String[]{"XXX"})); assertThat(ids).isEmpty(); - ids = dao.getResourceIds(ResourceQuery.create().setQualifiers(new String[] {})); + ids = dao.getResourceIds(ResourceQuery.create().setQualifiers(new String[]{})); assertThat(ids).hasSize(4); } @@ -143,14 +146,27 @@ public class ResourceDaoTest extends AbstractDaoTestCase { assertThat(dao.getResourceIds(ResourceQuery.create().setExcludeDisabled(true))).containsOnly(2L); } + @Test + public void should_find_components_by_resource_ids() { + setupData("fixture"); + + Collection results = dao.findByIds(newArrayList(1)); + assertThat(results).hasSize(1); + Component component = results.iterator().next(); + assertThat(component.key()).isNotNull(); + assertThat(component.name()).isNotNull(); + assertThat(component.longName()).isNotNull(); + assertThat(component.qualifier()).isNotNull(); + } + @Test public void should_update() { setupData("update"); ResourceDto project = new ResourceDto() - .setKey("org.struts:struts").setScope(Scopes.PROJECT).setQualifier(Qualifiers.PROJECT) - .setName("Struts").setLongName("Apache Struts").setLanguage("java").setDescription("MVC Framework") - .setId(1L); + .setKey("org.struts:struts").setScope(Scopes.PROJECT).setQualifier(Qualifiers.PROJECT) + .setName("Struts").setLongName("Apache Struts").setLanguage("java").setDescription("MVC Framework") + .setId(1L); dao.insertOrUpdate(project); @@ -163,17 +179,17 @@ public class ResourceDaoTest extends AbstractDaoTestCase { setupData("insert"); ResourceDto file1 = new ResourceDto() - .setKey("org.struts:struts:org.struts.Action").setScope(Scopes.FILE).setQualifier(Qualifiers.FILE) - .setLanguage("java").setName("Action").setLongName("org.struts.Action"); + .setKey("org.struts:struts:org.struts.Action").setScope(Scopes.FILE).setQualifier(Qualifiers.FILE) + .setLanguage("java").setName("Action").setLongName("org.struts.Action"); ResourceDto file2 = new ResourceDto() - .setKey("org.struts:struts:org.struts.Filter").setScope(Scopes.FILE).setQualifier(Qualifiers.FILE) - .setLanguage("java").setName("Filter").setLongName("org.struts.Filter"); + .setKey("org.struts:struts:org.struts.Filter").setScope(Scopes.FILE).setQualifier(Qualifiers.FILE) + .setLanguage("java").setName("Filter").setLongName("org.struts.Filter"); dao.insertOrUpdate(file1, file2); assertThat(file1.getId()).isNotNull(); assertThat(file2.getId()).isNotNull(); - checkTables("insert", new String[] {"created_at"}, "projects"); + checkTables("insert", new String[]{"created_at"}, "projects"); // SONAR-3636 : created_at must be fed when inserting a new entry in the 'projects' table ResourceDto fileLoadedFromDB = dao.getResource(file1.getId()); diff --git a/sonar-core/src/test/java/org/sonar/core/rule/DefaultRuleFinderTest.java b/sonar-core/src/test/java/org/sonar/core/rule/DefaultRuleFinderTest.java index cc2c899007e..f2bdccae419 100644 --- a/sonar-core/src/test/java/org/sonar/core/rule/DefaultRuleFinderTest.java +++ b/sonar-core/src/test/java/org/sonar/core/rule/DefaultRuleFinderTest.java @@ -27,49 +27,53 @@ import org.sonar.jpa.test.AbstractDbUnitTestCase; import java.util.Collection; +import static com.google.common.collect.Lists.newArrayList; import static org.fest.assertions.Assertions.assertThat; -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsNull.nullValue; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; public class DefaultRuleFinderTest extends AbstractDbUnitTestCase { @Test - public void shouldFindById() { + public void should_find_by_id() { setupData("shared"); RuleFinder finder = new DefaultRuleFinder(getSessionFactory()); - assertThat(finder.findById(3).getConfigKey(), is("Checker/Treewalker/AnnotationUseStyleCheck")); + assertThat(finder.findById(3).getConfigKey()).isEqualTo("Checker/Treewalker/AnnotationUseStyleCheck"); } @Test - public void shouldNotFindDisabledRuleById() { + public void should_not_find_disabled_rule_by_id() { setupData("shared"); RuleFinder finder = new DefaultRuleFinder(getSessionFactory()); - assertThat(finder.findById(2), nullValue()); + assertThat(finder.findById(2)).isNull(); } @Test - public void shouldFindByKey() { + public void should_find_by_ids() { + setupData("shared"); + DefaultRuleFinder finder = new DefaultRuleFinder(getSessionFactory()); + // 2 is disabled + assertThat(finder.findByIds(newArrayList(2, 3))).hasSize(1); + } + + @Test + public void should_find_by_key() { setupData("shared"); RuleFinder finder = new DefaultRuleFinder(getSessionFactory()); Rule rule = finder.findByKey("checkstyle", "com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck"); - assertNotNull(rule); - assertThat(rule.getKey(), is("com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck")); - assertThat(rule.isEnabled(), is(true)); + assertThat(rule).isNotNull(); + assertThat(rule.getKey()).isEqualTo(("com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck")); + assertThat(rule.isEnabled()).isTrue(); } @Test - public void findShouldReturnNullIfNoResults() { + public void find_should_return_null_if_no_results() { setupData("shared"); RuleFinder finder = new DefaultRuleFinder(getSessionFactory()); - assertNull(finder.findByKey("checkstyle", "unknown")); - assertNull(finder.find(RuleQuery.create().withRepositoryKey("checkstyle").withConfigKey("unknown"))); + assertThat(finder.findByKey("checkstyle", "unknown")).isNull(); + assertThat(finder.find(RuleQuery.create().withRepositoryKey("checkstyle").withConfigKey("unknown"))).isNull(); } @Test - public void findRepositoryRules() { + public void find_repository_rules() { setupData("shared"); RuleFinder finder = new DefaultRuleFinder(getSessionFactory()); Collection rules = finder.findAll(RuleQuery.create().withRepositoryKey("checkstyle")); @@ -78,7 +82,7 @@ public class DefaultRuleFinderTest extends AbstractDbUnitTestCase { } @Test - public void findAllEnabled() { + public void find_all_enabled() { setupData("shared"); RuleFinder finder = new DefaultRuleFinder(getSessionFactory()); Collection rules = finder.findAll(RuleQuery.create()); @@ -87,18 +91,18 @@ public class DefaultRuleFinderTest extends AbstractDbUnitTestCase { } @Test - public void doNotFindDisabledRules() { + public void do_not_find_disabled_rules() { setupData("shared"); RuleFinder finder = new DefaultRuleFinder(getSessionFactory()); Rule rule = finder.findByKey("checkstyle", "DisabledCheck"); - assertNull(rule); + assertThat(rule).isNull(); } @Test - public void doNotFindUnknownRules() { + public void do_not_find_unknown_rules() { setupData("shared"); RuleFinder finder = new DefaultRuleFinder(getSessionFactory()); Collection rules = finder.findAll(RuleQuery.create().withRepositoryKey("unknown_repository")); - assertThat(rules.size(), is(0)); + assertThat(rules).isEmpty(); } } diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/insert-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/insert-result.xml index 36b63884e8d..5868688a27b 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/insert-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/insert-result.xml @@ -7,7 +7,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="the description" line="200" cost="10.0" diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_all.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_all.xml index 31c11303b42..4a27dd7013c 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_all.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_all.xml @@ -9,7 +9,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="4.2" @@ -33,7 +32,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="4.2" @@ -59,7 +57,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="4.2" diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_component_root.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_component_root.xml index 0d7e81eaa15..e9308bf33ae 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_component_root.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_component_root.xml @@ -8,7 +8,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="[null]" @@ -32,7 +31,6 @@ severity="MAJOR" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="120" cost="[null]" diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_date_creation.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_date_creation.xml index 0be9f22976a..11fe9b857b8 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_date_creation.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_date_creation.xml @@ -8,7 +8,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="4.2" diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_id.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_id.xml index ea7465b52c3..8c738fe5fc4 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_id.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_id.xml @@ -8,7 +8,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="4.2" diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_key.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_key.xml index 0be9f22976a..11fe9b857b8 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_key.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_key.xml @@ -8,7 +8,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="4.2" diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_query.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_query.xml index 0be9f22976a..11fe9b857b8 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_query.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_query.xml @@ -8,7 +8,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="4.2" diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_rules.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_rules.xml index b4126a6a8e8..a1223eafc6e 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_rules.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_rules.xml @@ -9,7 +9,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="4.2" @@ -33,7 +32,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="4.2" @@ -59,7 +57,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="4.2" diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_open_issues.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_open_issues.xml index 3457c423977..dbf79e29804 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_open_issues.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_open_issues.xml @@ -8,7 +8,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="[null]" @@ -32,7 +31,6 @@ severity="MAJOR" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="120" cost="[null]" @@ -56,7 +54,6 @@ severity="MAJOR" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="120" cost="[null]" diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/update-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/update-result.xml index 7fa92d380bc..0574a4c9d88 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/update-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/update-result.xml @@ -8,7 +8,6 @@ severity="NEW_SEV" manual_severity="[true]" manual_issue="[false]" - title="NEW_TITLE" description="[null]" line="1000" cost="[null]" diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/update.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/update.xml index b83b83c4670..d701d8d68c3 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/update.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/update.xml @@ -8,7 +8,6 @@ severity="BLOCKER" manual_severity="[false]" manual_issue="[false]" - title="[null]" description="[null]" line="200" cost="[null]" diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java index d842bec2d4e..57ae00e3a45 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java @@ -50,8 +50,6 @@ public interface Issue { String severity(); - String title(); - String description(); Integer line(); 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 43837b19f7d..e3c3b5a62ce 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 @@ -21,8 +21,12 @@ package org.sonar.api.issue; import org.sonar.api.ServerComponent; +import org.sonar.api.component.Component; +import org.sonar.api.rules.Rule; import javax.annotation.Nullable; + +import java.util.Collection; import java.util.List; /** @@ -35,12 +39,21 @@ public interface IssueFinder extends ServerComponent { interface Results { List issues(); + + Rule rule(Issue issue); + + Collection rules(); + + Component component(Issue issue); + + Collection components(); } Results find(IssueQuery query, @Nullable Integer currentUserId, String role); Issue findByKey(String key /* TODO @Nullable Integer currentUserId */); -/* + + /* Map rules(Collection issues); Map components(Collection issues); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rule/JRubyRules.java b/sonar-plugin-api/src/main/java/org/sonar/api/rule/JRubyRules.java new file mode 100644 index 00000000000..87656e61133 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rule/JRubyRules.java @@ -0,0 +1,43 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.api.rule; + +import org.sonar.api.ServerComponent; + +/** + * Facade for JRuby on Rails extensions to request rules. + *

+ * Reference from Ruby code : Api.rules + *

+ * + * @since 3.6 + */ +public interface JRubyRules extends ServerComponent { + + /** + * Return the localized name of a rule. + * + *

+ * Ruby: Api.rules.ruleName(I18n.locale, rule.rule_key) + *

+ */ + String ruleName(String rubyLocale, RuleKey ruleKey); + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java index 282d5b0cfb6..3a5cc176be2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java @@ -32,19 +32,7 @@ import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.SonarException; import org.sonar.check.Cardinality; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; +import javax.persistence.*; import java.util.ArrayList; import java.util.Date; @@ -545,6 +533,9 @@ public final class Rule { return ImmutableSet.of(STATUS_READY, STATUS_BETA, STATUS_DEPRECATED, STATUS_REMOVED); } + /** + * @since 3.6 + */ public RuleKey ruleKey() { return RuleKey.of(getRepositoryKey(), getKey()); } diff --git a/sonar-server/src/main/java/org/sonar/server/issue/DefaultJRubyIssues.java b/sonar-server/src/main/java/org/sonar/server/issue/DefaultJRubyIssues.java index f1d7e07121b..8052a2aa4a6 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/DefaultJRubyIssues.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/DefaultJRubyIssues.java @@ -33,6 +33,7 @@ import org.sonar.api.web.UserRole; import org.sonar.server.ui.JRubyFacades; import javax.annotation.Nullable; + import java.util.Collection; import java.util.Date; import java.util.List; @@ -57,6 +58,7 @@ public class DefaultJRubyIssues implements JRubyIssues { /** * Requires the role {@link org.sonar.api.web.UserRole#CODEVIEWER} */ + @Override public IssueFinder.Results find(Map params, @Nullable Integer currentUserId) { // TODO move the role to IssueFinder return finder.find(toQuery(params), currentUserId, UserRole.CODEVIEWER); diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueFinder.java b/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueFinder.java index 16333b441fe..f406f4867b7 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueFinder.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueFinder.java @@ -19,39 +19,52 @@ */ package org.sonar.server.issue; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.ibatis.session.SqlSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.component.Component; 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.core.issue.IssueDao; import org.sonar.core.issue.IssueDto; import org.sonar.core.persistence.MyBatis; +import org.sonar.core.resource.ResourceDao; +import org.sonar.core.rule.DefaultRuleFinder; import org.sonar.core.user.AuthorizationDao; import javax.annotation.Nullable; + +import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Set; +import static com.google.common.collect.Maps.newHashMap; + /** * @since 3.6 */ public class ServerIssueFinder implements IssueFinder { private static final Logger LOG = LoggerFactory.getLogger(ServerIssueFinder.class); - private final MyBatis myBatis; private final IssueDao issueDao; private final AuthorizationDao authorizationDao; + private final DefaultRuleFinder ruleFinder; + private final ResourceDao resourceDao; - - public ServerIssueFinder(MyBatis myBatis, IssueDao issueDao, AuthorizationDao authorizationDao) { + public ServerIssueFinder(MyBatis myBatis, IssueDao issueDao, AuthorizationDao authorizationDao, DefaultRuleFinder ruleFinder, ResourceDao resourceDao) { this.myBatis = myBatis; this.issueDao = issueDao; this.authorizationDao = authorizationDao; + this.ruleFinder = ruleFinder; + this.resourceDao = resourceDao; } public Results find(IssueQuery query, @Nullable Integer currentUserId, String role) { @@ -65,18 +78,56 @@ public class ServerIssueFinder implements IssueFinder { componentIds.add(issueDto.getResourceId()); } Set authorizedComponentIds = authorizationDao.keepAuthorizedComponentIds(componentIds, currentUserId, role, sqlSession); + Set ruleIds = Sets.newLinkedHashSet(); List issues = Lists.newArrayList(); for (IssueDto dto : dtos) { if (authorizedComponentIds.contains(dto.getResourceId())) { issues.add(dto.toDefaultIssue()); + ruleIds.add(dto.getRuleId()); } } - return new DefaultResults(issues); + return new DefaultResults(issues, getRulesByIssue(issues, ruleIds), getComponentsByIssue(issues, componentIds)); } finally { MyBatis.closeQuietly(sqlSession); } } + private Map getRulesByIssue(List issues, Set ruleIds) { + Map rulesByIssue = newHashMap(); + Collection rules = ruleFinder.findByIds(ruleIds); + for (Issue issue : issues) { + rulesByIssue.put(issue, findRule(issue, rules)); + } + return rulesByIssue; + } + + private Rule findRule(final Issue issue, Collection rules) { + return Iterables.find(rules, new Predicate() { + @Override + public boolean apply(Rule rule) { + return issue.ruleKey().equals(rule.ruleKey()); + } + }, null); + } + + private Map getComponentsByIssue(List issues, Set componentIds) { + Map componentsByIssue = newHashMap(); + Collection components = resourceDao.findByIds(componentIds); + for (Issue issue : issues) { + componentsByIssue.put(issue, findComponent(issue, components)); + } + return componentsByIssue; + } + + private Component findComponent(final Issue issue, Collection components) { + return Iterables.find(components, new Predicate() { + @Override + public boolean apply(Component component) { + return issue.componentKey().equals(component.key()); + } + }, null); + } + public Issue findByKey(String key) { IssueDto dto = issueDao.selectByKey(key); return dto != null ? dto.toDefaultIssue() : null; @@ -84,14 +135,35 @@ public class ServerIssueFinder implements IssueFinder { static class DefaultResults implements Results { private final List issues; + private final Map rulesByIssue; + private final Map componentsByIssue; - DefaultResults(List issues) { + DefaultResults(List issues, Map rulesByIssue, Map componentsByIssue) { this.issues = issues; + this.rulesByIssue = rulesByIssue; + this.componentsByIssue = componentsByIssue; } @Override public List issues() { return issues; } + + public Rule rule(Issue issue) { + return rulesByIssue.get(issue); + } + + public Collection rules() { + return rulesByIssue.values(); + } + + public Component component(Issue issue) { + return componentsByIssue.get(issue); + } + + public Collection components() { + return componentsByIssue.values(); + } + } } 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 ecbd17a2f4d..5af1888b1ee 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,7 +41,6 @@ import org.sonar.core.i18n.GwtI18n; import org.sonar.core.i18n.I18nManager; import org.sonar.core.i18n.RuleI18nManager; import org.sonar.core.issue.workflow.IssueWorkflow; -import org.sonar.server.issue.ServerIssueFinder; import org.sonar.core.measure.MeasureFilterEngine; import org.sonar.core.measure.MeasureFilterExecutor; import org.sonar.core.measure.MeasureFilterFactory; @@ -72,12 +71,14 @@ import org.sonar.server.configuration.ProfilesManager; import org.sonar.server.database.EmbeddedDatabaseFactory; import org.sonar.server.issue.DefaultJRubyIssues; import org.sonar.server.issue.ServerIssueChanges; +import org.sonar.server.issue.ServerIssueFinder; import org.sonar.server.macro.MacroInterpreter; import org.sonar.server.notifications.NotificationCenter; import org.sonar.server.notifications.NotificationService; import org.sonar.server.notifications.reviews.ReviewsNotificationManager; import org.sonar.server.plugins.*; import org.sonar.server.qualitymodel.DefaultModelManager; +import org.sonar.server.rule.DefaultJRubyRules; import org.sonar.server.rules.ProfilesConsole; import org.sonar.server.rules.RulesConsole; import org.sonar.server.startup.*; @@ -243,6 +244,9 @@ public final class Platform { servicesContainer.addSingleton(ServerIssueFinder.class); servicesContainer.addSingleton(DefaultJRubyIssues.class); + // rules + servicesContainer.addSingleton(DefaultJRubyRules.class); + // Notifications servicesContainer.addSingleton(EmailSettings.class); servicesContainer.addSingleton(NotificationService.class); diff --git a/sonar-server/src/main/java/org/sonar/server/rule/DefaultJRubyRules.java b/sonar-server/src/main/java/org/sonar/server/rule/DefaultJRubyRules.java new file mode 100644 index 00000000000..7323d0a2d3c --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule/DefaultJRubyRules.java @@ -0,0 +1,53 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.rule; + +import org.sonar.api.rule.JRubyRules; +import org.sonar.api.rule.RuleKey; +import org.sonar.server.ui.JRubyFacades; +import org.sonar.server.ui.JRubyI18n; + +/** + * Facade of rules components for JRuby on Rails webapp + * + * @since 3.6 + */ +public class DefaultJRubyRules implements JRubyRules { + + private final JRubyI18n jRubyI18n; + + public DefaultJRubyRules(JRubyI18n jRubyI18n) { + this.jRubyI18n = jRubyI18n; + JRubyFacades.setRules(this); + } + + public String ruleName(String rubyLocale, RuleKey ruleKey) { + String l18n = jRubyI18n.getRuleName(rubyLocale, ruleKey.repository(), ruleKey.rule()); + if (l18n != null) { + return l18n; + } else { + return jRubyI18n.getRuleName("en", ruleKey.repository(), ruleKey.rule()); + } + } + + public void start() { + // used to force pico to instantiate the singleton at startup + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacades.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacades.java index cf7e4cf3a0d..0cd75620df2 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacades.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacades.java @@ -21,6 +21,7 @@ package org.sonar.server.ui; import org.sonar.api.ServerComponent; import org.sonar.api.issue.JRubyIssues; +import org.sonar.api.rule.JRubyRules; /** * All the facades to Java components @@ -30,6 +31,7 @@ import org.sonar.api.issue.JRubyIssues; public class JRubyFacades implements ServerComponent { private static JRubyIssues issues = null; + private static JRubyRules rules = null; public static void setIssues(JRubyIssues i) { JRubyFacades.issues = i; @@ -38,4 +40,13 @@ public class JRubyFacades implements ServerComponent { public static JRubyIssues issues() { return issues; } + + public static void setRules(JRubyRules rules) { + JRubyFacades.rules = rules; + } + + public static JRubyRules rules() { + return rules; + } + } diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyI18n.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyI18n.java index 5f4bb703e50..13f62128a21 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyI18n.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyI18n.java @@ -33,7 +33,7 @@ import java.util.Map; /** * Bridge between JRuby webapp and Java I18n component */ -public final class JRubyI18n implements ServerComponent { +public class JRubyI18n implements ServerComponent { private I18n i18n; private Map localesByRubyKey = Maps.newHashMap(); diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb index 3f7e4e42db5..31421e48d46 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb @@ -28,11 +28,12 @@ class IssuesController < ApplicationController # Used for the permalink, e.g. http://localhost:9000/issues/view/1 def view - issues = find_issues({'keys' => params[:id]}) - if issues.length == 1 - @issue = issues[0] + issue_result = find_issues({'keys' => params[:id]}) + if issue_result.issues.length == 1 + @issue = issue_result.issues[0] + @rule = issue_result.rule(@issue) @resource = Project.by_key(@issue.component_key) - render 'issues/_view', :locals => {:issue => @issue} + render 'issues/_view', :locals => {:issue => @issue, :rule => @rule, :resource => @resource} else render :text => "Cannot access this issue : not found." end @@ -42,7 +43,7 @@ class IssuesController < ApplicationController def find_issues(map) user = current_user ? current_user.id : nil - Api.issues.find(map, user).issues + Api.issues.find(map, user) end end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/api.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/api.rb index c79b6ef8e94..478d9a3a591 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/api.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/api.rb @@ -25,4 +25,9 @@ class Api Java::OrgSonarServerUi::JRubyFacades.issues() end + # since 3.6 + def self.rules + Java::OrgSonarServerUi::JRubyFacades.rules() + end + end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_issue.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_issue.html.erb index 05d2bd36866..859714b9f85 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_issue.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_issue.html.erb @@ -58,14 +58,14 @@ <% # TODO action plan %> - <% if issue.rule_key %> - <% rule = Rule.by_key_or_id(issue.rule_key.to_s) %> + <% if rule %> + <% rule_name = Api.rules.rule_name(I18n.locale, rule.rule_key) %> <%= message('rule') -%>: - <%= h(rule.name) -%> + <%= h(rule_name) -%> <% end %> @@ -74,14 +74,14 @@ <%= message('file') -%>: - <%= qualifier_icon(@resource) -%> - <% if @resource.root_project.id != @resource.id %> - <%= @resource.root_project.long_name -%> <%= image_tag 'sep12.png' -%> + <%= qualifier_icon(resource) -%> + <% if resource.root_project.id != resource.id %> + <%= resource.root_project.long_name -%> <%= image_tag 'sep12.png' -%> <% end %> - <% if @resource.last_snapshot %> - <%= link_to_resource(@resource, @resource.long_name, {:tab => :violations, :rule => issue.resolution == "FALSE-POSITIVE" ? "false_positive_issues" : ""}) %> + <% if resource.last_snapshot %> + <%= link_to_resource(resource, resource.long_name, {:tab => :violations, :rule => issue.resolution == "FALSE-POSITIVE" ? "false_positive_issues" : ""}) %> <% else %> - <%= @resource.long_name -%> + <%= resource.long_name -%> <% end %> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_view.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_view.html.erb index a75722ceb02..048a5dbc067 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_view.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/issues/_view.html.erb @@ -3,5 +3,5 @@ # hack in case 'error_message' is nil (this should disappear when refactoring the '_view' and '_issue' partials) error_message = error_message %> - <%= render :partial => 'issues/issue', :locals => {:issue => @issue, :workflow => @issue, :error_message => error_message} -%> + <%= render :partial => 'issues/issue', :locals => {:issue => @issue, :rule => @rule, :resource => @resource, :workflow => @issue, :error_message => error_message} -%> diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/385_create_issues.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/385_create_issues.rb index 3da2d1b97b7..51635e96e02 100644 --- a/sonar-server/src/main/webapp/WEB-INF/db/migrate/385_create_issues.rb +++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/385_create_issues.rb @@ -31,7 +31,6 @@ class CreateIssues < ActiveRecord::Migration t.column :severity, :string, :null => true, :limit => 10 t.column :manual_severity, :boolean, :null => false t.column :manual_issue, :boolean, :null => false - t.column :title, :string , :null => true, :limit => 500 t.column :description, :string, :null => true, :limit => 4000 t.column :line, :integer, :null => true t.column :cost, :decimal, :null => true, :precision => 30, :scale => 20 diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueFinderTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueFinderTest.java index 66b4c0950f6..d3bf37c085e 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueFinderTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueFinderTest.java @@ -24,13 +24,18 @@ import org.junit.Before; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import org.sonar.api.component.Component; 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.web.UserRole; +import org.sonar.core.component.ComponentDto; import org.sonar.core.issue.IssueDao; import org.sonar.core.issue.IssueDto; import org.sonar.core.persistence.MyBatis; +import org.sonar.core.resource.ResourceDao; +import org.sonar.core.rule.DefaultRuleFinder; import org.sonar.core.user.AuthorizationDao; import java.util.List; @@ -45,15 +50,20 @@ public class ServerIssueFinderTest { MyBatis mybatis; ServerIssueFinder finder; + IssueDao issueDao; AuthorizationDao authorizationDao; + DefaultRuleFinder ruleFinder; + ResourceDao resourceDao; @Before public void before() { mybatis = mock(MyBatis.class); issueDao = mock(IssueDao.class); authorizationDao = mock(AuthorizationDao.class); - finder = new ServerIssueFinder(mybatis, issueDao, authorizationDao); + ruleFinder = mock(DefaultRuleFinder.class); + resourceDao = mock(ResourceDao.class); + finder = new ServerIssueFinder(mybatis, issueDao, authorizationDao, ruleFinder, resourceDao); } @Test @@ -90,6 +100,54 @@ public class ServerIssueFinderTest { assertThat(issue.ruleKey().toString()).isEqualTo("squid:AvoidCycle"); } + @Test + public void should_get_rule_from_result() { + Rule rule = Rule.create().setRepositoryKey("squid").setKey("AvoidCycle"); + when(ruleFinder.findByIds(anyCollection())).thenReturn(newArrayList(rule)); + + grantAccessRights(); + IssueQuery issueQuery = mock(IssueQuery.class); + + IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123) + .setComponentKey_unit_test_only("Action.java") + .setRuleKey_unit_test_only("squid", "AvoidCycle"); + IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(123) + .setComponentKey_unit_test_only("Action.java") + .setRuleKey_unit_test_only("squid", "AvoidCycle"); + List dtoList = newArrayList(issue1, issue2); + when(issueDao.select(eq(issueQuery), any(SqlSession.class))).thenReturn(dtoList); + + IssueFinder.Results results = finder.find(issueQuery, null, UserRole.USER); + Issue issue = results.issues().iterator().next(); + assertThat(results.issues()).hasSize(2); + assertThat(results.rule(issue)).isEqualTo(rule); + assertThat(results.rules()).hasSize(1); + } + + @Test + public void should_get_component_from_result() { + Component component = new ComponentDto().setKey("Action.java"); + when(resourceDao.findByIds(anyCollection())).thenReturn(newArrayList(component)); + + grantAccessRights(); + IssueQuery issueQuery = mock(IssueQuery.class); + + IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123) + .setComponentKey_unit_test_only("Action.java") + .setRuleKey_unit_test_only("squid", "AvoidCycle"); + IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(123) + .setComponentKey_unit_test_only("Action.java") + .setRuleKey_unit_test_only("squid", "AvoidCycle"); + List dtoList = newArrayList(issue1, issue2); + when(issueDao.select(eq(issueQuery), any(SqlSession.class))).thenReturn(dtoList); + + IssueFinder.Results results = finder.find(issueQuery, null, UserRole.USER); + Issue issue = results.issues().iterator().next(); + assertThat(results.issues()).hasSize(2); + assertThat(results.component(issue)).isEqualTo(component); + assertThat(results.components()).hasSize(1); + } + private void grantAccessRights() { when(authorizationDao.keepAuthorizedComponentIds(anySet(), anyInt(), anyString(), any(SqlSession.class))) .thenAnswer(new Answer() { diff --git a/sonar-server/src/test/java/org/sonar/server/rule/DefaultJRubyRulesTest.java b/sonar-server/src/test/java/org/sonar/server/rule/DefaultJRubyRulesTest.java new file mode 100644 index 00000000000..67820369694 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/rule/DefaultJRubyRulesTest.java @@ -0,0 +1,60 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.rule; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.rule.RuleKey; +import org.sonar.server.ui.JRubyI18n; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultJRubyRulesTest { + + private DefaultJRubyRules defaultJRubyRules; + private JRubyI18n jRubyI18n; + + @Before + public void before() { + jRubyI18n = mock(JRubyI18n.class); + defaultJRubyRules = new DefaultJRubyRules(jRubyI18n); + } + + @Test + public void should_get_localize_name_of_a_rule() { + String ruleName = "Tabulation characters should not be used"; + String locale = "en"; + RuleKey ruleKey = RuleKey.of("squid", "AvoidCycle"); + when(jRubyI18n.getRuleName(locale, "squid", "AvoidCycle")).thenReturn(ruleName); + assertThat(defaultJRubyRules.ruleName(locale, ruleKey)).isEqualTo(ruleName); + } + + @Test + public void should_get_english_translation_if_not_found_for_given_locale() { + String englishRuleName = "Tabulation characters should not be used"; + RuleKey ruleKey = RuleKey.of("squid", "AvoidCycle"); + when(jRubyI18n.getRuleName("fr", "squid", "AvoidCycle")).thenReturn(null); + when(jRubyI18n.getRuleName("en", "squid", "AvoidCycle")).thenReturn(englishRuleName); + assertThat(defaultJRubyRules.ruleName("fr", ruleKey)).isEqualTo(englishRuleName); + } + +} -- 2.39.5