aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsimonbrandhof <simon.brandhof@gmail.com>2011-07-14 18:19:37 +0200
committersimonbrandhof <simon.brandhof@gmail.com>2011-07-14 18:19:52 +0200
commit8d542803129dc572828a4a12174c5c4d2c1c81c8 (patch)
treef539237e35b66ce4978e07a130078076c0678c3a
parent75eeadcdaa600a5d2e2729125f7258ebddeb8101 (diff)
downloadsonarqube-8d542803129dc572828a4a12174c5c4d2c1c81c8.tar.gz
sonarqube-8d542803129dc572828a4a12174c5c4d2c1c81c8.zip
SONAR-2610 web service for manual measures + copy manual measures during analysis
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java1
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ManualMeasureDecorator.java69
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ManualMeasureDecoratorTest.java52
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ManualMeasureDecoratorTest/testCopyManualMeasures.xml11
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/entity/ManualMeasure.java78
-rw-r--r--sonar-core/src/main/resources/META-INF/persistence.xml1
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/api/api_controller.rb103
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/api/events_controller.rb2
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/api/manual_measures_controller.rb113
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/manual_measure.rb42
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/project.rb1
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/config/routes.rb1
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/db/migrate/211_create_manual_measures.rb10
13 files changed, 448 insertions, 36 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
index 5b4358eb5cb..807e8834852 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
@@ -214,6 +214,7 @@ public class CorePlugin extends SonarPlugin {
extensions.add(FilesDecorator.class);
extensions.add(CloseReviewsDecorator.class);
extensions.add(ReferenceAnalysis.class);
+ extensions.add(ManualMeasureDecorator.class);
// time machine
extensions.add(TendencyDecorator.class);
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ManualMeasureDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ManualMeasureDecorator.java
new file mode 100644
index 00000000000..5d26067deee
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ManualMeasureDecorator.java
@@ -0,0 +1,69 @@
+/*
+ * 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.core.sensors;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.Phase;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MetricFinder;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.jpa.entity.ManualMeasure;
+
+import java.util.List;
+
+@Phase(name = Phase.Name.PRE)
+public class ManualMeasureDecorator implements Decorator {
+
+ private DatabaseSession session;
+ private MetricFinder metricFinder;
+
+ public ManualMeasureDecorator(DatabaseSession session, MetricFinder metricFinder) {
+ this.session = session;
+ this.metricFinder = metricFinder;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (resource.getId() != null) {
+ List<ManualMeasure> manualMeasures = session.getResults(ManualMeasure.class, "resourceId", resource.getId());
+ for (ManualMeasure manualMeasure : manualMeasures) {
+ context.saveMeasure(copy(manualMeasure));
+ }
+ }
+ }
+
+ private Measure copy(ManualMeasure manualMeasure) {
+ Measure measure = new Measure(metricFinder.findById(manualMeasure.getMetricId()));
+ measure.setValue(manualMeasure.getValue(), 5);
+ measure.setData(manualMeasure.getTextValue());
+ return measure;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ManualMeasureDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ManualMeasureDecoratorTest.java
new file mode 100644
index 00000000000..4ca1719de50
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ManualMeasureDecoratorTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.core.sensors;
+
+import org.junit.Test;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.JavaFile;
+import org.sonar.api.test.IsMeasure;
+import org.sonar.core.components.DefaultMetricFinder;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class ManualMeasureDecoratorTest extends AbstractDbUnitTestCase {
+
+ private Metric reviewNote = new Metric.Builder("review_note", "Note", Metric.ValueType.FLOAT).create().setId(2);
+
+ @Test
+ public void testCopyManualMeasures() throws Exception {
+ setupData("testCopyManualMeasures");
+
+ JavaFile javaFile = new JavaFile("Foo.java");
+ javaFile.setId(40);
+
+ ManualMeasureDecorator decorator = new ManualMeasureDecorator(getSession(), new DefaultMetricFinder(getSessionFactory()));
+ DecoratorContext context = mock(DecoratorContext.class);
+ decorator.decorate(javaFile, context);
+
+ verify(context).saveMeasure(argThat(new IsMeasure(reviewNote, 6.0, "six")));
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ManualMeasureDecoratorTest/testCopyManualMeasures.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ManualMeasureDecoratorTest/testCopyManualMeasures.xml
new file mode 100644
index 00000000000..9c772983186
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ManualMeasureDecoratorTest/testCopyManualMeasures.xml
@@ -0,0 +1,11 @@
+<dataset>
+ <metrics id="1" NAME="ncloc" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name=""
+ enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+ <metrics id="2" NAME="review_note" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name=""
+ enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+
+
+ <manual_measures id="1" metric_id="2" resource_id="30" value="3.14" text_value="pi" created_at="[null]" updated_at="[null]"/>
+ <manual_measures id="2" metric_id="2" resource_id="40" value="6" text_value="six" created_at="[null]" updated_at="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/jpa/entity/ManualMeasure.java b/sonar-core/src/main/java/org/sonar/jpa/entity/ManualMeasure.java
new file mode 100644
index 00000000000..9f0bbf94ba9
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/entity/ManualMeasure.java
@@ -0,0 +1,78 @@
+/*
+ * 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.jpa.entity;
+
+import javax.persistence.*;
+import java.util.Date;
+
+@Entity
+@Table(name = "manual_measures")
+public final class ManualMeasure {
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Long id;
+
+ @Column(name = "value", updatable = true, nullable = true, precision = 30, scale = 20)
+ private Double value = null;
+
+ @Column(name = "text_value", updatable = true, nullable = true, length = 4000)
+ private String textValue;
+
+ @Column(name = "metric_id", updatable = false, nullable = false)
+ private Integer metricId;
+
+ @Column(name = "resource_id", updatable = true, nullable = true)
+ private Integer resourceId;
+
+ @Column(name = "created_at", updatable = true, nullable = true)
+ private Date createdAt;
+
+ @Column(name = "updated_at", updatable = true, nullable = true)
+ private Date updatedAt;
+
+ public Long getId() {
+ return id;
+ }
+
+ public Double getValue() {
+ return value;
+ }
+
+ public String getTextValue() {
+ return textValue;
+ }
+
+ public Integer getMetricId() {
+ return metricId;
+ }
+
+ public Integer getResourceId() {
+ return resourceId;
+ }
+
+ public Date getCreatedAt() {
+ return createdAt;
+ }
+
+ public Date getUpdatedAt() {
+ return updatedAt;
+ }
+}
diff --git a/sonar-core/src/main/resources/META-INF/persistence.xml b/sonar-core/src/main/resources/META-INF/persistence.xml
index a4a8d36b3e4..cb768e0e986 100644
--- a/sonar-core/src/main/resources/META-INF/persistence.xml
+++ b/sonar-core/src/main/resources/META-INF/persistence.xml
@@ -7,6 +7,7 @@
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.sonar.jpa.entity.SchemaMigration</class>
+ <class>org.sonar.jpa.entity.ManualMeasure</class>
<class>org.sonar.api.database.configuration.Property</class>
<class>org.sonar.api.qualitymodel.Model</class>
<class>org.sonar.api.qualitymodel.Characteristic</class>
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/api_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/api_controller.rb
index 594635c62a1..f1e0100884c 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/api_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/api_controller.rb
@@ -20,42 +20,93 @@
require 'json'
require 'time'
class Api::ApiController < ApplicationController
-
- protected
-
+
+ class ApiException < Exception
+ attr_reader :code, :msg
+
+ def initialize(code, msg)
+ @code = code
+ @msg = msg
+ end
+ end
+
+ rescue_from ApiException do |exception|
+ render_error(exception.msg, exception.code)
+ end
+
+ rescue_from ActiveRecord::RecordInvalid do |exception|
+ render_error(exception.message, 400)
+ end
+
+ rescue_from ActiveRecord::RecordNotFound do |exception|
+ render_error(exception.message, 404)
+ end
+
+ protected
+
def text_not_supported
"Not supported"
end
-
+
def xml_not_supported
xml = Builder::XmlMarkup.new(:indent => 0)
xml.instruct!
xml.not_supported
end
-
+
def json_not_supported
JSON({:not_supported => true})
end
-
+
def jsonp(json)
- text=( (json.is_a? String) ? json : JSON(json))
-
+ text=((json.is_a? String) ? json : JSON(json))
+
if params['callback']
- params['callback'] + '(' + text + ');'
+ params['callback'] + '(' + text + ');'
else
text
end
end
-
+
+ # deprecated. Use Api::Utils.format_datetime
+ def format_datetime(datetime)
+ Api::Utils.format_datetime(datetime)
+ end
+
+ # deprecated. Use Api::Utils.parse_datetime
+ def parse_datetime(datetime_string, default_is_now=true)
+ Api::Utils.parse_datetime(datetime_string, default_is_now)
+ end
+
+ def load_resource(resource_key, role=nil)
+ resource=Project.by_key(resource_key)
+ not_found("Resource not found: #{resource_key}") if resource.nil?
+ access_denied if role && !has_role?(role, resource)
+ resource
+ end
+
+
+
+ #----------------------------------------------------------------------------
+ # ERRORS
+ #----------------------------------------------------------------------------
+ def not_found(message)
+ raise ApiException.new(404, message)
+ end
+
+ def bad_request(message)
+ raise ApiException.new(400, message)
+ end
+
def access_denied
- render_error('Unauthorized', 401)
+ raise ApiException.new(401, 'Unauthorized')
end
-
+
def render_error(msg, http_status=400)
- respond_to do |format|
- format.json{ render :json => error_to_json(msg, http_status), :status => http_status }
- format.xml{ render :xml => error_to_xml(msg, http_status), :status => http_status}
- format.text{ render :text => msg, :status => http_status }
+ respond_to do |format|
+ format.json { render :json => error_to_json(msg, http_status), :status => http_status }
+ format.xml { render :xml => error_to_xml(msg, http_status), :status => http_status }
+ format.text { render :text => msg, :status => http_status }
end
end
@@ -68,7 +119,7 @@ class Api::ApiController < ApplicationController
def error_to_xml(msg, error_code=nil)
xml = Builder::XmlMarkup.new(:indent => 0)
- xml.error do
+ xml.error do
xml.code(error_code) if error_code
xml.msg(msg) if msg
end
@@ -78,23 +129,5 @@ class Api::ApiController < ApplicationController
render_error(msg, 200)
end
- # deprecated. Use Api::Utils.format_datetime
- def format_datetime(datetime)
- Api::Utils.format_datetime(datetime)
- end
-
- # deprecated. Use Api::Utils.parse_datetime
- def parse_datetime(datetime_string, default_is_now=true)
- Api::Utils.parse_datetime(datetime_string, default_is_now)
- end
-
- class ApiException < Exception
- attr_reader :code, :msg
- def initialize(code, msg)
- @code = code
- @msg = msg
- end
- end
-
end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/events_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/events_controller.rb
index afc773baa48..8f842296cb4 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/events_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/events_controller.rb
@@ -43,6 +43,7 @@ class Api::EventsController < Api::ApiController
conditions<<'resource_id IS NULL'
end
+ from=nil
if params[:fromDateTime]
from=parse_datetime(params[:fromDateTime], false)
elsif params[:fromDate]
@@ -53,6 +54,7 @@ class Api::EventsController < Api::ApiController
values[:from]=from
end
+ to=nil
if params[:toDateTime]
to=parse_datetime(params[:toDateTime], false)
elsif params[:toDate]
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/manual_measures_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/manual_measures_controller.rb
new file mode 100644
index 00000000000..e5ad10169e6
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/manual_measures_controller.rb
@@ -0,0 +1,113 @@
+#
+# Sonar, entreprise quality control 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
+#
+
+require 'json'
+
+class Api::ManualMeasuresController < Api::ApiController
+
+ #
+ # GET /api/manual_measures?resource=<resource>&metric=<optional metric>
+ #
+ def index
+ resource=load_resource(params[:resource], :user)
+
+ metric=nil
+ if params[:metric].present?
+ metric=Metric.by_key(params[:metric])
+ bad_request("Unknown metric: #{params[:metric]}") if metric.nil?
+ end
+
+ result = resource.manual_measures
+ if metric
+ result = result.select{|m| m.metric_id==metric.id}
+ end
+
+ respond_to do |format|
+ format.json { render :json => jsonp(manual_measures_to_json(result)) }
+ format.xml { render :xml => xml_not_supported }
+ end
+ end
+
+ #
+ # POST /api/manual_measures?resource=<resource>&metric=<metric>&val=<optional decimal value>&text=<optional text>
+ # Create or update measure.
+ #
+ def create
+ resource=load_resource(params[:resource], :admin)
+
+ metric=Metric.by_key(params[:metric])
+ bad_request("Unknown metric: #{params[:metric]}") if metric.nil?
+
+ value=params[:val]
+ bad_request("Not a numeric value: #{value}") if value && !Api::Utils.is_number?(value)
+
+ measure=ManualMeasure.find(:first, :conditions => ['resource_id=? and metric_id=?', resource.id, metric.id])
+ if measure.nil?
+ measure=ManualMeasure.new(:resource => resource, :user => current_user, :metric_id => metric.id)
+ end
+
+ measure.value = value
+ measure.text_value = params[:text]
+ measure.save!
+
+ respond_to do |format|
+ format.json { render :json => jsonp(manual_measure_to_json(measure)) }
+ format.xml { render :xml => xml_not_supported }
+ end
+ end
+
+
+ #
+ # DELETE /api/manual_measures?resource=<resource>&metric=<metric>
+ #
+ def destroy
+ resource=load_resource(params[:resource], :admin)
+
+ metric=Metric.by_key(params[:metric])
+ bad_request("Unknown metric: #{params[:metric]}") if metric.nil?
+
+ count = ManualMeasure.delete_all(['resource_id=? and metric_id=?', resource.id, metric.id])
+
+ render_success "Deleted #{count} measures"
+ end
+
+ private
+
+ def manual_measures_to_json(manual_measures)
+ json = []
+ manual_measures.each do |m|
+ json<<manual_measure_to_json(m)
+ end
+ json
+ end
+
+ def manual_measure_to_json(manual_measure)
+ hash={:id => manual_measure.id, :metric => manual_measure.metric.key}
+ hash[:val]=manual_measure.value if manual_measure.value
+ hash[:text]=manual_measure.text_value if manual_measure.text_value
+ hash[:created_at]=format_datetime(manual_measure.created_at)
+ hash[:updated_at]=format_datetime(manual_measure.updated_at) if manual_measure.updated_at
+ if manual_measure.user
+ hash[:login]=manual_measure.user.login
+ hash[:username]=manual_measure.user.name
+ end
+ hash
+ end
+end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/manual_measure.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/manual_measure.rb
new file mode 100644
index 00000000000..b458c336e2a
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/models/manual_measure.rb
@@ -0,0 +1,42 @@
+#
+# Sonar, entreprise quality control 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
+#
+class ManualMeasure < ActiveRecord::Base
+ belongs_to :resource, :class_name => 'Project'
+ belongs_to :user
+ validates_uniqueness_of :metric_id, :scope => :resource_id
+ validates_length_of :text_value, :maximum => 4000, :allow_nil => true, :allow_blank => true
+ validate :validate_metric
+
+ def metric
+ @metric ||=
+ begin
+ Metric.by_id(metric_id)
+ end
+ end
+
+ def metric=(m)
+ @metric = m
+ write_attribute(:metric_id, m.id) if m.id
+ end
+
+ def validate_metric
+ errors.add_to_base("Not a valid metric") unless metric && metric.enabled?
+ end
+end \ No newline at end of file
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 9658c9dfcb3..5c7273c0296 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
@@ -28,6 +28,7 @@ class Project < ActiveRecord::Base
belongs_to :profile, :class_name => 'Profile', :foreign_key => 'profile_id'
has_many :user_roles, :foreign_key => 'resource_id'
has_many :group_roles, :foreign_key => 'resource_id'
+ has_many :manual_measures, :foreign_key => 'resource_id'
belongs_to :root, :class_name => 'Project', :foreign_key => 'root_id'
def self.by_key(k)
diff --git a/sonar-server/src/main/webapp/WEB-INF/config/routes.rb b/sonar-server/src/main/webapp/WEB-INF/config/routes.rb
index ba3c53ba26b..e0e0afe2416 100644
--- a/sonar-server/src/main/webapp/WEB-INF/config/routes.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/config/routes.rb
@@ -11,6 +11,7 @@ ActionController::Routing::Routes.draw do |map|
api.resources :events, :only => [:index, :show, :create, :destroy]
api.resources :user_properties, :only => [:index, :show, :create, :destroy], :requirements => { :id => /.*/ }
api.resources :favorites, :only => [:index, :show, :create, :destroy], :requirements => { :id => /.*/ }
+ api.resources :manual_measures, :only => [:index, :create, :destroy], :requirements => { :id => /.*/ }
api.resources :reviews, :only => [:index, :show, :create], :member => {
:add_comment => :put,
:reassign => :put,
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/211_create_manual_measures.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/211_create_manual_measures.rb
index 72e065fc238..9a10482fa55 100644
--- a/sonar-server/src/main/webapp/WEB-INF/db/migrate/211_create_manual_measures.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/211_create_manual_measures.rb
@@ -24,7 +24,15 @@
class CreateManualMeasures < ActiveRecord::Migration
def self.up
- # TODO
+ create_table 'manual_measures' do |t|
+ t.column 'metric_id', :integer, :null => false
+ t.column 'resource_id', :integer, :null => true
+ t.column 'value', :decimal, :null => true, :precision => 30, :scale => 20
+ t.column 'text_value', :string, :null => true, :limit => 4000
+ t.column 'user_id', :integer, :null => true
+ t.timestamps
+ end
+ alter_to_big_primary_key('manual_measures')
end
end