From b57aa56db1bc54a98c04323733635ec664d9ec7f Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Thu, 29 Nov 2012 15:33:01 +0100 Subject: [PATCH] SONAR-3825 support the parameter "baseId" --- .../resources/org/sonar/l10n/core.properties | 5 +- .../org/sonar/core/measure/MeasureFilter.java | 15 +++- .../core/measure/MeasureFilterCondition.java | 45 +++++++++++- .../core/measure/MeasureFilterDecoder.java | 2 +- .../core/measure/MeasureFilterExecutor.java | 3 + .../core/measure/MeasureFilterFactory.java | 14 ++-- .../org/sonar/core/resource/ResourceDao.java | 6 +- .../sonar/core/resource/ResourceMapper.java | 4 +- .../sonar/core/resource/ResourceMapper.xml | 4 + .../measure/MeasureFilterExecutorTest.java | 10 +-- .../WEB-INF/app/helpers/measures_helper.rb | 2 +- .../WEB-INF/app/models/measure_filter.rb | 12 ++- .../app/views/measures/_display_list.html.erb | 21 +++--- .../app/views/measures/_filters.html.erb | 2 + .../app/views/measures/search.html.erb | 73 +++++++++++++------ .../357_move_existing_measure_filters.rb | 6 +- .../src/main/webapp/stylesheets/layout.css | 1 + 17 files changed, 168 insertions(+), 57 deletions(-) diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties b/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties index f1c80cad45a..fad4634ba8f 100644 --- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties @@ -392,7 +392,7 @@ measure_filter.col.key=Key measure_filter.col.language=Language measure_filter.col.links=Links measure_filter.col.name=Name -measure_filter.col.short_name=Name +measure_filter.col.short_name=Short Name measure_filter.col.version=Version measure_filter.missing_name=Name is missing measure_filter.name_too_long=Name is too long @@ -403,6 +403,9 @@ measure_filter.remove_from_system=Remove from system measure_filter.add_to_system=Add to system measure_filter.title_shared_filters=Shared Filters measure_filter.title_system_filters=System Filters +measure_filter.key_like=Key like +measure_filter.name_contains=Name contains +measure_filter.only_favourites=Favourites only #------------------------------------------------------------------------------ # diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java index cbe358cb792..4145203540b 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java @@ -36,7 +36,11 @@ public class MeasureFilter { // conditions on resources private String baseResourceKey; - private boolean onBaseResourceChildren = false; // only if getBaseResourceKey is set + private Long baseResourceId; + + // only if baseResourceKey or baseResourceId are set + private boolean onBaseResourceChildren = false; + private List resourceScopes = Collections.emptyList(); private List resourceQualifiers = Collections.emptyList(); private List resourceLanguages = Collections.emptyList(); @@ -60,6 +64,15 @@ public class MeasureFilter { return this; } + public Long getBaseResourceId() { + return baseResourceId; + } + + public MeasureFilter setBaseResourceId(Long i) { + this.baseResourceId = i; + return this; + } + public MeasureFilter setOnBaseResourceChildren(boolean b) { this.onBaseResourceChildren = b; return this; diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterCondition.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterCondition.java index f991b2f270f..2485c905583 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterCondition.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterCondition.java @@ -24,13 +24,50 @@ import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.api.measures.Metric; public class MeasureFilterCondition { + public enum Operator { + EQUALS("eq", "="), GREATER("gt", ">"), GREATER_OR_EQUALS("gte", ">="), LESS("lt", "<"), LESS_OR_EQUALS("lte", "<="); + + private String code; + private String sql; + + private Operator(String code, String sql) { + this.code = code; + this.sql = sql; + } + + public String getCode() { + return code; + } + + public String getSql() { + return sql; + } + + public static Operator fromCode(String code) { + for (Operator operator : values()) { + if (operator.code.equals(code)) { + return operator; + } + } + throw new IllegalArgumentException("Unknown operator code: " + code); + } + + public static Operator fromSql(String sql) { + for (Operator operator : values()) { + if (operator.sql.equals(sql)) { + return operator; + } + } + throw new IllegalArgumentException("Unknown operator sql: " + sql); + } + } private final Metric metric; - private final String operator; + private final Operator operator; private final double value; private Integer period = null; - public MeasureFilterCondition(Metric metric, String operator, double value) { + public MeasureFilterCondition(Metric metric, Operator operator, double value) { this.metric = metric; this.operator = operator; this.value = value; @@ -45,7 +82,7 @@ public class MeasureFilterCondition { return metric; } - public String operator() { + public Operator operator() { return operator; } @@ -67,7 +104,7 @@ public class MeasureFilterCondition { void appendSqlCondition(StringBuilder sql) { sql.append(" pm.metric_id="); sql.append(metric.getId()); - sql.append(" AND ").append(valueColumn()).append(operator).append(value); + sql.append(" AND ").append(valueColumn()).append(operator.getSql()).append(value); } @Override diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterDecoder.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterDecoder.java index c1871be38de..eed8ded2246 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterDecoder.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterDecoder.java @@ -106,7 +106,7 @@ public class MeasureFilterDecoder implements ServerComponent { Metric metric = metricFinder.findByKey((String) c.get("metric")); String operator = (String) c.get("op"); Double value = (Double) c.get("val"); - MeasureFilterCondition condition = new MeasureFilterCondition(metric, operator, value); + MeasureFilterCondition condition = new MeasureFilterCondition(metric, MeasureFilterCondition.Operator.fromSql(operator), value); if (c.containsKey("period")) { condition.setPeriod(((Long) c.get("period")).intValue()); } diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterExecutor.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterExecutor.java index 0de8fad5f27..64179399792 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterExecutor.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterExecutor.java @@ -72,11 +72,14 @@ public class MeasureFilterExecutor implements ServerComponent { private void prepareContext(MeasureFilterContext context, MeasureFilter filter, SqlSession session) { if (filter.getBaseResourceKey() != null) { context.setBaseSnapshot(resourceDao.getLastSnapshot(filter.getBaseResourceKey(), session)); + } else if (filter.getBaseResourceId() != null) { + context.setBaseSnapshot(resourceDao.getLastSnapshotByResourceId(filter.getBaseResourceId(), session)); } } static boolean isValid(MeasureFilter filter, MeasureFilterContext context) { boolean valid = (Strings.isNullOrEmpty(filter.getBaseResourceKey()) || context.getBaseSnapshot()!=null); + valid &= (filter.getBaseResourceId()==null || context.getBaseSnapshot()!=null); valid &= !(filter.isOnBaseResourceChildren() && context.getBaseSnapshot() == null); valid &= !(filter.isOnFavourites() && context.getUserId() == null); valid &= validateMeasureConditions(filter); diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterFactory.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterFactory.java index bff48d90249..e951420881c 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterFactory.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterFactory.java @@ -45,6 +45,9 @@ public class MeasureFilterFactory implements ServerComponent { public MeasureFilter create(Map properties) { MeasureFilter filter = new MeasureFilter(); filter.setBaseResourceKey((String) properties.get("base")); + if (properties.containsKey("baseId")) { + filter.setBaseResourceId(Long.valueOf((String) properties.get("baseId"))); + } filter.setResourceScopes(toList(properties.get("scopes"))); filter.setResourceQualifiers(toList(properties.get("qualifiers"))); filter.setResourceLanguages(toList(properties.get("languages"))); @@ -92,13 +95,14 @@ public class MeasureFilterFactory implements ServerComponent { private MeasureFilterCondition toCondition(Map props, int index) { MeasureFilterCondition condition = null; - String metricKey = (String) props.get("c" + index + "metric"); - String op = (String) props.get("c" + index + "op"); - String val = (String) props.get("c" + index + "val"); + String metricKey = (String) props.get("c" + index + "_metric"); + String op = (String) props.get("c" + index + "_op"); + String val = (String) props.get("c" + index + "_val"); if (!Strings.isNullOrEmpty(metricKey) && !Strings.isNullOrEmpty(op) && !Strings.isNullOrEmpty(val)) { Metric metric = metricFinder.findByKey(metricKey); - condition = new MeasureFilterCondition(metric, op, Double.parseDouble(val)); - String period = (String) props.get("c" + index + "period"); + MeasureFilterCondition.Operator operator = MeasureFilterCondition.Operator.fromCode(op); + condition = new MeasureFilterCondition(metric, operator, Double.parseDouble(val)); + String period = (String) props.get("c" + index + "_period"); if (period != null) { condition.setPeriod(Integer.parseInt(period)); } diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java index 1b6a4e1398f..b8a7c88e546 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java @@ -77,9 +77,13 @@ public class ResourceDao { } public SnapshotDto getLastSnapshot(String resourceKey, SqlSession session) { - return session.getMapper(ResourceMapper.class).selectLastSnapshotByKey(resourceKey); + return session.getMapper(ResourceMapper.class).selectLastSnapshotByResourceKey(resourceKey); } + public SnapshotDto getLastSnapshotByResourceId(long resourceId, SqlSession session) { + return session.getMapper(ResourceMapper.class).selectLastSnapshotByResourceId(resourceId); + } + public List getDescendantProjects(long projectId) { SqlSession session = mybatis.openSession(); try { diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java index 05b8ba5d53d..68ac9a3af59 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java @@ -26,7 +26,9 @@ import java.util.List; public interface ResourceMapper { SnapshotDto selectSnapshot(Long snapshotId); - SnapshotDto selectLastSnapshotByKey(String resourceKey); + SnapshotDto selectLastSnapshotByResourceKey(String resourceKey); + + SnapshotDto selectLastSnapshotByResourceId(long resourceId); ResourceDto selectResource(long id); diff --git a/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml b/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml index 0cc52cbbdf8..044da26c78f 100644 --- a/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml @@ -83,6 +83,10 @@ select s.* from snapshots s, projects p where p.kee=#{id} and p.enabled=${_true} and p.copy_resource_id is null and s.islast=${_true} and p.id=s.project_id + + diff --git a/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterExecutorTest.java b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterExecutorTest.java index d71a364c238..a755be6cdbe 100644 --- a/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterExecutorTest.java +++ b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterExecutorTest.java @@ -75,7 +75,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void filter_is_not_valid_if_condition_on_unknown_metric() { MeasureFilterContext context = new MeasureFilterContext(); - MeasureFilter filter = new MeasureFilter().addCondition(new MeasureFilterCondition(null, "<", 3.0)); + MeasureFilter filter = new MeasureFilter().addCondition(new MeasureFilterCondition(null, MeasureFilterCondition.Operator.LESS, 3.0)); assertThat(MeasureFilterExecutor.isValid(filter, context)).isFalse(); } @@ -263,7 +263,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { public void condition_on_numeric_measure() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")) .setSortOnMetric(METRIC_LINES) - .addCondition(new MeasureFilterCondition(METRIC_LINES, ">", 200)); + .addCondition(new MeasureFilterCondition(METRIC_LINES, MeasureFilterCondition.Operator.GREATER, 200)); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1); @@ -274,7 +274,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { public void condition_on_measure_variation() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")) .setSortOnMetric(METRIC_LINES) - .addCondition(new MeasureFilterCondition(METRIC_LINES, ">", 1000).setPeriod(5)); + .addCondition(new MeasureFilterCondition(METRIC_LINES, MeasureFilterCondition.Operator.GREATER, 1000).setPeriod(5)); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1); @@ -285,8 +285,8 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { public void multiple_conditions_on_numeric_measures() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")) .setSortOnMetric(METRIC_LINES) - .addCondition(new MeasureFilterCondition(METRIC_LINES, ">", 2)) - .addCondition(new MeasureFilterCondition(METRIC_LINES, "<=", 50)); + .addCondition(new MeasureFilterCondition(METRIC_LINES, MeasureFilterCondition.Operator.GREATER, 2)) + .addCondition(new MeasureFilterCondition(METRIC_LINES, MeasureFilterCondition.Operator.LESS_OR_EQUALS, 50)); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1); diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb index bb3202ce1c0..9898d17773d 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb @@ -56,7 +56,7 @@ module MeasuresHelper elsif column.key=='version' h result.snapshot.version elsif column.key=='language' - h result.snapshot.resource.language + Api::Utils.language_name(result.snapshot.resource.language) elsif column.key=='links' && result.links html = '' result.links.select { |link| link.href.start_with?('http') }.each do |link| diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb index 9ea3e4fe2a0..9095a567eec 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb @@ -170,6 +170,14 @@ class MeasureFilter < ActiveRecord::Base criteria.merge({'id' => self.id}) end + def base_resource + if criteria('base') + Project.find(:first, :conditions => ['kee=? and copy_resource_id is null and person_id is null', criteria('base')]) + elsif criteria('baseId') + Project.find(criteria('baseId')) + end + end + private def init_results @@ -233,8 +241,8 @@ class MeasureFilter < ActiveRecord::Base end end end - if criteria['base'].present? - base_snapshot = Snapshot.find(:first, :include => 'project', :conditions => ['projects.kee=? and islast=?', criteria['base'], true]) + if base_resource + base_snapshot = base_resource.last_snapshot if base_snapshot @base_result = Result.new(base_snapshot) unless metric_ids.empty? diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb index 322041be554..01010c6562e 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb @@ -2,11 +2,14 @@ display_favourites = logged_in? colspan = @filter.display.columns.size colspan += 1 if display_favourites + table_columns = [] + table_columns << '' if display_favourites + table_columns.concat(@filter.display.columns.map{|c| c.key}) if edit_mode content_for :script do %> @@ -67,7 +70,7 @@ :html_id => 'select-metric', :allow_empty => true, :key_prefix => 'metric:', - :extra_values => [['Name', 'name'], ['Short Name', 'short_name'], ['Description', 'description'], ['Version', 'version']] -%> + :extra_values => [[message('measure_filter.col.name'), 'name'], [message('measure_filter.col.short_name'), 'short_name'], [message('measure_filter.col.description'), 'description'], [message('measure_filter.col.language'), 'language'], [message('measure_filter.col.version'), 'version']] -%> + <%= resource_select_tag 'baseId', :resource_type_property => 'supportsGlobalDashboards', :width => '100%', :selected_resource => @filter.base_resource -%> @@ -119,20 +119,44 @@ - Name:
+ <%= message('measure_filter.name_contains') -%>:
- Key:
+ <%= message('measure_filter.key_like') -%>:
- Favourites only:
+ <%= message 'measure_filter.only_favourites' %>:
<%= check_box_tag 'onFavourites', 'true', @filter.criteria['onFavourites']=='true' -%> + + <% condition_metrics = Metric.all.select { |m| m.numeric? } %> + + +
+ <%= metric_select_tag 'c1_metric', condition_metrics, :allow_empty => true, :selected_key => @filter.criteria('c1_metric'), :width => '180px' -%> + <%= select_tag 'c1_period', options_for_select([['Value', ''], ['Period 1', '1'], ['Period 2', '2'], ['Period 3', '3']], @filter.criteria('c1_period')) -%> + <%= select_tag 'c1_op', options_for_select([['Equals', 'eq'], ['Less than', 'lt'], ['Less or equals', 'lte'], ['Greater than', 'gt'], ['Greater or equals', 'gte']], @filter.criteria('c1_op')) -%> + +

