From: Jean-Baptiste Lievremont Date: Thu, 12 Mar 2015 16:56:46 +0000 (+0100) Subject: SONAR-6236 Migrate existing dashboards to use the new issue widgets X-Git-Tag: 5.2-RC1~2544 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=94ab0b3fb528418dbe747d0ab7f59b8dbb7bf177;p=sonarqube.git SONAR-6236 Migrate existing dashboards to use the new issue widgets --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/dashboard/db/WidgetDao.java b/server/sonar-server/src/main/java/org/sonar/server/dashboard/db/WidgetDao.java index ca7b10d9e5f..d6d12e4c0f5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/dashboard/db/WidgetDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/dashboard/db/WidgetDao.java @@ -38,7 +38,17 @@ public class WidgetDao extends BaseDao { return mapper(session).selectById(widgetId); } + @Override + protected WidgetDto doUpdate(DbSession session, WidgetDto item) { + mapper(session).update(item); + return item; + } + public Collection findByDashboard(DbSession session, long dashboardKey) { return mapper(session).selectByDashboard(dashboardKey); } + + public Collection findAll(DbSession session) { + return mapper(session).selectAll(); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/dashboard/db/WidgetPropertyDao.java b/server/sonar-server/src/main/java/org/sonar/server/dashboard/db/WidgetPropertyDao.java index ae85a6fd386..9aeead48d6a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/dashboard/db/WidgetPropertyDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/dashboard/db/WidgetPropertyDao.java @@ -26,6 +26,7 @@ import org.sonar.core.persistence.DbSession; import org.sonar.server.db.BaseDao; import java.util.Collection; +import java.util.List; public class WidgetPropertyDao extends BaseDao { @@ -33,6 +34,12 @@ public class WidgetPropertyDao extends BaseDao widgetIdsWithPropertiesToDelete) { + mapper(session).deleteByWidgetIds(widgetIdsWithPropertiesToDelete); + } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java b/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java index a0056e42064..fe5106cc7d6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java @@ -22,6 +22,7 @@ package org.sonar.server.db; import org.sonar.api.ServerComponent; import org.sonar.core.issue.db.ActionPlanDao; import org.sonar.core.issue.db.IssueChangeDao; +import org.sonar.core.issue.db.IssueFilterDao; import org.sonar.core.persistence.DaoComponent; import org.sonar.core.persistence.Database; import org.sonar.core.persistence.DbSession; @@ -82,6 +83,7 @@ public class DbClient implements ServerComponent { private final UserDao userDao; private final GroupDao groupDao; private final IssueDao issueDao; + private final IssueFilterDao issueFilterDao; private final IssueChangeDao issueChangeDao; private final ActionPlanDao actionPlanDao; private final AnalysisReportDao analysisReportDao; @@ -118,6 +120,7 @@ public class DbClient implements ServerComponent { userDao = getDao(map, UserDao.class); groupDao = getDao(map, GroupDao.class); issueDao = getDao(map, IssueDao.class); + issueFilterDao = getDao(map, IssueFilterDao.class); issueChangeDao = getDao(map, IssueChangeDao.class); actionPlanDao = getDao(map, ActionPlanDao.class); analysisReportDao = getDao(map, AnalysisReportDao.class); @@ -151,6 +154,10 @@ public class DbClient implements ServerComponent { return issueDao; } + public IssueFilterDao issueFilterDao() { + return issueFilterDao; + } + public IssueChangeDao issueChangeDao() { return issueChangeDao; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 6542ea824c6..1d9da7f47f4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -709,6 +709,7 @@ class ServerComponents { startupContainer.addSingleton(RegisterServletFilters.class); startupContainer.addSingleton(CopyRequirementsFromCharacteristicsToRules.class); startupContainer.addSingleton(ReportQueueCleaner.class); + startupContainer.addSingleton(RenameIssueWidgets.class); DoPrivileged.execute(new DoPrivileged.Task() { @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/startup/RegisterDashboards.java b/server/sonar-server/src/main/java/org/sonar/server/startup/RegisterDashboards.java index baea633bb32..084820d0a64 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/startup/RegisterDashboards.java +++ b/server/sonar-server/src/main/java/org/sonar/server/startup/RegisterDashboards.java @@ -27,14 +27,10 @@ import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.api.web.Dashboard; import org.sonar.api.web.DashboardTemplate; -import org.sonar.core.dashboard.ActiveDashboardDao; -import org.sonar.core.dashboard.ActiveDashboardDto; -import org.sonar.core.dashboard.DashboardDao; -import org.sonar.core.dashboard.DashboardDto; -import org.sonar.core.dashboard.WidgetDto; -import org.sonar.core.dashboard.WidgetPropertyDto; +import org.sonar.core.dashboard.*; import org.sonar.core.template.LoadedTemplateDao; import org.sonar.core.template.LoadedTemplateDto; +import org.sonar.server.issue.filter.RegisterIssueFilters; import java.io.Serializable; import java.util.Date; @@ -55,7 +51,7 @@ public class RegisterDashboards implements Startable { private final LoadedTemplateDao loadedTemplateDao; public RegisterDashboards(DashboardTemplate[] dashboardTemplatesArray, DashboardDao dashboardDao, - ActiveDashboardDao activeDashboardDao, LoadedTemplateDao loadedTemplateDao) { + ActiveDashboardDao activeDashboardDao, LoadedTemplateDao loadedTemplateDao, RegisterIssueFilters startupDependency) { this.dashboardTemplates = Lists.newArrayList(dashboardTemplatesArray); this.dashboardDao = dashboardDao; this.activeDashboardDao = activeDashboardDao; @@ -65,8 +61,8 @@ public class RegisterDashboards implements Startable { /** * Used when no plugin is defining some DashboardTemplate */ - public RegisterDashboards(DashboardDao dashboardDao, ActiveDashboardDao activeDashboardDao, LoadedTemplateDao loadedTemplateDao) { - this(new DashboardTemplate[] {}, dashboardDao, activeDashboardDao, loadedTemplateDao); + public RegisterDashboards(DashboardDao dashboardDao, ActiveDashboardDao activeDashboardDao, LoadedTemplateDao loadedTemplateDao, RegisterIssueFilters registerIssueFilters) { + this(new DashboardTemplate[] {}, dashboardDao, activeDashboardDao, loadedTemplateDao, registerIssueFilters); } @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/startup/RenameIssueWidgets.java b/server/sonar-server/src/main/java/org/sonar/server/startup/RenameIssueWidgets.java new file mode 100644 index 00000000000..e0fbf821209 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/startup/RenameIssueWidgets.java @@ -0,0 +1,151 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.startup; + +import com.google.common.collect.Lists; +import org.picocontainer.Startable; +import org.sonar.api.utils.log.Loggers; +import org.sonar.core.dashboard.WidgetDto; +import org.sonar.core.dashboard.WidgetPropertyDto; +import org.sonar.core.issue.db.IssueFilterDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.template.LoadedTemplateDto; +import org.sonar.server.db.DbClient; + +import java.util.List; + +public class RenameIssueWidgets implements Startable { + + private static final String PROJECT_ISSUE_FILTER_WIDGET_KEY = "project_issue_filter"; + private static final String FILTER_PROPERTY = "filter"; + private static final String DISTRIBUTION_AXIS_PROPERTY = "distributionAxis"; + + private final DbClient dbClient; + + public RenameIssueWidgets(DbClient dbClient) { + this.dbClient = dbClient; + } + + @Override + public void start() { + DbSession session = dbClient.openSession(false); + + try { + if (dbClient.loadedTemplateDao().countByTypeAndKey(LoadedTemplateDto.ONE_SHOT_TASK_TYPE, getClass().getSimpleName()) != 0) { + // Already done + return; + } + + IssueFilterDto unresolvedIssues = dbClient.issueFilterDao().selectProvidedFilterByName("Unresolved Issues"); + IssueFilterDto hiddenDebt = dbClient.issueFilterDao().selectProvidedFilterByName("False Positive and Won't Fix Issues"); + IssueFilterDto myUnresolvedIssues = dbClient.issueFilterDao().selectProvidedFilterByName("My Unresolved Issues"); + + if (unresolvedIssues == null || hiddenDebt == null || myUnresolvedIssues == null) { + // One of the filter has been deleted, no need to do anything + return; + } + + Loggers.get(getClass()).info("Replacing issue related widgets with issue filter widgets"); + + List widgetIdsWithPropertiesToDelete = Lists.newArrayList(); + List widgetPropertiesToCreate = Lists.newArrayList(); + + for (WidgetDto widget : dbClient.widgetDao().findAll(session)) { + switch (widget.getWidgetKey()) { + case "false_positive_reviews": + widgetPropertiesToCreate.add( + new WidgetPropertyDto() + .setWidgetId(widget.getId()) + .setPropertyKey(FILTER_PROPERTY) + .setTextValue(hiddenDebt.getId().toString())); + widgetPropertiesToCreate.add( + new WidgetPropertyDto() + .setWidgetId(widget.getId()) + .setPropertyKey(DISTRIBUTION_AXIS_PROPERTY) + .setTextValue("resolutions")); + updateWidget(session, widgetIdsWithPropertiesToDelete, widget); + break; + case "my_reviews": + widgetPropertiesToCreate.add( + new WidgetPropertyDto() + .setWidgetId(widget.getId()) + .setPropertyKey(FILTER_PROPERTY) + .setTextValue(myUnresolvedIssues.getId().toString())); + updateWidget(session, widgetIdsWithPropertiesToDelete, widget); + break; + case "reviews_per_developer": + widgetPropertiesToCreate.add( + new WidgetPropertyDto() + .setWidgetId(widget.getId()) + .setPropertyKey(FILTER_PROPERTY) + .setTextValue(unresolvedIssues.getId().toString())); + widgetPropertiesToCreate.add( + new WidgetPropertyDto() + .setWidgetId(widget.getId()) + .setPropertyKey(DISTRIBUTION_AXIS_PROPERTY) + .setTextValue("assignees")); + updateWidget(session, widgetIdsWithPropertiesToDelete, widget); + break; + case "unresolved_issues_statuses": + widgetPropertiesToCreate.add( + new WidgetPropertyDto() + .setWidgetId(widget.getId()) + .setPropertyKey(FILTER_PROPERTY) + .setTextValue(unresolvedIssues.getId().toString())); + widgetPropertiesToCreate.add( + new WidgetPropertyDto() + .setWidgetId(widget.getId()) + .setPropertyKey(DISTRIBUTION_AXIS_PROPERTY) + .setTextValue("statuses")); + updateWidget(session, widgetIdsWithPropertiesToDelete, widget); + break; + default: + // Nothing to do, move along + break; + } + } + + dbClient.widgetPropertyDao().deleteByWidgetIds(session, widgetIdsWithPropertiesToDelete); + dbClient.widgetPropertyDao().insert(session, widgetPropertiesToCreate); + + dbClient.loadedTemplateDao().insert(new LoadedTemplateDto() + .setType(LoadedTemplateDto.ONE_SHOT_TASK_TYPE) + .setKey(getClass().getSimpleName()), session); + + session.commit(); + } finally { + MyBatis.closeQuietly(session); + } + } + + private void updateWidget(DbSession session, List widgetIdsWithPropertiesToDelete, WidgetDto widget) { + dbClient.widgetDao().update(session, + widget.setWidgetKey(PROJECT_ISSUE_FILTER_WIDGET_KEY) + .setConfigured(true)); + widgetIdsWithPropertiesToDelete.add(widget.getId()); + } + + @Override + public void stop() { + // do nothing + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/startup/RegisterDashboardsTest.java b/server/sonar-server/src/test/java/org/sonar/server/startup/RegisterDashboardsTest.java index 64988ba63bd..5ababc6e721 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/startup/RegisterDashboardsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/startup/RegisterDashboardsTest.java @@ -27,12 +27,7 @@ import org.mockito.ArgumentMatcher; import org.sonar.api.web.Dashboard; import org.sonar.api.web.DashboardLayout; import org.sonar.api.web.DashboardTemplate; -import org.sonar.core.dashboard.ActiveDashboardDao; -import org.sonar.core.dashboard.ActiveDashboardDto; -import org.sonar.core.dashboard.DashboardDao; -import org.sonar.core.dashboard.DashboardDto; -import org.sonar.core.dashboard.WidgetDto; -import org.sonar.core.dashboard.WidgetPropertyDto; +import org.sonar.core.dashboard.*; import org.sonar.core.template.LoadedTemplateDao; import org.sonar.core.template.LoadedTemplateDto; @@ -64,7 +59,7 @@ public class RegisterDashboardsTest { fakeDashboardTemplate = mock(DashboardTemplate.class); task = new RegisterDashboards(new DashboardTemplate[]{fakeDashboardTemplate}, dashboardDao, - activeDashboardDao, loadedTemplateDao); + activeDashboardDao, loadedTemplateDao, null); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/startup/RenameIssueWidgetsTest.java b/server/sonar-server/src/test/java/org/sonar/server/startup/RenameIssueWidgetsTest.java new file mode 100644 index 00000000000..95047b5c59b --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/startup/RenameIssueWidgetsTest.java @@ -0,0 +1,84 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.startup; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.sonar.api.utils.DateUtils; +import org.sonar.api.utils.System2; +import org.sonar.core.issue.db.IssueFilterDao; +import org.sonar.core.persistence.DbTester; +import org.sonar.core.template.LoadedTemplateDao; +import org.sonar.server.dashboard.db.WidgetDao; +import org.sonar.server.dashboard.db.WidgetPropertyDao; +import org.sonar.server.db.DbClient; +import org.sonar.test.DbTests; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@Category(DbTests.class) +public class RenameIssueWidgetsTest { + + @Rule + public DbTester dbTester = new DbTester(); + + @Test + public void should_rename_widgets() throws Exception { + dbTester.prepareDbUnit(this.getClass(), "before.xml"); + + doStart(); + + dbTester.assertDbUnit(this.getClass(), "after.xml", "widgets", "widget_properties", "loaded_templates"); + } + + @Test + public void should_skip_when_filter_removed() throws Exception { + dbTester.prepareDbUnit(this.getClass(), "empty.xml"); + + doStart(); + } + + @Test + public void should_skip_when_already_executed() throws Exception { + dbTester.prepareDbUnit(this.getClass(), "after.xml"); + + doStart(); + } + + private void doStart() { + System2 system2 = mock(System2.class); + when(system2.now()).thenReturn(DateUtils.parseDateTime("2003-03-23T01:23:45+0100").getTime()); + + RenameIssueWidgets task = new RenameIssueWidgets( + new DbClient( + dbTester.database(), + dbTester.myBatis(), + new WidgetDao(system2), + new WidgetPropertyDao(system2), + new IssueFilterDao(dbTester.myBatis()), + new LoadedTemplateDao(dbTester.myBatis()) + )); + + task.start(); + task.stop(); + } +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RenameIssueWidgetsTest/after.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RenameIssueWidgetsTest/after.xml new file mode 100644 index 00000000000..ab8310df45a --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/startup/RenameIssueWidgetsTest/after.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RenameIssueWidgetsTest/before.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RenameIssueWidgetsTest/before.xml new file mode 100644 index 00000000000..d22705c26af --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/startup/RenameIssueWidgetsTest/before.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RenameIssueWidgetsTest/empty.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RenameIssueWidgetsTest/empty.xml new file mode 100644 index 00000000000..871dedcb5e9 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/startup/RenameIssueWidgetsTest/empty.xml @@ -0,0 +1,3 @@ + + + diff --git a/sonar-core/src/main/java/org/sonar/core/dashboard/WidgetMapper.java b/sonar-core/src/main/java/org/sonar/core/dashboard/WidgetMapper.java index 4cb08989823..20bde27df8a 100644 --- a/sonar-core/src/main/java/org/sonar/core/dashboard/WidgetMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/dashboard/WidgetMapper.java @@ -22,6 +22,7 @@ package org.sonar.core.dashboard; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; import java.util.Collection; @@ -43,4 +44,21 @@ public interface WidgetMapper { @Select("select " + COLUMNS + " from widgets where dashboard_id=#{id}") Collection selectByDashboard(long dashboardKey); + + @Select("select " + COLUMNS + " from widgets") + Collection selectAll(); + + @Update("UPDATE widgets SET " + + "dashboard_id=#{dashboardId}, " + + "widget_key=#{widgetKey}, " + + "name=#{name}, " + + "description=#{description}, " + + "column_index=#{columnIndex}, " + + "row_index=#{rowIndex}, " + + "configured=#{configured}, " + + "created_at=#{createdAt}, " + + "updated_at=#{updatedAt}, " + + "resource_id=#{resourceId} " + + "WHERE id=#{id}") + void update(WidgetDto item); } diff --git a/sonar-core/src/main/java/org/sonar/core/dashboard/WidgetPropertyMapper.java b/sonar-core/src/main/java/org/sonar/core/dashboard/WidgetPropertyMapper.java index eef46818904..43a473f1ff6 100644 --- a/sonar-core/src/main/java/org/sonar/core/dashboard/WidgetPropertyMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/dashboard/WidgetPropertyMapper.java @@ -26,6 +26,7 @@ import org.apache.ibatis.annotations.Select; import javax.annotation.CheckForNull; import java.util.Collection; +import java.util.List; public interface WidgetPropertyMapper { @@ -42,4 +43,6 @@ public interface WidgetPropertyMapper { @Select("select " + COLUMNS + " from widget_properties wp " + "inner join widgets w on w.id=wp.widget_id where w.dashboard_id=#{id}") Collection selectByDashboard(long dashboardKey); + + void deleteByWidgetIds(List widgetIds); } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueFilterDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueFilterDao.java index a8c253e61b3..770c503de03 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueFilterDao.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueFilterDao.java @@ -23,6 +23,7 @@ package org.sonar.core.issue.db; import org.apache.ibatis.session.SqlSession; import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; +import org.sonar.core.persistence.DaoComponent; import org.sonar.core.persistence.MyBatis; import javax.annotation.CheckForNull; @@ -32,7 +33,7 @@ import java.util.List; /** * @since 3.7 */ -public class IssueFilterDao implements BatchComponent, ServerComponent { +public class IssueFilterDao implements BatchComponent, ServerComponent, DaoComponent { private final MyBatis mybatis; diff --git a/sonar-core/src/main/resources/org/sonar/core/dashboard/WidgetPropertyMapper.xml b/sonar-core/src/main/resources/org/sonar/core/dashboard/WidgetPropertyMapper.xml index ed1109dc856..e4744f93075 100644 --- a/sonar-core/src/main/resources/org/sonar/core/dashboard/WidgetPropertyMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/dashboard/WidgetPropertyMapper.xml @@ -3,4 +3,10 @@ + + DELETE FROM widget_properties + WHERE widget_id IN + #{wid} + +