diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2011-05-19 16:14:21 +0200 |
---|---|---|
committer | simonbrandhof <simon.brandhof@gmail.com> | 2011-05-19 16:14:21 +0200 |
commit | ff3117718a1642e99d97416b88b2db702d1b96f8 (patch) | |
tree | aa3afbd405482ee72453b9dca17266b5465d8e66 | |
parent | 1dfe5d781675f0a7dfe19dce93757a47262abbae (diff) | |
download | sonarqube-ff3117718a1642e99d97416b88b2db702d1b96f8.tar.gz sonarqube-ff3117718a1642e99d97416b88b2db702d1b96f8.zip |
Fix merge of release 2.8
24 files changed, 338 insertions, 147 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CloseReviewsDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CloseReviewsDecorator.java index b46108b8840..0b32e50b288 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CloseReviewsDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CloseReviewsDecorator.java @@ -19,6 +19,8 @@ */ package org.sonar.plugins.core.sensors; +import javax.persistence.Query; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.Decorator; @@ -29,10 +31,9 @@ import org.sonar.api.database.DatabaseSession; import org.sonar.api.database.model.Snapshot; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; +import org.sonar.api.resources.ResourceUtils; import org.sonar.batch.index.ResourcePersister; -import javax.persistence.Query; - /** * Decorator that currently only closes a review when its corresponding violation has been fixed. */ @@ -50,7 +51,7 @@ public class CloseReviewsDecorator implements Decorator { } public boolean shouldExecuteOnProject(Project project) { - return true; + return project.isLatestAnalysis(); } public void decorate(Resource resource, DecoratorContext context) { @@ -58,16 +59,32 @@ public class CloseReviewsDecorator implements Decorator { if (currentSnapshot != null) { int resourceId = currentSnapshot.getResourceId(); int snapshotId = currentSnapshot.getId(); - Query query = databaseSession.createNativeQuery(generateSqlRequest(resourceId, snapshotId)); + Query query = databaseSession.createNativeQuery(generateUpdateOnResourceSqlRequest(resourceId, snapshotId)); int rowUpdated = query.executeUpdate(); LOG.debug("- {} reviews set to 'closed' on resource #{}", rowUpdated, resourceId); + + if (ResourceUtils.isRootProject(resource)) { + query = databaseSession.createNativeQuery(generateUpdateOnProjectSqlRequest(resourceId, currentSnapshot.getId())); + query.setParameter(1, Boolean.TRUE); + rowUpdated = query.executeUpdate(); + LOG.debug("- {} reviews set to 'closed' on project #{}", rowUpdated, resourceId); + } + databaseSession.commit(); } } - String generateSqlRequest(int resourceId, int snapshotId) { - return "UPDATE reviews SET status='CLOSED' WHERE resource_id = " + resourceId + " AND rule_failure_permanent_id NOT IN " - + "(SELECT permanent_id FROM rule_failures WHERE snapshot_id = " + snapshotId + " AND permanent_id IS NOT NULL)"; + protected String generateUpdateOnResourceSqlRequest(int resourceId, int snapshotId) { + return "UPDATE reviews SET status='CLOSED', updated_at=CURRENT_TIMESTAMP WHERE resource_id = " + resourceId + + " AND rule_failure_permanent_id NOT IN " + "(SELECT permanent_id FROM rule_failures WHERE snapshot_id = " + snapshotId + + " AND permanent_id IS NOT NULL)"; + } + + protected String generateUpdateOnProjectSqlRequest(int projectId, int projectSnapshotId) { + return "UPDATE reviews SET status='CLOSED', updated_at=CURRENT_TIMESTAMP WHERE status='OPEN' AND project_id=" + projectId + + " AND resource_id IN ( SELECT prev.project_id FROM snapshots prev WHERE prev.root_project_id=" + projectId + + " AND prev.islast=? AND NOT EXISTS ( SELECT cur.id FROM snapshots cur WHERE cur.root_snapshot_id=" + projectSnapshotId + + " AND cur.created_at > prev.created_at AND cur.root_project_id=" + projectId + " AND cur.project_id=prev.project_id ) )"; } } diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CloseReviewsDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CloseReviewsDecoratorTest.java index 493352c1147..2ea8a7c1de4 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CloseReviewsDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CloseReviewsDecoratorTest.java @@ -21,25 +21,47 @@ package org.sonar.plugins.core.sensors; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.sql.Statement; +import junit.framework.ComparisonFailure; + import org.junit.Test; +import org.sonar.api.resources.Project; import org.sonar.test.persistence.DatabaseTestCase; public class CloseReviewsDecoratorTest extends DatabaseTestCase { @Test + public void testShouldExecuteOnProject() throws Exception { + Project project = mock(Project.class); + when(project.isLatestAnalysis()).thenReturn(true); + CloseReviewsDecorator reviewsDecorator = new CloseReviewsDecorator(null, null); + assertTrue(reviewsDecorator.shouldExecuteOnProject(project)); + } + + @Test public void shouldCloseReviewWithoutCorrespondingViolation() throws Exception { setupData("fixture"); CloseReviewsDecorator reviewsDecorator = new CloseReviewsDecorator(null, null); - String sqlRequest = reviewsDecorator.generateSqlRequest(666, 222); - + String sqlRequest = reviewsDecorator.generateUpdateOnResourceSqlRequest(666, 222); + Statement stmt = getConnection().createStatement(); int count = stmt.executeUpdate(sqlRequest); assertThat(count, is(1)); - assertTables("shouldCloseReviewWithoutCorrespondingViolation", "reviews"); + assertTables("shouldCloseReviewWithoutCorrespondingViolation", new String[] { "reviews" }, new String[] { "updated_at" }); + + try { + assertTables("shouldCloseReviewWithoutCorrespondingViolation", new String[] { "reviews" }); + fail("'updated_at' columns are identical whereas they should be different."); + } catch (ComparisonFailure e) { + // "updated_at" column must be different, so the comparison should raise this exception + } } } diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/CloseReviewsDecoratorTest/shouldCloseReviewWithoutCorrespondingViolation-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/CloseReviewsDecoratorTest/shouldCloseReviewWithoutCorrespondingViolation-result.xml index d46429ddf3d..db7bf76f0e0 100644 --- a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/CloseReviewsDecoratorTest/shouldCloseReviewWithoutCorrespondingViolation-result.xml +++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/CloseReviewsDecoratorTest/shouldCloseReviewWithoutCorrespondingViolation-result.xml @@ -5,18 +5,18 @@ status="OPEN" rule_failure_permanent_id="1" resource_id="555" - created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/> + created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]" project_id="[null]"/> <reviews id="2" status="CLOSED" rule_failure_permanent_id="2" resource_id="666" - created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/> + created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]" project_id="[null]"/> <reviews id="3" status="OPEN" rule_failure_permanent_id="3" resource_id="666" - created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/> + created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]" project_id="[null]"/> </dataset>
\ No newline at end of file diff --git a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java index f38a6c39b20..a02d8d0a308 100644 --- a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java +++ b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java @@ -19,30 +19,41 @@ */ package org.sonar.plugins.dbcleaner; +import java.util.Arrays; +import java.util.List; + import org.sonar.api.Properties; import org.sonar.api.Property; import org.sonar.api.SonarPlugin; import org.sonar.plugins.dbcleaner.api.DbCleanerConstants; import org.sonar.plugins.dbcleaner.period.DefaultPeriodCleaner; import org.sonar.plugins.dbcleaner.period.PeriodPurge; -import org.sonar.plugins.dbcleaner.purges.*; +import org.sonar.plugins.dbcleaner.purges.PurgeDeletedResources; +import org.sonar.plugins.dbcleaner.purges.PurgeDependencies; +import org.sonar.plugins.dbcleaner.purges.PurgeDeprecatedLast; +import org.sonar.plugins.dbcleaner.purges.PurgeDisabledResources; +import org.sonar.plugins.dbcleaner.purges.PurgeEntities; +import org.sonar.plugins.dbcleaner.purges.PurgeEventOrphans; +import org.sonar.plugins.dbcleaner.purges.PurgeOrphanResources; +import org.sonar.plugins.dbcleaner.purges.PurgeOrphanReviews; +import org.sonar.plugins.dbcleaner.purges.PurgePropertyOrphans; +import org.sonar.plugins.dbcleaner.purges.PurgeResourceRoles; +import org.sonar.plugins.dbcleaner.purges.PurgeRuleMeasures; +import org.sonar.plugins.dbcleaner.purges.PurgeUnprocessed; import org.sonar.plugins.dbcleaner.runner.PurgeRunner; -import java.util.Arrays; -import java.util.List; - @Properties({ - @Property(key = DbCleanerConstants.MONTHS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_WEEK, - defaultValue = DbCleanerConstants.ONE_MONTH, name = "Number of months before starting to keep only one snapshot by week", + @Property(key = DbCleanerConstants.MONTHS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_WEEK, defaultValue = DbCleanerConstants.ONE_MONTH, + name = "Number of months before starting to keep only one snapshot by week", description = "After this number of months, if there are several snapshots during the same week, " + "the DbCleaner keeps the first one and fully delete the other ones.", global = true, project = true), - @Property(key = DbCleanerConstants.MONTHS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_MONTH, - defaultValue = DbCleanerConstants.ONE_YEAR, name = "Number of months before starting to keep only one snapshot by month", + @Property(key = DbCleanerConstants.MONTHS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_MONTH, defaultValue = DbCleanerConstants.ONE_YEAR, + name = "Number of months before starting to keep only one snapshot by month", description = "After this number of months, if there are several snapshots during the same month, " + "the DbCleaner keeps the first one and fully delete the other ones.", global = true, project = true), @Property(key = DbCleanerConstants.MONTHS_BEFORE_DELETING_ALL_SNAPSHOTS, defaultValue = DbCleanerConstants.FIVE_YEARS, name = "Number of months before starting to delete all remaining snapshots", - description = "After this number of months, all snapshots are fully deleted.", global = true, project = true)}) + description = "After this number of months, all snapshots are fully deleted.", global = true, project = true) }) public final class DbCleanerPlugin extends SonarPlugin { public List getExtensions() { @@ -51,9 +62,9 @@ public final class DbCleanerPlugin extends SonarPlugin { DefaultPeriodCleaner.class, // purges - PurgeOrphanResources.class, PurgeEntities.class, PurgeRuleMeasures.class, PurgeUnprocessed.class, - PurgeDeletedResources.class, PurgeDeprecatedLast.class, PurgeDisabledResources.class, - PurgeResourceRoles.class, PurgeEventOrphans.class, PurgePropertyOrphans.class, PeriodPurge.class, PurgeDependencies.class, + PurgeOrphanResources.class, PurgeEntities.class, PurgeRuleMeasures.class, PurgeUnprocessed.class, PurgeDeletedResources.class, + PurgeDeprecatedLast.class, PurgeDisabledResources.class, PurgeResourceRoles.class, PurgeEventOrphans.class, + PurgePropertyOrphans.class, PeriodPurge.class, PurgeDependencies.class, PurgeOrphanReviews.class, // post-job PurgeRunner.class); diff --git a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviews.java b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviews.java new file mode 100644 index 00000000000..8e52416b767 --- /dev/null +++ b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviews.java @@ -0,0 +1,66 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 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.plugins.dbcleaner.purges; + +import javax.persistence.Query; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.database.DatabaseSession; +import org.sonar.plugins.dbcleaner.api.Purge; +import org.sonar.plugins.dbcleaner.api.PurgeContext; + +/** + * Purge Review that are attached to projects that have been deleted. + * + * @since 2.8 + */ +public final class PurgeOrphanReviews extends Purge { + + private static final Logger LOG = LoggerFactory.getLogger(PurgeOrphanReviews.class); + + public PurgeOrphanReviews(DatabaseSession session) { + super(session); + } + + public void purge(PurgeContext context) { + DatabaseSession session = getSession(); + + // delete reviews + Query query = session.createNativeQuery(getDeleteReviewsSqlRequest()); + int rowDeleted = query.executeUpdate(); + LOG.debug("- {} reviews deleted.", rowDeleted); + + // and delete review comments + query = session.createNativeQuery(getDeleteReviewCommentsSqlRequest()); + rowDeleted = query.executeUpdate(); + LOG.debug("- {} review comments deleted.", rowDeleted); + + session.commit(); + } + + protected String getDeleteReviewsSqlRequest() { + return "DELETE FROM reviews WHERE project_id not in (SELECT id FROM projects WHERE scope = 'PRJ' and qualifier = 'TRK')"; + } + + protected String getDeleteReviewCommentsSqlRequest() { + return "DELETE FROM review_comments WHERE review_id not in (SELECT id FROM reviews)"; + } +} diff --git a/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest.java b/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest.java new file mode 100644 index 00000000000..de1632ac757 --- /dev/null +++ b/plugins/sonar-dbcleaner-plugin/src/test/java/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest.java @@ -0,0 +1,46 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 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.plugins.dbcleaner.purges; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.sql.Statement; + +import org.junit.Test; +import org.sonar.test.persistence.DatabaseTestCase; + +public class PurgeOrphanReviewsTest extends DatabaseTestCase { + + @Test + public void shouldCloseReviewWithoutCorrespondingViolation() throws Exception { + setupData("purgeOrphanReviews"); + + Statement stmt = getConnection().createStatement(); + int count = stmt.executeUpdate(new PurgeOrphanReviews(null).getDeleteReviewsSqlRequest()); + assertThat(count, is(1)); + + count = stmt.executeUpdate(new PurgeOrphanReviews(null).getDeleteReviewCommentsSqlRequest()); + assertThat(count, is(1)); + + assertTables("purgeOrphanReviews", "reviews"); + } + +} diff --git a/plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest/purgeOrphanReviews-result.xml b/plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest/purgeOrphanReviews-result.xml new file mode 100644 index 00000000000..7680bdd39c0 --- /dev/null +++ b/plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest/purgeOrphanReviews-result.xml @@ -0,0 +1,43 @@ +<dataset> + <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="other-project" name="other-project" + root_id="[null]" profile_id="[null]" + description="[null]" + enabled="true" language="java" copy_resource_id="[null]"/> + + <!-- project1 has been removed from UI --> + <!--<projects long_name="[null]" id="2" scope="PRJ" qualifier="TRK" kee="project1" name="project1"--> + <!--root_id="[null]"--> + <!--description="[null]"--> + <!--enabled="true" language="java" copy_resource_id="[null]"/>--> + + <reviews + id="1" + status="OPEN" + rule_failure_permanent_id="1" + resource_id="555" + project_id="1" + created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/> + + <!-- Following must have been deleted + <reviews + id="2" + status="CLOSED" + rule_failure_permanent_id="2" + resource_id="666" + project_id="2" + created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/> + --> + + <review_comments + id="1" + review_id="1" + created_at="[null]" updated_at="[null]" user_id="[null]" review_text=""/> + + <!-- Following must have been deleted + <review_comments + id="2" + review_id="2" + created_at="[null]" updated_at="[null]" user_id="[null]" review_text=""/> + --> + +</dataset>
\ No newline at end of file diff --git a/plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest/purgeOrphanReviews.xml b/plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest/purgeOrphanReviews.xml new file mode 100644 index 00000000000..01da1669b40 --- /dev/null +++ b/plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/purges/PurgeOrphanReviewsTest/purgeOrphanReviews.xml @@ -0,0 +1,39 @@ +<dataset> + <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="other-project" name="other-project" + root_id="[null]" profile_id="[null]" + description="[null]" + enabled="true" language="java" copy_resource_id="[null]"/> + + <!-- project1 has been removed from UI --> + <!--<projects long_name="[null]" id="2" scope="PRJ" qualifier="TRK" kee="project1" name="project1"--> + <!--root_id="[null]"--> + <!--description="[null]"--> + <!--enabled="true" language="java" copy_resource_id="[null]"/>--> + + <reviews + id="1" + status="OPEN" + rule_failure_permanent_id="1" + resource_id="555" + project_id="1" + created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/> + <reviews + id="2" + status="CLOSED" + rule_failure_permanent_id="2" + resource_id="666" + project_id="2" + created_at="[null]" updated_at="[null]" user_id="[null]" assignee_id="[null]" title="[null]" review_type="[null]" severity="[null]" resource_line="[null]"/> + + <review_comments + id="1" + review_id="1" + created_at="[null]" updated_at="[null]" user_id="[null]" review_text=""/> + + <review_comments + id="2" + review_id="2" + created_at="[null]" updated_at="[null]" user_id="[null]" review_text=""/> + + +</dataset>
\ No newline at end of file diff --git a/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/SamplePlugin.java b/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/SamplePlugin.java index f530e6b38b4..7622e032bd0 100644 --- a/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/SamplePlugin.java +++ b/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/SamplePlugin.java @@ -2,8 +2,6 @@ package com.mycompany.sonar.standard; import com.mycompany.sonar.standard.batch.RandomDecorator; import com.mycompany.sonar.standard.batch.SampleSensor; -import com.mycompany.sonar.standard.rules.SampleQualityProfile; -import com.mycompany.sonar.standard.rules.SampleRuleRepository; import com.mycompany.sonar.standard.ui.SampleFooter; import com.mycompany.sonar.standard.ui.SampleRubyWidget; import org.sonar.api.SonarPlugin; @@ -20,7 +18,7 @@ public final class SamplePlugin extends SonarPlugin { public List getExtensions() { return Arrays.asList( // Definitions - SampleRuleRepository.class, SampleMetrics.class, SampleQualityProfile.class, + SampleMetrics.class, // Batch SampleSensor.class, RandomDecorator.class, diff --git a/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/rules/SampleQualityProfile.java b/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/rules/SampleQualityProfile.java deleted file mode 100644 index ac6e981085b..00000000000 --- a/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/rules/SampleQualityProfile.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.mycompany.sonar.standard.rules; - -import org.sonar.api.profiles.ProfileDefinition; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.Java; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RulePriority; -import org.sonar.api.utils.ValidationMessages; - -public class SampleQualityProfile extends ProfileDefinition { - - private SampleRuleRepository ruleRepository; - - // The component ruleRepository is injected at runtime - public SampleQualityProfile(SampleRuleRepository ruleRepository) { - this.ruleRepository = ruleRepository; - } - - @Override - public RulesProfile createProfile(ValidationMessages validation) { - RulesProfile profile = RulesProfile.create("Sample profile", Java.KEY); - profile.activateRule(ruleRepository.getRule1(), RulePriority.MAJOR); - profile.activateRule(ruleRepository.getRule2(), null); - profile.activateRule(Rule.create("checkstyle", "com.puppycrawl.tools.checkstyle.checks.coding.CovariantEqualsCheck", null), null); - return profile; - } -} diff --git a/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/rules/SampleRuleRepository.java b/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/rules/SampleRuleRepository.java deleted file mode 100644 index 4bd909a3b11..00000000000 --- a/samples/standard-plugin/src/main/java/com/mycompany/sonar/standard/rules/SampleRuleRepository.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.mycompany.sonar.standard.rules; - -import org.sonar.api.resources.Java; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RulePriority; -import org.sonar.api.rules.RuleRepository; - -import java.util.Arrays; -import java.util.List; - -/** - * This class declares rules. It is not the engine used to execute rules during project analysis. - */ -public class SampleRuleRepository extends RuleRepository { - - public static final String REPOSITORY_KEY = "sample"; - - public SampleRuleRepository() { - super(REPOSITORY_KEY, Java.KEY); - setName("Sample"); - } - - @Override - public List<Rule> createRules() { - // This method is called when server is started. It's used to register rules into database. - // Definition of rules can be loaded from any sources, like XML files. - return Arrays.asList( - getRule1(), - getRule2()); - } - - public Rule getRule1() { - return Rule.create(REPOSITORY_KEY, "fake1", "Fake one").setSeverity(RulePriority.CRITICAL); - } - - public Rule getRule2() { - return Rule.create(REPOSITORY_KEY, "fake2", "Fake two").setDescription("Description of fake two"); - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java b/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java index 7a6ea61b454..2216a49c1b5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java +++ b/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java @@ -104,7 +104,7 @@ public class ProjectBatch { @Override protected void configure() { - ProjectDefinition projectDefinition = getComponent(ProjectTree.class).getProjectDefinition(project.getKey()); + ProjectDefinition projectDefinition = getComponent(ProjectTree.class).getProjectDefinition(project); addComponent(projectDefinition); for (Object component : projectDefinition.getContainerExtensions()) { addComponent(component); diff --git a/sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java b/sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java index 54b97fb2b6f..1ea52bc5738 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java +++ b/sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java @@ -21,10 +21,10 @@ package org.sonar.batch; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.apache.maven.project.MavenProject; import org.slf4j.LoggerFactory; -import org.sonar.api.CoreProperties; import org.sonar.api.database.DatabaseSession; import org.sonar.api.resources.Project; import org.sonar.batch.bootstrapper.ProjectDefinition; @@ -35,9 +35,10 @@ import java.util.*; public class ProjectTree { - private List<Project> projects; private ProjectBuilder projectBuilder; + private List<Project> projects; private List<ProjectDefinition> definitions; + private Map<ProjectDefinition, Project> projectsMap; public ProjectTree(Reactor sonarReactor, DatabaseSession databaseSession) { this.projectBuilder = new ProjectBuilder(databaseSession); @@ -75,24 +76,24 @@ public class ProjectTree { public void start() throws IOException { projects = Lists.newArrayList(); - Map<ProjectDefinition, Project> map = Maps.newHashMap(); + projectsMap = Maps.newHashMap(); for (ProjectDefinition def : definitions) { Project project = projectBuilder.create(def); - map.put(def, project); + projectsMap.put(def, project); projects.add(project); } - for (Map.Entry<ProjectDefinition, Project> entry : map.entrySet()) { + for (Map.Entry<ProjectDefinition, Project> entry : projectsMap.entrySet()) { ProjectDefinition def = entry.getKey(); Project project = entry.getValue(); for (ProjectDefinition module : def.getModules()) { - map.get(module).setParent(project); + projectsMap.get(module).setParent(project); } } // Configure - for (Map.Entry<ProjectDefinition, Project> entry : map.entrySet()) { + for (Map.Entry<ProjectDefinition, Project> entry : projectsMap.entrySet()) { projectBuilder.configure(entry.getValue(), entry.getKey()); } @@ -181,26 +182,12 @@ public class ProjectTree { throw new IllegalStateException("Can not find the root project from the list of Maven modules"); } - private String getProjectKey(ProjectDefinition def) { - String key = def.getProperties().getProperty(CoreProperties.PROJECT_KEY_PROPERTY); - String branch = def.getProperties().getProperty(CoreProperties.PROJECT_BRANCH_PROPERTY); - if (StringUtils.isNotBlank(branch)) { - return key + ":" + branch; - } else { - return key; - } - } - - public ProjectDefinition getProjectDefinition(String key) { - for (ProjectDefinition def : definitions) { - if (StringUtils.equals(key, getProjectKey(def))) { - return def; + public ProjectDefinition getProjectDefinition(Project project) { + for (Map.Entry<ProjectDefinition, Project> entry : projectsMap.entrySet()) { + if (ObjectUtils.equals(entry.getValue(), project)) { + return entry.getKey(); } } - return null; - } - - public List<Object> getProjectExtensions(String key) { - return getProjectDefinition(key).getContainerExtensions(); + throw new IllegalStateException("Can not find ProjectDefinition for " + project); } } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/reviews_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/reviews_controller.rb index 62b3f3b8759..7a69b96676c 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/reviews_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/reviews_controller.rb @@ -53,7 +53,7 @@ class ReviewsController < ApplicationController def show @review = Review.find(params[:id], :include => ['project']) if has_role?(:user, @review.project) - render :partial => 'reviews/view' + render :partial => 'reviews/show' else render :text => "access denied" end @@ -76,7 +76,7 @@ class ReviewsController < ApplicationController @review.assignee = User.find params[:assignee_id] @review.save - render :partial => 'reviews/view' + render :partial => 'reviews/show' end # GET @@ -106,7 +106,7 @@ class ReviewsController < ApplicationController @review.comments.create!(:user => current_user, :text => params[:text]) end - render :partial => "reviews/view" + render :partial => "reviews/show" end # GET @@ -134,7 +134,7 @@ class ReviewsController < ApplicationController @review.comments.create(:review_text => params[:comment], :user_id => current_user.id) end - render :partial => "reviews/view" + render :partial => "reviews/show" end # POST @@ -149,7 +149,7 @@ class ReviewsController < ApplicationController comment=@review.comments.find(params[:comment_id].to_i) comment.delete if comment end - render :partial => "reviews/view" + render :partial => "reviews/show" end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb index e7f95603788..854c5f746db 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb @@ -32,6 +32,8 @@ class SettingsController < ApplicationController project=Project.by_key(params[:resource_id]) return access_denied unless is_admin?(project) resource_id=project.id + else + return access_denied unless is_admin? end plugins = java_facade.getPluginsMetadata() diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/project.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/project.rb index 8489caea88b..9658c9dfcb3 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/project.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/project.rb @@ -43,6 +43,13 @@ class Project < ActiveRecord::Base root||self end + def root_project + @root_project ||= + begin + parent_module(self) + end + end + def last_snapshot @last_snapshot ||= begin @@ -153,4 +160,9 @@ class Project < ActiveRecord::Base end chart_measures end + + def parent_module(current_module) + current_module.root ? parent_module(current_module.root) : current_module + end + end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/review.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/review.rb index 46e97ba9c95..1f91e4abe79 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/review.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/review.rb @@ -205,7 +205,7 @@ class Review < ActiveRecord::Base def assign_project if self.project.nil? && self.resource - self.project=self.resource.project + self.project=self.resource.root_project end end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_violation.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_violation.html.erb index 25989a7b1e0..3282a0fedee 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_violation.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/resource/_violation.html.erb @@ -2,7 +2,7 @@ <div class="violation"> <div class="vtitle"> <% if violation.review %> - <div class="review_permalink"><span class="review_permalink"><%= link_to "Review #"+violation.review.id.to_s, :controller => "reviews", :action => "view", :id => violation.review.id -%></span></div> + <div style="float: right"><span class="review_permalink"><%= link_to violation.review.id.to_s, :controller => "reviews", :action => "view", :id => violation.review.id -%></span></div> <% end %> <%= image_tag("priority/" + violation.failure_level.to_s + '.png') -%> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/_review.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/_review.html.erb index 5e8fcd7299e..79b0a2c864d 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/_review.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/_review.html.erb @@ -1,6 +1,7 @@ <div id="rev_<%= review.id -%>"> <div class="reportTitle"> - <h2>Review #<%= h(review.id.to_s) -%> - <%= h(review.title) -%></h2> + <div style="float: right"><span class="violation_date"><%= review.id.to_s -%></span></div> + <h2><%= h(review.title) -%></h2> <% if current_user && review.status != "CLOSED" %> <span class="actions" id="rActions"> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/_show.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/_show.html.erb new file mode 100644 index 00000000000..b9faf2e6d30 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/_show.html.erb @@ -0,0 +1,11 @@ +<div id="backReviewDiv" class="marginbottom10"> + <a href="#" onclick="backReviews()">« Back to reviews</a> +</div> +<script> + if ($('reviews-search')==null) { + // This happens when this page results from a call made from the review permalink page + $('backReviewDiv').hide(); + } +</script> + +<%= render :partial => 'reviews/review', :locals => {:review => @review} -%>
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/index.html.erb index 9946a4564f8..7bd4895f05a 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/index.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/reviews/index.html.erb @@ -107,11 +107,11 @@ function reviewIdFieldModified(field) { <tr class="<%= cycle('even', 'odd') -%>"> <td><img src="<%= ApplicationController.root_context -%>/images/status/<%= review.status -%>.png" title="<%= review.status.capitalize -%>"/></td> <td> - <%= link_to h(review.id), :controller => "reviews", :action => "view", :id => review.id -%> + <%= link_to_remote( h(review.id), :update => 'review', :url => {:action => 'show', :id => review.id}, :loading => 'onReviewLoading()', :complete => "onReviewLoaded()") -%> </td> <td><img src="<%= ApplicationController.root_context -%>/images/priority/<%= review.severity -%>.png" title="<%= review.severity.capitalize -%>"/></td> <td> - <%= link_to h(review.title), :controller => "reviews", :action => "view", :id => review.id -%> + <%= link_to_remote(h(review.title), :update => 'review', :url => {:action => 'show', :id => review.id}, :loading => 'onReviewLoading()', :complete => "onReviewLoaded()") -%> </td> <td><%= review.project.name -%> <br/><span class="note"><%= review.resource.long_name -%></span></td> @@ -138,6 +138,8 @@ function reviewIdFieldModified(field) { </div> +<div id="review-loading" style="display: none"><%= image_tag 'loading.gif' -%></div> +<div id="review" style="display: none"></div> <script> $('review_id').focus(); </script>
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/stylesheets/style.css b/sonar-server/src/main/webapp/stylesheets/style.css index fd11c07a403..a6a1af75d40 100644 --- a/sonar-server/src/main/webapp/stylesheets/style.css +++ b/sonar-server/src/main/webapp/stylesheets/style.css @@ -686,7 +686,7 @@ ul.operations li img { div.vtitle{ background-color:#E4ECF3; margin:0; - padding:0 0 0 10px; + padding:0 10px; line-height: 2.2em; text-shadow: 0 1px 0 #FFF; color:#777 @@ -706,24 +706,9 @@ span.violation_date { color:#777; font-size:90%; } -div.review_permalink { - float: right; - background-color: #F4F4F4; - border-color: #CDCDCD; - border-style: none none none solid; - border-width: 1px; - color: #333333; - font-size: 12px; - font-weight: bold; - margin: 0; - padding: 0 10px; - text-shadow: 1px 1px 0 #FFFFFF; -} span.review_permalink a { color:#777; font-size:90%; - padding: 0 0 0 20px; - background: url('../images/zoom.png') no-repeat left; } span.rulename a:hover { text-decoration: underline; diff --git a/sonar-testing-harness/src/main/java/org/sonar/test/persistence/DatabaseTestCase.java b/sonar-testing-harness/src/main/java/org/sonar/test/persistence/DatabaseTestCase.java index ba9e47b6e76..7c8d4543d6c 100644 --- a/sonar-testing-harness/src/main/java/org/sonar/test/persistence/DatabaseTestCase.java +++ b/sonar-testing-harness/src/main/java/org/sonar/test/persistence/DatabaseTestCase.java @@ -196,6 +196,20 @@ public abstract class DatabaseTestCase { } } + protected final void assertTables(String testName, String[] tables, String[] ignoreCols) { + try { + IDataSet dataSet = getCurrentDataSet(); + IDataSet expectedDataSet = getExpectedData(testName); + for (String table : tables) { + Assertion.assertEqualsIgnoreCols(expectedDataSet.getTable(table), dataSet.getTable(table), ignoreCols); + } + } catch (DataSetException e) { + throw translateException("Error while checking results", e); + } catch (DatabaseUnitException e) { + fail(e.getMessage()); + } + } + protected final void assertEmptyTables(String... emptyTables) { for (String table : emptyTables) { try { diff --git a/sonar-testing-harness/src/main/resources/org/sonar/test/persistence/sonar-test.ddl b/sonar-testing-harness/src/main/resources/org/sonar/test/persistence/sonar-test.ddl index c5125179cbf..54f49a0953b 100644 --- a/sonar-testing-harness/src/main/resources/org/sonar/test/persistence/sonar-test.ddl +++ b/sonar-testing-harness/src/main/resources/org/sonar/test/persistence/sonar-test.ddl @@ -492,6 +492,7 @@ CREATE TABLE REVIEWS ( STATUS VARCHAR(10), SEVERITY VARCHAR(10), RULE_FAILURE_PERMANENT_ID INTEGER, + PROJECT_ID INTEGER, RESOURCE_ID INTEGER, RESOURCE_LINE INTEGER, primary key (id) |