From 65cb425e561a1789beee8baa12e6b1aace140c15 Mon Sep 17 00:00:00 2001 From: Fabrice Bellingard Date: Tue, 13 Dec 2011 15:40:00 +0100 Subject: [PATCH] SONAR-1929 Modify the new DashboardTemplate API - Replace the annotation-based one with a POJO one - Fix some issues based on Simon's feedback --- .../core/dashboards/HotspotsDashboard.java | 91 ++++------ .../core/dashboards/SonarMainDashboard.java | 49 ++---- .../resources/org/sonar/l10n/core.properties | 8 +- .../sonar/persistence/model/Dashboard.java | 4 +- .../org/sonar/persistence/model/Widget.java | 4 +- .../persistence/dao/DashboardDaoTest.java | 5 +- .../DashboardDaoTest/shouldInsert-result.xml | 8 +- .../org/sonar/api/web/AbstractDashboard.java | 75 -------- .../java/org/sonar/api/web/Dashboard.java | 53 ------ .../org/sonar/api/web/DashboardWidget.java | 65 ------- .../org/sonar/api/web/DashboardWidgets.java | 43 ----- .../org/sonar/api/web/WidgetProperty.java | 3 + .../sonar/api/web/dashboard/Dashboard.java | 164 ++++++++++++++++++ .../web/{ => dashboard}/DashboardLayouts.java | 26 ++- .../DashboardTemplate.java} | 19 +- .../org/sonar/api/web/dashboard/Widget.java | 94 ++++++++++ .../api/web/dashboard/DashboardTest.java | 65 +++++++ .../sonar/api/web/dashboard/WidgetTest.java | 48 +++++ .../startup/RegisterProvidedDashboards.java | 65 ++++--- .../app/controllers/dashboards_controller.rb | 1 - .../webapp/WEB-INF/app/models/dashboard.rb | 10 +- .../db/migrate/235_create_loaded_templates.rb | 4 +- .../db/migrate/236_add_key_to_dashboards.rb | 6 +- .../RegisterProvidedDashboardsTest.java | 128 +++----------- 24 files changed, 535 insertions(+), 503 deletions(-) delete mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/web/AbstractDashboard.java delete mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/web/Dashboard.java delete mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/web/DashboardWidget.java delete mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/web/DashboardWidgets.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/web/dashboard/Dashboard.java rename sonar-plugin-api/src/main/java/org/sonar/api/web/{ => dashboard}/DashboardLayouts.java (75%) rename sonar-plugin-api/src/main/java/org/sonar/api/web/{Template.java => dashboard/DashboardTemplate.java} (71%) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/web/dashboard/Widget.java create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/web/dashboard/DashboardTest.java create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/web/dashboard/WidgetTest.java diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/dashboards/HotspotsDashboard.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/dashboards/HotspotsDashboard.java index 03f71f62496..f6625d8854c 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/dashboards/HotspotsDashboard.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/dashboards/HotspotsDashboard.java @@ -19,72 +19,49 @@ */ package org.sonar.plugins.core.dashboards; -import org.sonar.api.web.AbstractDashboard; -import org.sonar.api.web.Dashboard; -import org.sonar.api.web.DashboardLayouts; -import org.sonar.api.web.DashboardWidget; -import org.sonar.api.web.DashboardWidgets; -import org.sonar.api.web.WidgetProperty; -import org.sonar.api.web.WidgetPropertyType; +import org.sonar.api.web.dashboard.Dashboard; +import org.sonar.api.web.dashboard.DashboardLayouts; +import org.sonar.api.web.dashboard.DashboardTemplate; +import org.sonar.api.web.dashboard.Widget; -@DashboardWidgets ({ - @DashboardWidget(id="hotspot_most_violated_rules", columnIndex=1, rowIndex=1), - @DashboardWidget(id="hotspot_metric", columnIndex=1, rowIndex=2, - properties={ - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "test_execution_time"), - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING, defaultValue = "Longest unit tests") +/** + * Hotspot dashboard for Sonar + */ +public class HotspotsDashboard extends DashboardTemplate { - }), - @DashboardWidget(id="hotspot_metric", columnIndex=1, rowIndex=3, - properties={ - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "complexity"), - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING, defaultValue = "Highest complexity") + @Override + public org.sonar.api.web.dashboard.Dashboard createDashboard() { + Dashboard dashboard = Dashboard.createDashboard("sonar-hotspots", "Hotspots", DashboardLayouts.TWO_COLUMNS); - }), - @DashboardWidget(id="hotspot_metric", columnIndex=1, rowIndex=4, - properties={ - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "duplicated_lines"), - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING, defaultValue = "Highest duplications") + Widget widget = dashboard.addWidget("hotspot_most_violated_rules", 1, 1); - }), - @DashboardWidget(id="hotspot_most_violated_resources", columnIndex=2, rowIndex=1), - @DashboardWidget(id="hotspot_metric", columnIndex=2, rowIndex=2, - properties={ - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "uncovered_lines"), - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING, defaultValue = "Highest untested lines") + widget = dashboard.addWidget("hotspot_metric", 1, 2); + widget.addProperty("metric", "test_execution_time"); + widget.addProperty("title", "Longest unit tests"); - }), - @DashboardWidget(id="hotspot_metric", columnIndex=2, rowIndex=3, - properties={ - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "function_complexity"), - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING, defaultValue = "Highest average method complexity") + widget = dashboard.addWidget("hotspot_metric", 1, 3); + widget.addProperty("metric", "complexity"); + widget.addProperty("title", "Highest complexity"); - }), - @DashboardWidget(id="hotspot_metric", columnIndex=2, rowIndex=4, - properties={ - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "public_undocumented_api"), - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING, defaultValue = "Most undocumented APIs") + widget = dashboard.addWidget("hotspot_metric", 1, 4); + widget.addProperty("metric", "duplicated_lines"); + widget.addProperty("title", "Highest duplications"); - }) -}) -/** - * Hotspot dashboard for Sonar - */ -public class HotspotsDashboard extends AbstractDashboard implements Dashboard { + widget = dashboard.addWidget("hotspot_most_violated_resources", 2, 1); - @Override - public String getId() { - return "sonar-hotspots-dashboard"; - } + widget = dashboard.addWidget("hotspot_metric", 2, 2); + widget.addProperty("metric", "uncovered_lines"); + widget.addProperty("title", "Highest untested lines"); - @Override - public String getName() { - return "Hotspots"; - } - - @Override - public String getLayout() { - return DashboardLayouts.TWO_COLUMNS; + widget = dashboard.addWidget("hotspot_metric", 2, 3); + widget.addProperty("metric", "function_complexity"); + widget.addProperty("title", "Highest average method complexity"); + + widget = dashboard.addWidget("hotspot_metric", 2, 4); + widget.addProperty("metric", "public_undocumented_api"); + widget.addProperty("title", "Most undocumented APIs"); + + return dashboard; } } \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/dashboards/SonarMainDashboard.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/dashboards/SonarMainDashboard.java index 820419a462d..8b81c8b747e 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/dashboards/SonarMainDashboard.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/dashboards/SonarMainDashboard.java @@ -19,43 +19,30 @@ */ package org.sonar.plugins.core.dashboards; -import org.sonar.api.web.AbstractDashboard; -import org.sonar.api.web.Dashboard; -import org.sonar.api.web.DashboardLayouts; -import org.sonar.api.web.DashboardWidget; -import org.sonar.api.web.DashboardWidgets; +import org.sonar.api.web.dashboard.Dashboard; +import org.sonar.api.web.dashboard.DashboardLayouts; +import org.sonar.api.web.dashboard.DashboardTemplate; -@DashboardWidgets ({ - @DashboardWidget(id="size", columnIndex=1, rowIndex=1), - @DashboardWidget(id="comments_duplications", columnIndex=1, rowIndex=2), - @DashboardWidget(id="complexity", columnIndex=1, rowIndex=3), - @DashboardWidget(id="code_coverage", columnIndex=1, rowIndex=4), - @DashboardWidget(id="events", columnIndex=1, rowIndex=5), - @DashboardWidget(id="description", columnIndex=1, rowIndex=6), - @DashboardWidget(id="rules", columnIndex=2, rowIndex=1), - @DashboardWidget(id="alerts", columnIndex=2, rowIndex=2), - @DashboardWidget(id="file_design", columnIndex=2, rowIndex=3), - @DashboardWidget(id="package_design", columnIndex=2, rowIndex=4), - @DashboardWidget(id="ckjm", columnIndex=2, rowIndex=5) -}) /** * Default dashboard for Sonar */ -public class SonarMainDashboard extends AbstractDashboard implements Dashboard { +public class SonarMainDashboard extends DashboardTemplate { @Override - public String getId() { - return "sonar-main-dashboard"; - } - - @Override - public String getName() { - return "Dashboard"; - } - - @Override - public String getLayout() { - return DashboardLayouts.TWO_COLUMNS; + public org.sonar.api.web.dashboard.Dashboard createDashboard() { + Dashboard dashboard = Dashboard.createDashboard("sonar-main", "Dashboard", DashboardLayouts.TWO_COLUMNS); + dashboard.addWidget("size", 1, 1); + dashboard.addWidget("comments_duplications", 1, 2); + dashboard.addWidget("complexity", 1, 3); + dashboard.addWidget("code_coverage", 1, 4); + dashboard.addWidget("events", 1, 5); + dashboard.addWidget("description", 1, 6); + dashboard.addWidget("rules", 2, 1); + dashboard.addWidget("alerts", 2, 2); + dashboard.addWidget("file_design", 2, 3); + dashboard.addWidget("package_design", 2, 4); + dashboard.addWidget("ckjm", 2, 5); + return dashboard; } } \ No newline at end of file diff --git a/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties b/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties index 60f4c525a91..78585d8bd10 100644 --- a/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties +++ b/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties @@ -449,11 +449,11 @@ property.category.server_id=Server ID # #------------------------------------------------------------------------------ -dashboard.sonar-main-dashboard.name=Dashboard -dashboard.sonar-main-dashboard.description=Default dashboard +dashboard.sonar-main.name=Dashboard +dashboard.sonar-main.description=Default dashboard -dashboard.sonar-hotspots-dashboard.name=Hotspots -dashboard.sonar-hotspots-dashboard.description=Most useful hotspots widgets +dashboard.sonar-hotspots.name=Hotspots +dashboard.sonar-hotspots.description=Most useful hotspots widgets #------------------------------------------------------------------------------ diff --git a/sonar-core/src/main/java/org/sonar/persistence/model/Dashboard.java b/sonar-core/src/main/java/org/sonar/persistence/model/Dashboard.java index a2281e83cce..91429cd1005 100644 --- a/sonar-core/src/main/java/org/sonar/persistence/model/Dashboard.java +++ b/sonar-core/src/main/java/org/sonar/persistence/model/Dashboard.java @@ -19,9 +19,9 @@ */ package org.sonar.persistence.model; -import java.util.ArrayList; import java.util.Collection; import java.util.Date; +import java.util.List; import com.google.common.collect.Lists; @@ -36,7 +36,7 @@ public class Dashboard { private boolean shared; private Date createdAt; private Date updatedAt; - private ArrayList widgets = Lists.newArrayList(); + private List widgets = Lists.newArrayList(); /** * @return the id diff --git a/sonar-core/src/main/java/org/sonar/persistence/model/Widget.java b/sonar-core/src/main/java/org/sonar/persistence/model/Widget.java index 163db94bb5a..a90a4834819 100644 --- a/sonar-core/src/main/java/org/sonar/persistence/model/Widget.java +++ b/sonar-core/src/main/java/org/sonar/persistence/model/Widget.java @@ -19,9 +19,9 @@ */ package org.sonar.persistence.model; -import java.util.ArrayList; import java.util.Collection; import java.util.Date; +import java.util.List; import com.google.common.collect.Lists; @@ -37,7 +37,7 @@ public class Widget { private boolean configured; private Date createdAt; private Date updatedAt; - private ArrayList widgetProperties = Lists.newArrayList(); + private List widgetProperties = Lists.newArrayList(); /** * @return the id diff --git a/sonar-core/src/test/java/org/sonar/persistence/dao/DashboardDaoTest.java b/sonar-core/src/test/java/org/sonar/persistence/dao/DashboardDaoTest.java index c8fea9648ff..1824cd169bd 100644 --- a/sonar-core/src/test/java/org/sonar/persistence/dao/DashboardDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/persistence/dao/DashboardDaoTest.java @@ -39,7 +39,7 @@ public class DashboardDaoTest extends DaoTestCase { @Test public void shouldInsert() throws Exception { setupData("shouldInsert"); - Date aDate = new Date(123456789); + Date aDate = new Date(); Dashboard dashboard = new Dashboard(); dashboard.setKey("d-key"); @@ -70,13 +70,12 @@ public class DashboardDaoTest extends DaoTestCase { dao.insert(dashboard); - checkTables("shouldInsert", "dashboards", "widgets", "widget_properties"); + checkTables("shouldInsert", new String[] { "created_at", "updated_at" }, "dashboards", "widgets", "widget_properties"); } @Test public void shouldInsertWithNullableColumns() throws Exception { setupData("shouldInsert"); - Date aDate = new Date(123456789); Dashboard dashboard = new Dashboard(); dashboard.setKey("d-key"); diff --git a/sonar-core/src/test/resources/org/sonar/persistence/dao/DashboardDaoTest/shouldInsert-result.xml b/sonar-core/src/test/resources/org/sonar/persistence/dao/DashboardDaoTest/shouldInsert-result.xml index f5b4eca9ec8..5f4e92ddd80 100644 --- a/sonar-core/src/test/resources/org/sonar/persistence/dao/DashboardDaoTest/shouldInsert-result.xml +++ b/sonar-core/src/test/resources/org/sonar/persistence/dao/DashboardDaoTest/shouldInsert-result.xml @@ -7,9 +7,7 @@ name="My Dashboard" description="This is a dashboard" column_layout="100%" - shared="true" - created_at="1970-01-02 11:17:36.789" - updated_at="1970-01-02 11:17:36.789"/> + shared="true"/> + configured="true"/> widgets; + + private Dashboard() { + widgets = Lists.newArrayList(); + } + + /** + * Creates a new {@link Dashboard}. See {@link DashboardLayouts} for the layout parameter. + * + * @param id + * the id + * @param name + * the name + * @param layout + * the layout + */ + public static Dashboard createDashboard(String id, String name, String layout) { + Dashboard dashboard = new Dashboard(); + dashboard.setId(id); + dashboard.setName(name); + dashboard.setDescription(""); + dashboard.setLayout(layout); + return dashboard; + } + + /** + * Add a widget with the given parameters, and return the newly created {@link Widget} object if one wants to add parameters to it. + * + * @param id + * the id of the widget + * @param columnId + * the column for the widget + * @param rowId + * the row for the widget + * @return the new widget + */ + public Widget addWidget(String id, int columnId, int rowId) { + Widget widget = new Widget(id, columnId, rowId); + widgets.add(widget); + return widget; + } + + /** + * Returns the list of widgets. + * + * @return the widgets of this dashboard + */ + public Collection getWidgets() { + return widgets; + } + + /** + * Returns the identifier of the dashboard. + * + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id + * the id to set + */ + private void setId(String id) { + this.id = id; + } + + /** + * Returns the name of the dashboard. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name + * the name to set + */ + private void setName(String name) { + this.name = name; + } + + /** + * Returns the description of the dashboard. + * + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * Sets the description of the dashboard. + * + * Note: you should use the i18n mechanism for the description. + * + * @param description + * the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Returns the layout of the dashboard. + * + * @return the layout + */ + public String getLayout() { + return layout; + } + + /** + * @param layout + * the layout to set + */ + private void setLayout(String layout) { + this.layout = layout; + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/DashboardLayouts.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/dashboard/DashboardLayouts.java similarity index 75% rename from sonar-plugin-api/src/main/java/org/sonar/api/web/DashboardLayouts.java rename to sonar-plugin-api/src/main/java/org/sonar/api/web/dashboard/DashboardLayouts.java index 5aa79ce4055..186a16f5b4c 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/web/DashboardLayouts.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/dashboard/DashboardLayouts.java @@ -17,19 +17,41 @@ * License along with Sonar; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ -package org.sonar.api.web; +package org.sonar.api.web.dashboard; /** * Possible layouts for a dashboard. * * @since 2.13 */ -public class DashboardLayouts { +public final class DashboardLayouts { + private DashboardLayouts() { + } + + /** + * Only 1 column that take all the page + */ public static final String ONE_COLUMN = "100%"; + + /** + * 2 columns of the same width + */ public static final String TWO_COLUMNS = "50%-50%"; + + /** + * 2 columns with the first one smaller than the second + */ public static final String TWO_COLUMNS_30_70 = "30%-70%"; + + /** + * 2 columns with the first one bigger than the second + */ public static final String TWO_COLUMNS_70_30 = "70%-30%"; + + /** + * 3 columns of the same width + */ public static final String TREE_COLUMNS = "33%-33%-33%"; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/Template.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/dashboard/DashboardTemplate.java similarity index 71% rename from sonar-plugin-api/src/main/java/org/sonar/api/web/Template.java rename to sonar-plugin-api/src/main/java/org/sonar/api/web/dashboard/DashboardTemplate.java index c8eec915ed1..17412997c68 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/web/Template.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/dashboard/DashboardTemplate.java @@ -17,28 +17,23 @@ * License along with Sonar; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ -package org.sonar.api.web; +package org.sonar.api.web.dashboard; import org.sonar.api.ServerExtension; /** - * A template defines a server extension that will be run only once to initialize and store some data in the database. + * + * This extension point must be implemented to define a new dashboard. * * @since 2.13 */ -public interface Template extends ServerExtension { +public abstract class DashboardTemplate implements ServerExtension { /** - * Returns the identifier of this template. + * Returns the {@link Dashboard} object that represents the dashboard to use. * - * @return the id + * @return the dashboard */ - String getId(); + public abstract Dashboard createDashboard(); - /** - * Returns the kind of template. - * - * @return the type - */ - String getType(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/dashboard/Widget.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/dashboard/Widget.java new file mode 100644 index 00000000000..8929f4f60f9 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/dashboard/Widget.java @@ -0,0 +1,94 @@ +/* + * 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.api.web.dashboard; + +import java.util.Map; + +import com.google.common.collect.Maps; + +/** + * + * Definition of a widget inside a dashboard. + * + * @since 2.13 + */ +public final class Widget { + + private String id; + private int columnIndex; + private int rowIndex; + private Map properties; + + Widget(String id, int columnIndex, int rowIndex) { + this.id = id; + this.columnIndex = columnIndex; + this.rowIndex = rowIndex; + this.properties = Maps.newHashMap(); + } + + /** + * Adds a property to this widget. + * + * @param key + * the id of the property + * @param value + * the value of the property + */ + public void addProperty(String key, String value) { + properties.put(key, value); + } + + /** + * Returns the properties of this widget. + * + * @return the properties + */ + public Map getProperties() { + return properties; + } + + /** + * Returns the identifier of this widget. + * + * @return the id + */ + public String getId() { + return id; + } + + /** + * Returns the column index of this widget. + * + * @return the columnIndex + */ + public int getColumnIndex() { + return columnIndex; + } + + /** + * Returns the row index of this widget. + * + * @return the rowIndex + */ + public int getRowIndex() { + return rowIndex; + } + +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/web/dashboard/DashboardTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/web/dashboard/DashboardTest.java new file mode 100644 index 00000000000..bcac4887a06 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/web/dashboard/DashboardTest.java @@ -0,0 +1,65 @@ +/* + * 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.api.web.dashboard; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.util.Map.Entry; +import java.util.Set; + +import org.junit.Test; + +public class DashboardTest { + + @Test + public void shouldCreateDashboardAndWidget() throws Exception { + Dashboard dashboard = Dashboard.createDashboard("fake-dashboard", "Fake", "30%-70%"); + assertThat(dashboard.getId(), is("fake-dashboard")); + assertThat(dashboard.getName(), is("Fake")); + assertThat(dashboard.getLayout(), is("30%-70%")); + assertThat(dashboard.getDescription(), is("")); + + Widget widget = dashboard.addWidget("fake-widget", 12, 13); + assertThat(widget.getId(), is("fake-widget")); + assertThat(widget.getColumnIndex(), is(12)); + assertThat(widget.getRowIndex(), is(13)); + + widget.addProperty("fake-property", "fake_metric"); + Set> properties = widget.getProperties().entrySet(); + assertThat(properties.size(), is(1)); + Entry property = properties.iterator().next(); + assertThat(property.getKey(), is("fake-property")); + assertThat(property.getValue(), is("fake_metric")); + } + + @Test + public void shouldAddWidget() throws Exception { + Dashboard dashboard = Dashboard.createDashboard("fake-dashboard", "Fake", "30%-70%"); + dashboard.addWidget("fake-widget", 12, 13); + + Widget widget = dashboard.getWidgets().iterator().next(); + + assertThat(widget.getId(), is("fake-widget")); + assertThat(widget.getColumnIndex(), is(12)); + assertThat(widget.getRowIndex(), is(13)); + } + +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/web/dashboard/WidgetTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/web/dashboard/WidgetTest.java new file mode 100644 index 00000000000..199d83cd9ef --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/web/dashboard/WidgetTest.java @@ -0,0 +1,48 @@ +/* + * 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.api.web.dashboard; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.util.Map.Entry; +import java.util.Set; + +import org.junit.Test; + +public class WidgetTest { + + @Test + public void shouldCreateWidgetWithProperties() throws Exception { + Dashboard dashboard = Dashboard.createDashboard("fake-dashboard", "Fake", "30%-70%"); + Widget widget = dashboard.addWidget("fake-widget", 12, 13); + assertThat(widget.getId(), is("fake-widget")); + assertThat(widget.getColumnIndex(), is(12)); + assertThat(widget.getRowIndex(), is(13)); + + widget.addProperty("fake-property", "fake_metric"); + Set> properties = widget.getProperties().entrySet(); + assertThat(properties.size(), is(1)); + Entry property = properties.iterator().next(); + assertThat(property.getKey(), is("fake-property")); + assertThat(property.getValue(), is("fake_metric")); + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/startup/RegisterProvidedDashboards.java b/sonar-server/src/main/java/org/sonar/server/startup/RegisterProvidedDashboards.java index 470bb0c5271..87af9748a50 100644 --- a/sonar-server/src/main/java/org/sonar/server/startup/RegisterProvidedDashboards.java +++ b/sonar-server/src/main/java/org/sonar/server/startup/RegisterProvidedDashboards.java @@ -23,31 +23,31 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; +import java.util.List; import java.util.Locale; +import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.utils.TimeProfiler; -import org.sonar.api.web.Dashboard; -import org.sonar.api.web.DashboardWidget; -import org.sonar.api.web.DashboardWidgets; -import org.sonar.api.web.WidgetProperty; +import org.sonar.api.web.dashboard.Dashboard; +import org.sonar.api.web.dashboard.DashboardTemplate; +import org.sonar.api.web.dashboard.Widget; import org.sonar.core.i18n.I18nManager; import org.sonar.persistence.dao.ActiveDashboardDao; import org.sonar.persistence.dao.DashboardDao; import org.sonar.persistence.dao.LoadedTemplateDao; import org.sonar.persistence.model.ActiveDashboard; import org.sonar.persistence.model.LoadedTemplate; -import org.sonar.persistence.model.Widget; import com.google.common.collect.Lists; public final class RegisterProvidedDashboards { private static final Logger LOGGER = LoggerFactory.getLogger(RegisterProvidedDashboards.class); - private static final String MAIN_DASHBOARD_ID = "sonar-main-dashboard"; + private static final String MAIN_DASHBOARD_ID = "sonar-main"; - private ArrayList dashboards; + private List dashboardTemplates; private DashboardDao dashboardDao; private ActiveDashboardDao activeDashboardDao; private LoadedTemplateDao loadedTemplateDao; @@ -55,12 +55,12 @@ public final class RegisterProvidedDashboards { public RegisterProvidedDashboards(DashboardDao dashboardDao, ActiveDashboardDao activeDashboardDao, LoadedTemplateDao loadedTemplateDao, I18nManager i18nManager) { - this(new Dashboard[] {}, dashboardDao, activeDashboardDao, loadedTemplateDao, i18nManager); + this(new DashboardTemplate[] {}, dashboardDao, activeDashboardDao, loadedTemplateDao, i18nManager); } - public RegisterProvidedDashboards(Dashboard[] dashboardArray, DashboardDao dashboardDao, ActiveDashboardDao activeDashboardDao, - LoadedTemplateDao loadedTemplateDao, I18nManager i18nManager) { - this.dashboards = Lists.newArrayList(dashboardArray); + public RegisterProvidedDashboards(DashboardTemplate[] dashboardTemplatesArray, DashboardDao dashboardDao, + ActiveDashboardDao activeDashboardDao, LoadedTemplateDao loadedTemplateDao, I18nManager i18nManager) { + this.dashboardTemplates = Lists.newArrayList(dashboardTemplatesArray); this.dashboardDao = dashboardDao; this.activeDashboardDao = activeDashboardDao; this.loadedTemplateDao = loadedTemplateDao; @@ -73,7 +73,8 @@ public final class RegisterProvidedDashboards { // load the dashboards that need to be loaded ArrayList loadedDashboards = Lists.newArrayList(); org.sonar.persistence.model.Dashboard mainDashboard = null; - for (Dashboard dashboard : dashboards) { + for (DashboardTemplate dashboardTemplate : dashboardTemplates) { + Dashboard dashboard = dashboardTemplate.createDashboard(); if (shouldBeLoaded(dashboard)) { org.sonar.persistence.model.Dashboard dashboardDataModel = loadDashboard(dashboard); if (MAIN_DASHBOARD_ID.equals(dashboard.getId())) { @@ -89,7 +90,7 @@ public final class RegisterProvidedDashboards { profiler.stop(); } - protected void activateDashboards(ArrayList loadedDashboards, + protected void activateDashboards(List loadedDashboards, org.sonar.persistence.model.Dashboard mainDashboard) { int nextOrderIndex = 0; if (mainDashboard != null) { @@ -132,28 +133,22 @@ public final class RegisterProvidedDashboards { dashboardDataModel.setCreatedAt(now); dashboardDataModel.setUpdatedAt(now); - DashboardWidgets dashboardWidgets = dashboard.getClass().getAnnotation(DashboardWidgets.class); - if (dashboardWidgets != null) { - for (DashboardWidget dashboardWidget : dashboardWidgets.value()) { - Widget widget = new Widget(); - widget.setKey(dashboardWidget.id()); - widget.setName(i18nManager.message(Locale.ENGLISH, "widget." + dashboardWidget.id() + ".name", "")); - widget.setColumnIndex(dashboardWidget.columnIndex()); - widget.setRowIndex(dashboardWidget.rowIndex()); - widget.setConfigured(true); - widget.setCreatedAt(now); - widget.setUpdatedAt(now); - dashboardDataModel.addWidget(widget); - - WidgetProperty[] dashboardWidgetProperties = dashboardWidget.properties(); - for (int i = 0; i < dashboardWidgetProperties.length; i++) { - WidgetProperty dashboardWidgetProperty = dashboardWidgetProperties[i]; - org.sonar.persistence.model.WidgetProperty widgetProperty = new org.sonar.persistence.model.WidgetProperty(); - widgetProperty.setKey(dashboardWidgetProperty.key()); - widgetProperty.setValue(dashboardWidgetProperty.defaultValue()); - widgetProperty.setValueType(dashboardWidgetProperty.type().toString()); - widget.addWidgetProperty(widgetProperty); - } + for (Widget widget : dashboard.getWidgets()) { + org.sonar.persistence.model.Widget widgetDataModel = new org.sonar.persistence.model.Widget(); + widgetDataModel.setKey(widget.getId()); + widgetDataModel.setName(i18nManager.message(Locale.ENGLISH, "widget." + widget.getId() + ".name", "")); + widgetDataModel.setColumnIndex(widget.getColumnIndex()); + widgetDataModel.setRowIndex(widget.getRowIndex()); + widgetDataModel.setConfigured(true); + widgetDataModel.setCreatedAt(now); + widgetDataModel.setUpdatedAt(now); + dashboardDataModel.addWidget(widgetDataModel); + + for (Entry property : widget.getProperties().entrySet()) { + org.sonar.persistence.model.WidgetProperty widgetPropertyDataModel = new org.sonar.persistence.model.WidgetProperty(); + widgetPropertyDataModel.setKey(property.getKey()); + widgetPropertyDataModel.setValue(property.getValue()); + widgetDataModel.addWidgetProperty(widgetPropertyDataModel); } } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboards_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboards_controller.rb index d0868c84a84..22bfe87eb5a 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboards_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/dashboards_controller.rb @@ -168,7 +168,6 @@ class DashboardsController < ApplicationController def load_dashboard_from_params(dashboard) dashboard.name=params[:name] - dashboard.kee=dashboard.name.strip.downcase.sub(/\s+/, '_') dashboard.description=params[:description] dashboard.shared=(params[:shared].present? && is_admin?) dashboard.user_id=current_user.id diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/dashboard.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/dashboard.rb index 1a136f86081..bb5c54c1a84 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/dashboard.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/dashboard.rb @@ -30,17 +30,15 @@ class Dashboard < ActiveRecord::Base validates_length_of :description, :maximum => 1000, :allow_blank => true, :allow_nil => true validates_length_of :column_layout, :maximum => 20, :allow_blank => false, :allow_nil => false validates_uniqueness_of :name, :scope => :user_id + + before_create { |dashboard| dashboard.kee=dashboard.name.strip.downcase.sub(/\s+/, '_') } def name - default_name = read_attribute(:name) - default_name = Api::Utils.message('dashboard.' + read_attribute(:kee) + '.name', :default => default_name) if read_attribute(:kee) - default_name + Api::Utils.message("dashboard.#{kee}.name", :default => read_attribute(:name)) end def description - default_description = read_attribute(:description) - default_description = Api::Utils.message('dashboard.' + read_attribute(:kee) + '.description', :default => default_description) if read_attribute(:kee) - default_description + Api::Utils.message("dashboard.#{kee}.description", :default => read_attribute(:description)) end def shared? diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/235_create_loaded_templates.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/235_create_loaded_templates.rb index 57cd8bc438a..bd313df7d43 100644 --- a/sonar-server/src/main/webapp/WEB-INF/db/migrate/235_create_loaded_templates.rb +++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/235_create_loaded_templates.rb @@ -29,12 +29,10 @@ class CreateLoadedTemplates < ActiveRecord::Migration t.column 'template_type', :string, :null => true, :limit => 15 end - alter_to_big_primary_key('loaded_templates') - # if this is a migration, then the default dashboard already exists in the DB so it should not be loaded again main_dashboard = Dashboard.find(:first, :conditions => {:name => 'Dashboard'}) if main_dashboard - LoadedTemplate.new({:kee => 'sonar-main-dashboard', :template_type => 'DASHBOARD'}).save + LoadedTemplate.create({:kee => 'sonar-main', :template_type => 'DASHBOARD'}) end end diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/236_add_key_to_dashboards.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/236_add_key_to_dashboards.rb index f000fc068f1..d1031a6eeeb 100644 --- a/sonar-server/src/main/webapp/WEB-INF/db/migrate/236_add_key_to_dashboards.rb +++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/236_add_key_to_dashboards.rb @@ -24,7 +24,7 @@ class AddKeyToDashboards < ActiveRecord::Migration def self.up - add_column 'dashboards', 'kee', :string, :limit => 256 + add_column 'dashboards', 'kee', :string, :limit => 200 Dashboard.reset_column_information Dashboard.find(:all).each do |d| @@ -34,11 +34,11 @@ class AddKeyToDashboards < ActiveRecord::Migration main_dashboard = Dashboard.find(:first, :conditions => {:name => 'Dashboard'}) if main_dashboard - main_dashboard.kee = 'sonar-main-dashboard' + main_dashboard.kee = 'sonar-main' main_dashboard.save end - change_column 'dashboards', 'kee', :string, :limit => 256, :null => false + change_column 'dashboards', 'kee', :string, :limit => 200, :null => false Dashboard.reset_column_information end diff --git a/sonar-server/src/test/java/org/sonar/server/startup/RegisterProvidedDashboardsTest.java b/sonar-server/src/test/java/org/sonar/server/startup/RegisterProvidedDashboardsTest.java index 4525ad3e9ee..6973393972b 100644 --- a/sonar-server/src/test/java/org/sonar/server/startup/RegisterProvidedDashboardsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/startup/RegisterProvidedDashboardsTest.java @@ -23,6 +23,7 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -35,13 +36,8 @@ import java.util.Locale; import org.junit.Before; import org.junit.Test; -import org.sonar.api.web.AbstractDashboard; -import org.sonar.api.web.Dashboard; -import org.sonar.api.web.DashboardLayouts; -import org.sonar.api.web.DashboardWidget; -import org.sonar.api.web.DashboardWidgets; -import org.sonar.api.web.WidgetProperty; -import org.sonar.api.web.WidgetPropertyType; +import org.sonar.api.web.dashboard.Dashboard; +import org.sonar.api.web.dashboard.DashboardTemplate; import org.sonar.core.i18n.I18nManager; import org.sonar.persistence.dao.ActiveDashboardDao; import org.sonar.persistence.dao.DashboardDao; @@ -58,8 +54,8 @@ public class RegisterProvidedDashboardsTest { private DashboardDao dashboardDao; private ActiveDashboardDao activeDashboardDao; private LoadedTemplateDao loadedTemplateDao; - private Dashboard dashboard; private I18nManager i18nManager; + private DashboardTemplate fakeDashboardTemplate; @Before public void init() { @@ -69,40 +65,45 @@ public class RegisterProvidedDashboardsTest { i18nManager = mock(I18nManager.class); when(i18nManager.message(Locale.ENGLISH, "widget.fake-widget.name", "")).thenReturn("Fake Widget"); when(i18nManager.message(Locale.ENGLISH, "dashboard.fake-dashboard.name", "Fake")).thenReturn("Fake Dashboard"); - dashboard = new FakeDashboard(); - registerProvidedDashboards = new RegisterProvidedDashboards(new Dashboard[] { dashboard }, dashboardDao, activeDashboardDao, - loadedTemplateDao, i18nManager); + fakeDashboardTemplate = new FakeDashboard(); + + registerProvidedDashboards = new RegisterProvidedDashboards(new DashboardTemplate[] { fakeDashboardTemplate }, dashboardDao, + activeDashboardDao, loadedTemplateDao, i18nManager); + } + + @Test + public void testStart() throws Exception { + registerProvidedDashboards.start(); + verify(dashboardDao).insert(any(org.sonar.persistence.model.Dashboard.class)); + verify(loadedTemplateDao).insert(any(LoadedTemplate.class)); + verify(activeDashboardDao).insert(any(ActiveDashboard.class)); } @Test public void testShouldNotBeLoaded() throws Exception { when(loadedTemplateDao.selectByKeyAndType("fake-dashboard", LoadedTemplate.DASHBOARD_TYPE)).thenReturn(new LoadedTemplate()); - assertThat(registerProvidedDashboards.shouldBeLoaded(dashboard), is(false)); + assertThat(registerProvidedDashboards.shouldBeLoaded(fakeDashboardTemplate.createDashboard()), is(false)); } @Test public void testShouldBeLoaded() throws Exception { - assertThat(registerProvidedDashboards.shouldBeLoaded(dashboard), is(true)); + assertThat(registerProvidedDashboards.shouldBeLoaded(fakeDashboardTemplate.createDashboard()), is(true)); } @Test public void shouldLoadDasboard() throws Exception { - org.sonar.persistence.model.Dashboard dataModelDashboard = registerProvidedDashboards.loadDashboard(dashboard); + org.sonar.persistence.model.Dashboard dataModelDashboard = registerProvidedDashboards.loadDashboard(fakeDashboardTemplate + .createDashboard()); assertNotNull(dataModelDashboard); verify(dashboardDao).insert(dataModelDashboard); verify(loadedTemplateDao).insert(eq(new LoadedTemplate("fake-dashboard", LoadedTemplate.DASHBOARD_TYPE))); } - @Test - public void test() { - org.sonar.persistence.model.Dashboard dataModelDashboard = registerProvidedDashboards.createDataModelFromExtension(new HotspotsDashboard()); - System.out.println(dataModelDashboard); - } - @Test public void shouldCreateDataModelFromExtension() { - org.sonar.persistence.model.Dashboard dataModelDashboard = registerProvidedDashboards.createDataModelFromExtension(dashboard); + org.sonar.persistence.model.Dashboard dataModelDashboard = registerProvidedDashboards + .createDataModelFromExtension(fakeDashboardTemplate.createDashboard()); assertThat(dataModelDashboard.getUserId(), is(nullValue())); assertThat(dataModelDashboard.getKey(), is("fake-dashboard")); assertThat(dataModelDashboard.getName(), is("Fake Dashboard")); @@ -125,7 +126,6 @@ public class RegisterProvidedDashboardsTest { org.sonar.persistence.model.WidgetProperty widgetProperty = widget.getWidgetProperties().iterator().next(); assertThat(widgetProperty.getKey(), is("fake-property")); assertThat(widgetProperty.getValue(), is("fake_metric")); - assertThat(widgetProperty.getValueType(), is("METRIC")); } @Test @@ -186,87 +186,15 @@ public class RegisterProvidedDashboardsTest { verify(activeDashboardDao).insert(eq(ad2)); } - @DashboardWidgets({ @DashboardWidget(id = "fake-widget", columnIndex = 12, rowIndex = 13, properties = { @WidgetProperty( - key = "fake-property", type = WidgetPropertyType.METRIC, defaultValue = "fake_metric") }) }) - public class FakeDashboard extends AbstractDashboard implements Dashboard { + public class FakeDashboard extends DashboardTemplate { @Override - public String getId() { - return "fake-dashboard"; + public Dashboard createDashboard() { + Dashboard dashboard = Dashboard.createDashboard("fake-dashboard", "Fake", "30%-70%"); + org.sonar.api.web.dashboard.Widget widget = dashboard.addWidget("fake-widget", 12, 13); + widget.addProperty("fake-property", "fake_metric"); + return dashboard; } - - @Override - public String getName() { - return "Fake"; - } - - @Override - public String getLayout() { - return "30%-70%"; - } - } - - @DashboardWidgets ({ - @DashboardWidget(id="hotspot_most_violated_rules", columnIndex=1, rowIndex=1), - @DashboardWidget(id="hotspot_metric", columnIndex=1, rowIndex=2, - properties={ - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "test_execution_time"), - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING, defaultValue = "Longest unit tests") - - }), - @DashboardWidget(id="hotspot_metric", columnIndex=1, rowIndex=3, - properties={ - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "complexity"), - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING, defaultValue = "Highest complexity") - }), - @DashboardWidget(id="hotspot_metric", columnIndex=1, rowIndex=4, - properties={ - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "duplicated_lines"), - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING, defaultValue = "Highest duplications") - - }), - @DashboardWidget(id="hotspot_most_violated_resources", columnIndex=2, rowIndex=1), - @DashboardWidget(id="hotspot_metric", columnIndex=2, rowIndex=2, - properties={ - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "uncovered_lines"), - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING, defaultValue = "Highest untested lines") - - }), - @DashboardWidget(id="hotspot_metric", columnIndex=2, rowIndex=3, - properties={ - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "function_complexity"), - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING, defaultValue = "Highest average method complexity") - - }), - @DashboardWidget(id="hotspot_metric", columnIndex=2, rowIndex=4, - properties={ - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "public_undocumented_api"), - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING, defaultValue = "Most undocumented APIs") - - }) - }) - /** - * Hotspot dashboard for Sonar - */ - public class HotspotsDashboard extends AbstractDashboard implements Dashboard { - - @Override - public String getId() { - return "sonar-hotspots-dashboard"; - } - - @Override - public String getName() { - return "Hotspots"; - } - - @Override - public String getLayout() { - return DashboardLayouts.TWO_COLUMNS; - } - - } - } -- 2.39.5