+ + + + + +
+ <%= metric_select_tag 'c2_metric', condition_metrics, :allow_empty => true, :selected_key => @filter.criteria('c2_metric'), :width => '180px' -%> + <%= select_tag 'c2_period', options_for_select([['Value', ''], ['Period 1', '1'], ['Period 2', '2'], ['Period 3', '3']], @filter.criteria('c2_period')) -%> + <%= select_tag 'c2_op', options_for_select([['Equals', 'eq'], ['Less than', 'lt'], ['Less or equals', 'lte'], ['Greater than', 'gt'], ['Greater or equals', 'gte']], @filter.criteria('c2_op')) -%> + +

+ + + From date:
@@ -169,7 +193,8 @@ <% if @filter.results && @filter.display %> -
+
+ <% if @filter.name %>

<%= h @filter.name -%> @@ -177,32 +202,36 @@ - <%= h @filter.description -%> <% end %>

- <% end %> <% edit_mode = (params[:edit]=='true') unless edit_mode %> - Display as: - <% MeasureFilterDisplay.keys.each do |display_key| %> - <%= link_to_if display_key!=@filter.display.key, display_key, params.merge(:action => 'search', :display => display_key, :id => @filter.id) -%> - <% end %> - <%= message('configure') -%> - <% if logged_in? %> - <% if @filter.id==nil || @filter.user_id==current_user.id %> - <%= message('save') -%> - <% end %> - <%= message('copy') -%> - <% end %> + + + +
+ Display as: + <% MeasureFilterDisplay.keys.each do |display_key| %> + <%= link_to_if display_key!=@filter.display.key, display_key, params.merge(:action => 'search', :display => display_key, :id => @filter.id) -%> + <% end %> + + <%= message('configure') -%> + <% if logged_in? %> + <% if @filter.id==nil || @filter.user_id==current_user.id %> + <%= message('save') -%> + <% end %> + <% if @filter.id %> + <%= message('copy') -%> + <% end %> + <% end %> +
<% end %> - <%= render :partial => "measures/display_#{@filter.display.class::KEY}", :locals => {:edit_mode => edit_mode} -%> -
- <% if @filter.security_exclusions %>

