]> source.dussan.org Git - sonarqube.git/commitdiff
WS api/metrics/list - SONAR-6570
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Fri, 22 May 2015 14:57:17 +0000 (16:57 +0200)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Tue, 26 May 2015 11:43:06 +0000 (13:43 +0200)
12 files changed:
server/sonar-server/src/main/java/org/sonar/server/measure/persistence/MetricDao.java
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ListAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/measure/ws/MetricsWs.java
server/sonar-server/src/main/java/org/sonar/server/measure/ws/MetricsWsAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
server/sonar-server/src/main/resources/org/sonar/server/measure/ws/example-list.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/measure/ws/ListActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/measure/ws/MetricTesting.java [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/measure/ws/ListActionTest/list_metrics.json [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/measure/db/MetricMapper.java
sonar-core/src/main/resources/org/sonar/core/measure/db/MetricMapper.xml
sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java

index 97758e56759dd0cbaf43f78b98edac9c0615c1c2..32bf681229d179d4cf0d8ab61af4e8489d3266ff 100644 (file)
 
 package org.sonar.server.measure.persistence;
 
+import com.google.common.collect.Maps;
+import org.apache.ibatis.session.RowBounds;
 import org.sonar.api.server.ServerSide;
 import org.sonar.core.measure.db.MetricDto;
 import org.sonar.core.measure.db.MetricMapper;
 import org.sonar.core.persistence.DaoComponent;
 import org.sonar.core.persistence.DbSession;
+import org.sonar.server.es.SearchOptions;
 
 import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 
 import java.util.List;
+import java.util.Map;
 
 @ServerSide
 public class MetricDao implements DaoComponent {
@@ -42,6 +47,15 @@ public class MetricDao implements DaoComponent {
     return session.getMapper(MetricMapper.class).selectAllEnabled();
   }
 
+  public List<MetricDto> selectEnabled(DbSession session, @Nullable Boolean isCustom, SearchOptions searchOptions) {
+    Map<String, Object> properties = Maps.newHashMapWithExpectedSize(1);
+    if (isCustom != null) {
+      properties.put("isCustom", isCustom);
+    }
+
+    return session.getMapper(MetricMapper.class).selectAllEnabled(properties, new RowBounds(searchOptions.getOffset(), searchOptions.getLimit()));
+  }
+
   public void insert(DbSession session, MetricDto dto) {
     session.getMapper(MetricMapper.class).insert(dto);
   }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ListAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ListAction.java
new file mode 100644 (file)
index 0000000..9ba67c1
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.measure.ws;
+
+import org.sonar.api.measures.Metric;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.measure.db.MetricDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.es.SearchOptions;
+
+import javax.annotation.Nullable;
+
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newHashSet;
+
+public class ListAction implements MetricsWsAction {
+
+  public static final String PARAM_IS_CUSTOM = "is_custom";
+
+  public static final String FIELD_ID = "id";
+  public static final String FIELD_KEY = "key";
+  public static final String FIELD_NAME = "name";
+  public static final String FIELD_DESCRIPTION = "description";
+  public static final String FIELD_DOMAIN = "domain";
+  public static final String FIELD_TYPE = "type";
+  private static final Set<String> POSSIBLE_FIELDS = newHashSet(FIELD_ID, FIELD_KEY, FIELD_NAME, FIELD_DESCRIPTION, FIELD_DOMAIN, FIELD_TYPE);
+
+  private final DbClient dbClient;
+
+  public ListAction(DbClient dbClient) {
+    this.dbClient = dbClient;
+  }
+
+  @Override
+  public void define(WebService.NewController context) {
+    WebService.NewAction action = context.createAction("list")
+      .setSince("5.2")
+      .setResponseExample(getClass().getResource("example-list.json"))
+      .addPagingParams(100)
+      .addFieldsParam(POSSIBLE_FIELDS)
+      .setHandler(this);
+
+    action.createParam(PARAM_IS_CUSTOM)
+      .setExampleValue("true")
+      .setDescription("Choose custom metrics following 3 cases:" +
+        "<ul>" +
+        "<li>true: only custom metrics are returned</li>" +
+        "<li>false: only non custom metrics are returned</li>" +
+        "<li>not specified: all metrics are returned</li>" +
+        "</ul>");
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    SearchOptions searchOptions = new SearchOptions()
+      .setPage(request.mandatoryParamAsInt(Param.PAGE),
+        request.mandatoryParamAsInt(Param.PAGE_SIZE));
+    Boolean isCustom = request.paramAsBoolean(PARAM_IS_CUSTOM);
+    DbSession dbSession = dbClient.openSession(false);
+    try {
+      List<MetricDto> metrics = dbClient.metricDao().selectEnabled(dbSession, isCustom, searchOptions);
+      JsonWriter json = response.newJsonWriter();
+      json.beginObject();
+      Set<String> desiredFields = desiredFields(request.paramAsStrings(Param.FIELDS));
+      writeMetrics(json, metrics, desiredFields);
+      json.endObject();
+      json.close();
+    } finally {
+      MyBatis.closeQuietly(dbSession);
+    }
+  }
+
+  private Set<String> desiredFields(@Nullable List<String> fields) {
+    if (fields == null || fields.isEmpty()) {
+      return POSSIBLE_FIELDS;
+    }
+
+    return newHashSet(fields);
+  }
+
+  private void writeMetrics(JsonWriter json, List<MetricDto> metrics, Set<String> desiredFields) {
+    json.name("metrics");
+    json.beginArray();
+    for (MetricDto metric : metrics) {
+      json.beginObject();
+      json.prop(FIELD_ID, String.valueOf(metric.getId()));
+      json.prop(FIELD_KEY, metric.getKey());
+      writeIfDesired(json, FIELD_NAME, metric.getShortName(), desiredFields);
+      writeIfDesired(json, FIELD_DESCRIPTION, metric.getDescription(), desiredFields);
+      writeIfDesired(json, FIELD_DOMAIN, metric.getDomain(), desiredFields);
+      writeIfDesired(json, FIELD_TYPE, Metric.ValueType.descriptionOf(metric.getValueType()), desiredFields);
+      json.endObject();
+    }
+    json.endArray();
+  }
+
+  private void writeIfDesired(JsonWriter json, String field, String value, Set<String> desiredFields) {
+    if (desiredFields.contains(field)) {
+      json.prop(field, value);
+    }
+  }
+}
index ed34355709bcfb7d4c047f38d78f1840fa6ceafd..2f352ef3bc6a5117f3944f4a82db4f6e2ea83ecd 100644 (file)
@@ -25,12 +25,21 @@ import org.sonar.api.server.ws.WebService;
 
 public class MetricsWs implements WebService {
 
+  private final MetricsWsAction[] actions;
+
+  public MetricsWs(MetricsWsAction... actions) {
+    this.actions = actions;
+  }
+
   @Override
   public void define(Context context) {
     NewController controller = context.createController("api/metrics");
     controller.setDescription("Metrics management");
     controller.setSince("2.6");
 
+    for (MetricsWsAction action : actions) {
+      action.define(controller);
+    }
     defineIndexAction(controller);
 
     controller.done();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MetricsWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MetricsWsAction.java
new file mode 100644 (file)
index 0000000..61b28ba
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.measure.ws;
+
+import org.sonar.server.ws.WsAction;
+
+public interface MetricsWsAction extends WsAction {
+  // marker interface
+}
index a09daf2b385c7fc8c2b4fa2047491062b1dfd833..a7af1692d9672822af58461906e433b33724b9a0 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.server.platform.platformlevel;
 
-import java.util.List;
 import org.sonar.api.config.EmailSettings;
 import org.sonar.api.issue.action.Actions;
 import org.sonar.api.profiles.AnnotationProfileParser;
@@ -291,6 +290,8 @@ import org.sonar.server.view.index.ViewIndexer;
 import org.sonar.server.ws.ListingWs;
 import org.sonar.server.ws.WebServiceEngine;
 
+import java.util.List;
+
 public class PlatformLevel4 extends PlatformLevel {
 
   private final List<Object> level4AddedComponents;
@@ -431,6 +432,7 @@ public class PlatformLevel4 extends PlatformLevel {
       TimeMachineWs.class,
       ManualMeasuresWs.class,
       MetricsWs.class,
+      org.sonar.server.measure.ws.ListAction.class,
 
       // quality gates
       QualityGateDao.class,
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/measure/ws/example-list.json b/server/sonar-server/src/main/resources/org/sonar/server/measure/ws/example-list.json
new file mode 100644 (file)
index 0000000..3a7a4dd
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "metrics": [
+    {
+      "id": "23",
+      "key": "team_size",
+      "name": "Team size",
+      "description": "Number of people in the team",
+      "domain": "Management",
+      "type": "INT"
+    },
+    {
+      "id": "2",
+      "key": "uncovered_lines",
+      "name": "Uncovered lines",
+      "description": "Uncovered lines",
+      "domain": "Tests",
+      "type": "INT"
+    }
+  ],
+  "total": 2,
+  "p": 1,
+  "ps": 100
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ListActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ListActionTest.java
new file mode 100644 (file)
index 0000000..4bbcb9b
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.measure.ws;
+
+import org.apache.commons.lang.StringUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.core.measure.db.MetricDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.measure.persistence.MetricDao;
+import org.sonar.server.ws.WsTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.measure.ws.ListAction.PARAM_IS_CUSTOM;
+
+public class ListActionTest {
+
+  @ClassRule
+  public static DbTester db = new DbTester();
+  DbClient dbClient;
+  DbSession dbSession;
+  WsTester ws;
+
+  @Before
+  public void setUp() {
+    dbClient = new DbClient(db.database(), db.myBatis(), new MetricDao());
+    dbSession = dbClient.openSession(false);
+    ws = new WsTester(new MetricsWs(new ListAction(dbClient)));
+    db.truncateTables();
+  }
+
+  @After
+  public void tearDown() {
+    dbSession.close();
+  }
+
+  @Test
+  public void list_metrics_in_database() throws Exception {
+    insertNewCustomMetric("1", "2", "3");
+
+    WsTester.Result result = newRequest().execute();
+
+    result.assertJson(getClass(), "list_metrics.json");
+  }
+
+  @Test
+  public void list_metrics_ordered_by_name_case_insensitive() throws Exception {
+    insertNewCustomMetric("3", "1", "2");
+
+    String firstResult = newRequest().setParam(Param.PAGE, "1").setParam(Param.PAGE_SIZE, "1").execute().outputAsString();
+    String secondResult = newRequest().setParam(Param.PAGE, "2").setParam(Param.PAGE_SIZE, "1").execute().outputAsString();
+    String thirdResult = newRequest().setParam(Param.PAGE, "3").setParam(Param.PAGE_SIZE, "1").execute().outputAsString();
+
+    assertThat(firstResult).contains("custom-key-1").doesNotContain("custom-key-2").doesNotContain("custom-key-3");
+    assertThat(secondResult).contains("custom-key-2").doesNotContain("custom-key-1").doesNotContain("custom-key-3");
+    assertThat(thirdResult).contains("custom-key-3").doesNotContain("custom-key-1").doesNotContain("custom-key-2");
+  }
+
+  @Test
+  public void list_metrics_with_pagination() throws Exception {
+    insertNewCustomMetric("1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
+
+    WsTester.Result result = newRequest()
+      .setParam(Param.PAGE, "3")
+      .setParam(Param.PAGE_SIZE, "4")
+      .execute();
+
+    assertThat(StringUtils.countMatches(result.outputAsString(), "custom-key")).isEqualTo(2);
+  }
+
+  @Test
+  public void list_metric_with_is_custom_true() throws Exception {
+    insertNewCustomMetric("1", "2");
+    insertNewNonCustomMetric("3");
+
+    String result = newRequest()
+      .setParam(PARAM_IS_CUSTOM, "true").execute().outputAsString();
+
+    assertThat(result).contains("custom-key-1", "custom-key-2")
+      .doesNotContain("non-custom-key-3");
+  }
+
+  @Test
+  public void list_metric_with_is_custom_false() throws Exception {
+    insertNewCustomMetric("1", "2");
+    insertNewNonCustomMetric("3");
+
+    String result = newRequest()
+      .setParam(PARAM_IS_CUSTOM, "false").execute().outputAsString();
+
+    assertThat(result).doesNotContain("custom-key-1")
+      .doesNotContain("custom-key-2")
+      .contains("non-custom-key-3");
+  }
+
+  @Test
+  public void list_metric_with_chosen_fields() throws Exception {
+    insertNewCustomMetric("1");
+
+    String result = newRequest().setParam(Param.FIELDS, "name").execute().outputAsString();
+
+    assertThat(result).contains("id", "key", "name")
+      .doesNotContain("domain")
+      .doesNotContain("description")
+      .doesNotContain("type");
+  }
+
+  private void insertNewNonCustomMetric(String... ids) {
+    for (String id : ids) {
+      dbClient.metricDao().insert(dbSession, MetricTesting.newDto()
+        .setKey("non-custom-key-" + id)
+        .setEnabled(true)
+        .setUserManaged(false));
+    }
+    dbSession.commit();
+  }
+
+  private void insertNewCustomMetric(String... ids) {
+    for (String id : ids) {
+      dbClient.metricDao().insert(dbSession, newCustomMetric(id));
+    }
+    dbSession.commit();
+  }
+
+  private MetricDto newCustomMetric(String id) {
+    return MetricTesting.newDto()
+      .setKey("custom-key-" + id)
+      .setShortName("custom-name-" + id)
+      .setDomain("custom-domain-" + id)
+      .setDescription("custom-description-" + id)
+      .setValueType("INT")
+      .setUserManaged(true)
+      .setEnabled(true);
+  }
+
+  private WsTester.TestRequest newRequest() {
+    return ws.newGetRequest("api/metrics", "list");
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/MetricTesting.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/MetricTesting.java
new file mode 100644 (file)
index 0000000..753faf4
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.measure.ws;
+
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.commons.lang.math.RandomUtils;
+import org.sonar.api.measures.Metric;
+import org.sonar.core.measure.db.MetricDto;
+
+public class MetricTesting {
+  private MetricTesting() {
+    // static stuff only
+  }
+
+  public static MetricDto newDto() {
+    Metric.ValueType[] metricTypes = Metric.ValueType.values();
+    return new MetricDto()
+      .setId(RandomUtils.nextInt())
+      .setKey(RandomStringUtils.randomAlphanumeric(64))
+      .setShortName(RandomStringUtils.randomAlphanumeric(64))
+      .setValueType(metricTypes[RandomUtils.nextInt(metricTypes.length - 1)].name())
+      .setDomain(RandomStringUtils.randomAlphanumeric(64))
+      .setDescription(RandomStringUtils.randomAlphanumeric(250))
+      .setBestValue(RandomUtils.nextDouble())
+      .setDeleteHistoricalData(RandomUtils.nextBoolean())
+      .setDirection(RandomUtils.nextInt())
+      .setHidden(RandomUtils.nextBoolean())
+      .setEnabled(RandomUtils.nextBoolean())
+      .setOptimizedBestValue(RandomUtils.nextBoolean())
+      .setQualitative(RandomUtils.nextBoolean())
+      .setOrigin(RandomStringUtils.randomAlphabetic(3))
+      .setUserManaged(RandomUtils.nextBoolean())
+      .setWorstValue(RandomUtils.nextDouble());
+  }
+
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/measure/ws/ListActionTest/list_metrics.json b/server/sonar-server/src/test/resources/org/sonar/server/measure/ws/ListActionTest/list_metrics.json
new file mode 100644 (file)
index 0000000..4f5049c
--- /dev/null
@@ -0,0 +1,25 @@
+{
+  "metrics":[
+    {
+      "key":"custom-key-1",
+      "name":"custom-name-1",
+      "type":"Integer",
+      "domain":"custom-domain-1",
+      "description":"custom-description-1"
+    },
+    {
+      "key":"custom-key-2",
+      "name":"custom-name-2",
+      "type":"Integer",
+      "domain":"custom-domain-2",
+      "description":"custom-description-2"
+    },
+    {
+      "key":"custom-key-3",
+      "name":"custom-name-3",
+      "type":"Integer",
+      "domain":"custom-domain-3",
+      "description":"custom-description-3"
+    }
+  ]
+}
index 4ecec87bccbaad06fee98113cf9758522b51e65a..16b737866fc6dde3bb2ba55a8b5ac26c949ae5d8 100644 (file)
 package org.sonar.core.measure.db;
 
 import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.session.RowBounds;
 
 import java.util.List;
+import java.util.Map;
 
 public interface MetricMapper {
 
   MetricDto selectByKey(@Param("key") String key);
-
   List<MetricDto> selectAllEnabled();
-
+  List<MetricDto> selectAllEnabled(Map<String, Object> properties, RowBounds rowBounds);
   void insert(MetricDto dto);
 
 }
index add84117d0aa8ece6b28b4dd5f50f9e6cd6942f6..77757491d8802199cfaf76811ac7e1f9843db4d1 100644 (file)
     FROM metrics m
     <where>
       AND m.enabled=${_true}
+      <if test="isCustom!=null">
+        <if test="isCustom.equals(true)">
+          AND m.user_managed=${_true}
+        </if>
+        <if test="isCustom.equals(false)">
+          AND m.user_managed=${_false}
+        </if>
+      </if>
     </where>
+    ORDER BY UPPER(m.short_name)
   </select>
 
   <insert id="insert" parameterType="org.sonar.core.measure.db.MetricDto" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
index 95035b48bcd74ded08bfa431c413d4f998957052..6c98862765d2a25b7be9323e035295a380deace9 100644 (file)
@@ -23,8 +23,8 @@ import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.builder.ReflectionToStringBuilder;
 import org.apache.commons.lang.builder.ToStringStyle;
 import org.sonar.api.batch.BatchSide;
-import org.sonar.api.server.ServerSide;
 import org.sonar.api.batch.InstantiationStrategy;
+import org.sonar.api.server.ServerSide;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -65,28 +65,43 @@ public class Metric<G extends Serializable> implements Serializable, org.sonar.a
   public static final int DIRECTION_NONE = 0;
 
   public enum ValueType {
-    INT(Integer.class),
-    FLOAT(Double.class),
-    PERCENT(Double.class),
-    BOOL(Boolean.class),
-    STRING(String.class),
-    MILLISEC(Integer.class),
-    DATA(String.class),
-    LEVEL(Metric.Level.class),
-    DISTRIB(String.class),
-    RATING(Integer.class),
-    WORK_DUR(Long.class);
+    INT(Integer.class, "Integer"),
+    FLOAT(Double.class, "Float"),
+    PERCENT(Double.class, "Percent"),
+    BOOL(Boolean.class, "Yes/No"),
+    STRING(String.class, "String"),
+    MILLISEC(Integer.class, "Milliseconds"),
+    DATA(String.class, "Data"),
+    LEVEL(Metric.Level.class, "Level"),
+    DISTRIB(String.class, "Distribution"),
+    RATING(Integer.class, "Rating"),
+    WORK_DUR(Long.class, "Work Duration");
 
     private final Class valueClass;
+    private final String description;
 
-    private ValueType(Class valueClass) {
+    ValueType(Class valueClass, String description) {
       this.valueClass = valueClass;
+      this.description = description;
     }
 
     private Class valueType() {
       return valueClass;
     }
 
+    public String description() {
+      return description;
+    }
+
+    public static String descriptionOf(String key) {
+      for (ValueType valueType : values()) {
+        if (valueType.name().equals(key)) {
+          return valueType.description;
+        }
+      }
+
+      throw new IllegalArgumentException(String.format("Unknown ValueType key '%s'", key));
+    }
   }
 
   public enum Level {