<%= message('results_not_display_due_to_security') -%>

<% end %> - + <%= render :partial => "measures/display_#{@filter.display.class::KEY}", :locals => {:edit_mode => edit_mode} -%>
<% end %> diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/357_move_existing_measure_filters.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/357_move_existing_measure_filters.rb index a1a5b1b8f53..ca5b45c4422 100644 --- a/sonar-server/src/main/webapp/WEB-INF/db/migrate/357_move_existing_measure_filters.rb +++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/357_move_existing_measure_filters.rb @@ -61,11 +61,9 @@ class MoveExistingMeasureFilters < ActiveRecord::Migration new_filter.shared = (old_filter.shared || old_filter.user_id.nil?) data = [] data << 'onFavourites=true' if old_filter.favourites - if old_filter.resource_id - resource = Project.find(:first, :conditions => {:id => old_filter.id}) - data << "base=#{resource.kee}" if resource - end + data << "baseId=#{old_filter.resource_id}" if old_filter.resource_id data << "pageSize=#{old_filter.page_size}" if old_filter.page_size + data << "display=#{old_filter.default_value || 'list'}" columns = [] asc = nil diff --git a/sonar-server/src/main/webapp/stylesheets/layout.css b/sonar-server/src/main/webapp/stylesheets/layout.css index bbfe1cd935c..caebd92a378 100644 --- a/sonar-server/src/main/webapp/stylesheets/layout.css +++ b/sonar-server/src/main/webapp/stylesheets/layout.css @@ -319,4 +319,5 @@ body, a { display: table-cell; margin: 0; vertical-align: top; + width: 100%; } \ No newline at end of file -- 2.39.5