]> source.dussan.org Git - sonarqube.git/commitdiff
Add response example to WebService extension point
authorSimon Brandhof <simon.brandhof@gmail.com>
Tue, 29 Apr 2014 19:23:43 +0000 (21:23 +0200)
committerSimon Brandhof <simon.brandhof@gmail.com>
Tue, 29 Apr 2014 19:23:43 +0000 (21:23 +0200)
15 files changed:
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/RailsHandler.java
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/RequestHandler.java
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java
sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java
sonar-plugin-api/src/test/resources/org/sonar/api/server/ws/WebServiceTest/response-example.txt [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/rule2/RuleService.java
sonar-server/src/main/java/org/sonar/server/source/ws/ShowAction.java
sonar-server/src/main/java/org/sonar/server/ws/ListingWs.java
sonar-server/src/main/resources/org/sonar/server/source/ws/example-show.json [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/source/ws/ShowActionTest.java
sonar-server/src/test/java/org/sonar/server/ws/ListingWsTest.java
sonar-server/src/test/java/org/sonar/server/ws/WsTester.java
sonar-server/src/test/resources/org/sonar/server/ws/ListingWsTest/list.json
sonar-server/src/test/resources/org/sonar/server/ws/ListingWsTest/metrics_example.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/ws/ListingWsTest/response_example.json [new file with mode: 0644]

index 53c9ad12a4df00179041e421047917f48f40c4fe..87f798b525073fd5aec89b0f1e83761f1439c02d 100644 (file)
 package org.sonar.api.server.ws;
 
 /**
- * Used to declare web services that are still implemented in rails.
+ * Used to declare web services that are still implemented in Ruby on Rails.
  *
  * @since 4.4
  */
 public class RailsHandler implements RequestHandler {
 
-  public static final RequestHandler INSTANCE = new RailsHandler(){};
+  public static final RequestHandler INSTANCE = new RailsHandler();
 
   private RailsHandler() {
     // Nothing
index 94a3e050ae987a413bccc291c2fb138344527e69..84eb2af2e903c23849c235d7a5146acc39217e32 100644 (file)
@@ -26,6 +26,6 @@ import org.sonar.api.ServerExtension;
  */
 public interface RequestHandler extends ServerExtension {
 
-  void handle(Request request, Response response);
+  void handle(Request request, Response response) throws Exception;
 
 }
index 3f75ee3777f46f461dea7541c482665a09eed58f..3da8be70df17f2cff45e4937061e3f4864ca9382 100644 (file)
  */
 package org.sonar.api.server.ws;
 
+import com.google.common.base.Charsets;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
+import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.ServerExtension;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
-
+import java.io.IOException;
+import java.net.URL;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -248,6 +251,8 @@ public interface WebService extends ServerExtension {
     private boolean post = false, isInternal = false;
     private RequestHandler handler;
     private Map<String, NewParam> newParams = Maps.newHashMap();
+    private URL responseExample = null;
+    private String responseExampleFormat = null;
 
     private NewAction(String key) {
       this.key = key;
@@ -278,6 +283,31 @@ public interface WebService extends ServerExtension {
       return this;
     }
 
+    /**
+     * Link to the document containing an example of response. Content must be UTF-8 encoded.
+     * <p/>
+     * Example:
+     * <pre>
+     *   newAction.setResponseExample(getClass().getResource("/org/sonar/my-ws-response-example.json"));
+     * </pre>
+     *
+     * @since 4.4
+     */
+    public NewAction setResponseExample(@Nullable URL url) {
+      this.responseExample = url;
+      return this;
+    }
+
+    /**
+     * Used only if {@link #setResponseExample(java.net.URL)} is set. Example of values: "xml", "json", "txt", "csv".
+     *
+     * @since 4.4
+     */
+    public NewAction setResponseExampleFormat(@Nullable String format) {
+      this.responseExampleFormat = format;
+      return this;
+    }
+
     public NewParam createParam(String paramKey) {
       if (newParams.containsKey(paramKey)) {
         throw new IllegalStateException(
@@ -305,6 +335,8 @@ public interface WebService extends ServerExtension {
     private final boolean post, isInternal;
     private final RequestHandler handler;
     private final Map<String, Param> params;
+    private final URL responseExample;
+    private final String responseExampleFormat;
 
     private Action(Controller controller, NewAction newAction) {
       this.key = newAction.key;
@@ -313,9 +345,11 @@ public interface WebService extends ServerExtension {
       this.since = StringUtils.defaultIfBlank(newAction.since, controller.since);
       this.post = newAction.post;
       this.isInternal = newAction.isInternal;
+      this.responseExample = newAction.responseExample;
+      this.responseExampleFormat = newAction.responseExampleFormat;
 
       if (newAction.handler == null) {
-        throw new IllegalStateException("RequestHandler is not set on action " + path);
+        throw new IllegalArgumentException("RequestHandler is not set on action " + path);
       }
       this.handler = newAction.handler;
 
@@ -359,6 +393,37 @@ public interface WebService extends ServerExtension {
       return handler;
     }
 
+    /**
+     * @see org.sonar.api.server.ws.WebService.NewAction#setResponseExample(java.net.URL)
+     */
+    @CheckForNull
+    public URL responseExample() {
+      return responseExample;
+    }
+
+    /**
+     * @see org.sonar.api.server.ws.WebService.NewAction#setResponseExample(java.net.URL)
+     */
+    @CheckForNull
+    public String responseExampleAsString() {
+      try {
+        if (responseExample != null) {
+          return IOUtils.toString(responseExample, Charsets.UTF_8);
+        }
+        return null;
+      } catch (IOException e) {
+        throw new IllegalStateException("Fail to load " + responseExample, e);
+      }
+    }
+
+    /**
+     * @see org.sonar.api.server.ws.WebService.NewAction#setResponseExampleFormat(String)
+     */
+    @CheckForNull
+    public String responseExampleFormat() {
+      return responseExampleFormat;
+    }
+
     @CheckForNull
     public Param param(String key) {
       return params.get(key);
@@ -390,6 +455,7 @@ public interface WebService extends ServerExtension {
 
     /**
      * Is the parameter required or optional ? Default value is false (optional).
+     *
      * @since 4.4
      */
     public NewParam setRequired(boolean b) {
@@ -408,6 +474,7 @@ public interface WebService extends ServerExtension {
     /**
      * Exhaustive list of possible values when it makes sense, for example
      * list of severities.
+     *
      * @since 4.4
      */
     public NewParam setPossibleValues(@Nullable String... s) {
@@ -463,6 +530,7 @@ public interface WebService extends ServerExtension {
 
     /**
      * Is the parameter required or optional ?
+     *
      * @since 4.4
      */
     public boolean isRequired() {
index c089bfbf2e04342c841915ce3192da36177a04a8..431e21a2e9fa3b52228c091e5d6f7b970ae002ef 100644 (file)
  */
 package org.sonar.api.server.ws;
 
+import org.apache.commons.lang.StringUtils;
 import org.junit.Test;
 
+import java.net.MalformedURLException;
+import java.net.URL;
+
 import static org.fest.assertions.Assertions.assertThat;
 import static org.fest.assertions.Fail.fail;
 import static org.mockito.Mockito.mock;
@@ -50,6 +54,8 @@ public class WebServiceTest {
         .setSince("4.1")
         .setPost(true)
         .setInternal(true)
+        .setResponseExampleFormat("txt")
+        .setResponseExample(getClass().getResource("/org/sonar/api/server/ws/WebServiceTest/response-example.txt"))
         .setHandler(new RequestHandler() {
           @Override
           public void handle(Request request, Response response) {
@@ -142,7 +148,7 @@ public class WebServiceTest {
         }
       }.define(context);
       fail();
-    } catch (IllegalStateException e) {
+    } catch (IllegalArgumentException e) {
       assertThat(e).hasMessage("RequestHandler is not set on action rule/show");
     }
   }
@@ -298,4 +304,44 @@ public class WebServiceTest {
 
     assertThat(context.controller("api/rule").isInternal()).isTrue();
   }
+
+  @Test
+  public void response_example() {
+    MetricWebService metricWs = new MetricWebService();
+    metricWs.define(context);
+    WebService.Action action = context.controller("api/metric").action("create");
+
+    assertThat(action.responseExampleFormat()).isEqualTo("txt");
+    assertThat(action.responseExample()).isNotNull();
+    assertThat(StringUtils.trim(action.responseExampleAsString())).isEqualTo("example of WS response");
+  }
+
+  @Test
+  public void fail_to_open_response_example() {
+    WebService ws = new WebService() {
+      @Override
+      public void define(Context context) {
+        try {
+          NewController controller = context.createController("foo");
+          controller
+            .createAction("bar")
+            .setHandler(mock(RequestHandler.class))
+            .setResponseExample(new URL("file:/does/not/exist"));
+          controller.done();
+
+        } catch (MalformedURLException e) {
+          e.printStackTrace();
+        }
+      }
+    };
+    ws.define(context);
+
+    WebService.Action action = context.controller("foo").action("bar");
+    try {
+      action.responseExampleAsString();
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("Fail to load file:/does/not/exist");
+    }
+  }
 }
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/server/ws/WebServiceTest/response-example.txt b/sonar-plugin-api/src/test/resources/org/sonar/api/server/ws/WebServiceTest/response-example.txt
new file mode 100644 (file)
index 0000000..21c78dd
--- /dev/null
@@ -0,0 +1 @@
+example of WS response
index 197edb791cf3d43eaea73d6f1b9c6912659daecb..35d1c0ee18bbd7d9c416506f5303eb8fcf53639f 100644 (file)
@@ -19,8 +19,6 @@
  */
 package org.sonar.server.rule2;
 
-import org.apache.commons.beanutils.BeanUtils;
-
 import org.sonar.api.ServerComponent;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.core.rule.RuleDao;
@@ -28,7 +26,6 @@ import org.sonar.core.rule.RuleDto;
 import org.sonar.server.search.Hit;
 
 import javax.annotation.CheckForNull;
-
 import java.util.Collection;
 import java.util.Collections;
 
@@ -40,7 +37,7 @@ public class RuleService implements ServerComponent {
   private RuleDao dao;
   private RuleIndex index;
 
-  public RuleService(RuleDao dao, RuleIndex index){
+  public RuleService(RuleDao dao, RuleIndex index) {
     this.dao = dao;
     this.index = index;
   }
@@ -48,23 +45,23 @@ public class RuleService implements ServerComponent {
   @CheckForNull
   public Rule getByKey(RuleKey key) {
     Hit hit = index.getByKey(key);
-    if(hit != null){
+    if (hit != null) {
       return toRule(hit);
     } else {
       return null;
     }
   }
 
-  public Collection<Hit> search(RuleQuery query){
+  public Collection<Hit> search(RuleQuery query) {
 
     return Collections.emptyList();
   }
 
-  public static Rule toRule(RuleDto ruleDto){
+  public static Rule toRule(RuleDto ruleDto) {
     return new RuleImpl();
   }
 
-  public static Rule toRule(Hit hit){
+  public static Rule toRule(Hit hit) {
 //    BeanUtils.setProperty(bean, name, value);
     return new RuleImpl();
   }
index 0acdcbe82e3851a249f50eb535cf24ccc48a0f0e..08463a3ff0a073f3facd5ad6f68302e6a178301a 100644 (file)
@@ -44,6 +44,8 @@ public class ShowAction implements RequestHandler {
       .setDescription("Get source code. Parameter 'output' with value 'raw' is missing before being marked as a public WS.")
       .setSince("4.2")
       .setInternal(true)
+      .setResponseExampleFormat("json")
+      .setResponseExample(getClass().getResource("/org/sonar/server/source/ws/example-show.json"))
       .setHandler(this);
 
     action
index 010f3f19d84eba7a189d81146834769cd04ad5ce..c67c5f107f24534b33527bf05855198eeb98e8c4 100644 (file)
  */
 package org.sonar.server.ws;
 
+import com.google.common.base.Charsets;
 import com.google.common.base.Function;
 import com.google.common.collect.Ordering;
+import org.apache.commons.io.IOUtils;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.RequestHandler;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.text.JsonWriter;
 
+import java.io.IOException;
 import java.util.List;
 
 /**
@@ -39,20 +42,56 @@ public class ListingWs implements WebService {
 
   @Override
   public void define(final Context context) {
-    NewController controller = context.createController("api/webservices")
-      .setDescription("List web services")
-      .setSince("4.2");
-    controller.createAction("list")
+    NewController controller = context
+      .createController("api/webservices")
+      .setDescription("List web services");
+    defineList(context, controller);
+    defineResponseExample(context, controller);
+    controller.done();
+  }
+
+  private void defineList(final Context context, NewController controller) {
+    controller
+      .createAction("list")
+      .setSince("4.2")
       .setHandler(new RequestHandler() {
         @Override
         public void handle(Request request, Response response) {
-          list(context.controllers(), response);
+          handleList(context.controllers(), response);
         }
       });
-    controller.done();
   }
 
-  void list(List<Controller> controllers, Response response) {
+  private void defineResponseExample(final Context context, NewController controller) {
+    NewAction action = controller
+      .createAction("responseExample")
+      .setHandler(new RequestHandler() {
+        @Override
+        public void handle(Request request, Response response) throws Exception {
+          Controller controller = context.controller(request.mandatoryParam("controller"));
+          Action action = controller.action(request.mandatoryParam("action"));
+          handleResponseExample(action, response);
+        }
+      });
+    action.createParam("controller").setRequired(true);
+    action.createParam("action").setRequired(true);
+  }
+
+  private void handleResponseExample(Action action, Response response) throws IOException {
+    if (action.responseExample() != null) {
+      response
+        .newJsonWriter()
+        .beginObject()
+        .prop("format", action.responseExampleFormat())
+        .prop("example", IOUtils.toString(action.responseExample(), Charsets.UTF_8))
+        .endObject()
+        .close();
+    } else {
+      response.noContent();
+    }
+  }
+
+  void handleList(List<Controller> controllers, Response response) {
     JsonWriter writer = response.newJsonWriter();
     writer.beginObject();
     writer.name("webServices").beginArray();
@@ -64,14 +103,14 @@ public class ListingWs implements WebService {
       }
     });
     for (Controller controller : ordering.sortedCopy(controllers)) {
-      write(writer, controller);
+      writeController(writer, controller);
     }
     writer.endArray();
     writer.endObject();
     writer.close();
   }
 
-  private void write(JsonWriter writer, Controller controller) {
+  private void writeController(JsonWriter writer, Controller controller) {
     writer.beginObject();
     writer.prop("path", controller.path());
     writer.prop("since", controller.since());
@@ -84,19 +123,20 @@ public class ListingWs implements WebService {
     });
     writer.name("actions").beginArray();
     for (Action action : ordering.sortedCopy(controller.actions())) {
-      write(writer, action);
+      writeAction(writer, action);
     }
     writer.endArray();
     writer.endObject();
   }
 
-  private void write(JsonWriter writer, Action action) {
+  private void writeAction(JsonWriter writer, Action action) {
     writer.beginObject();
     writer.prop("key", action.key());
     writer.prop("description", action.description());
     writer.prop("since", action.since());
     writer.prop("internal", action.isInternal());
     writer.prop("post", action.isPost());
+    writer.prop("hasResponseExample", action.responseExample()!=null);
     if (!action.params().isEmpty()) {
       // sort parameters by key
       Ordering<Param> ordering = Ordering.natural().onResultOf(new Function<Param, String>() {
@@ -106,14 +146,14 @@ public class ListingWs implements WebService {
       });
       writer.name("params").beginArray();
       for (Param param : ordering.sortedCopy(action.params())) {
-        write(writer, param);
+        writeParam(writer, param);
       }
       writer.endArray();
     }
     writer.endObject();
   }
 
-  private void write(JsonWriter writer, Param param) {
+  private void writeParam(JsonWriter writer, Param param) {
     writer.beginObject();
     writer.prop("key", param.key());
     writer.prop("description", param.description());
diff --git a/sonar-server/src/main/resources/org/sonar/server/source/ws/example-show.json b/sonar-server/src/main/resources/org/sonar/server/source/ws/example-show.json
new file mode 100644 (file)
index 0000000..f421822
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "source": {
+    "20": "<span class=\"k\">package </span>org.sonar.check;",
+    "21": "",
+    "22": "<span class=\"k\">public </span><span class=\"k\">enum </span><span class=\"sym-922 sym\">Priority</span> {",
+    "23": "  <span class=\"cppd\">/**</span>",
+    "24": "<span class=\"cppd\">   * WARNING : DO NOT CHANGE THE ENUMERATION ORDER</span>",
+    "25": "<span class=\"cppd\">   * the enum ordinal is used for db persistence</span>"
+  },
+  "scm": {
+    "20": ["simon.brandhof@gmail.com", "2010-09-06"]
+  }
+}
index 7e45810dbc3158c4fbb97d74add1c58bfd1dabe5..07f8c14edacad0452c8c6f97960cf75f3c52f23b 100644 (file)
  */
 package org.sonar.server.source.ws;
 
+import org.apache.commons.io.IOUtils;
 import org.junit.Before;
 import org.junit.Test;
+import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.source.SourceService;
@@ -67,6 +69,13 @@ public class ShowActionTest {
     request.execute().assertJson(getClass(), "show_source.json");
   }
 
+  @Test
+  public void response_example_exists() throws Exception {
+    WebService.Action show = tester.controller("api/sources").action("show");
+    assertThat(show.responseExampleFormat()).isEqualTo("json");
+    assertThat(show.responseExampleAsString()).isNotEmpty();
+  }
+
   @Test
   public void fail_to_show_source_if_no_source_found() throws Exception {
     String componentKey = "src/Foo.java";
index a6525564341bc1b22ef8a34f890c044f841e47a5..b60cdbc0ea35d631745702ccae8fc445d93a0470 100644 (file)
@@ -38,8 +38,7 @@ public class ListingWsTest {
     assertThat(controller).isNotNull();
     assertThat(controller.path()).isEqualTo("api/webservices");
     assertThat(controller.description()).isNotEmpty();
-    assertThat(controller.since()).isEqualTo("4.2");
-    assertThat(controller.actions()).hasSize(1);
+    assertThat(controller.actions()).hasSize(2);
 
     WebService.Action index = controller.action("list");
     assertThat(index).isNotNull();
@@ -48,14 +47,26 @@ public class ListingWsTest {
     assertThat(index.since()).isEqualTo("4.2");
     assertThat(index.isPost()).isFalse();
     assertThat(index.isInternal()).isFalse();
+
+    assertThat(controller.action("responseExample")).isNotNull();
   }
 
   @Test
-  public void index() throws Exception {
+  public void list() throws Exception {
     WsTester tester = new WsTester(ws, new MetricWebService());
     tester.newRequest("api/webservices", "list").execute().assertJson(getClass(), "list.json");
   }
 
+  @Test
+  public void response_example() throws Exception {
+    WsTester tester = new WsTester(ws, new MetricWebService());
+    tester
+      .newRequest("api/webservices", "responseExample")
+      .setParam("controller", "api/metric")
+      .setParam("action", "create")
+      .execute().assertJson(getClass(), "response_example.json");
+  }
+
   static class MetricWebService implements WebService {
     @Override
     public void define(Context context) {
@@ -78,6 +89,8 @@ public class ListingWsTest {
         .setSince("4.1")
         .setPost(true)
         .setInternal(true)
+        .setResponseExample(getClass().getResource("/org/sonar/server/ws/ListingWsTest/metrics_example.json"))
+        .setResponseExampleFormat("json")
         .setHandler(new RequestHandler() {
           @Override
           public void handle(Request request, Response response) {
index 890699710d1098f40003a70cea9c612e915a4888..ec27a53760f225301784c03ff6f246d4f8c097a3 100644 (file)
@@ -87,7 +87,7 @@ public class WsTester {
       return params.get(key);
     }
 
-    public Result execute() {
+    public Result execute() throws Exception {
       TestResponse response = new TestResponse();
       action.handler().handle(this, response);
       return new Result(response);
@@ -181,7 +181,7 @@ public class WsTester {
      * at src/test/resources/org/foo/BarTest/index.json.
      *
      * @param clazz                the test class
-     * @param jsonResourceFilename name of the file containing the expected JSON
+     * @param expectedJsonFilename name of the file containing the expected JSON
      */
     public Result assertJson(Class clazz, String expectedJsonFilename) throws Exception {
       String path = clazz.getSimpleName() + "/" + expectedJsonFilename;
index fe48e902ea35058de33afd7a338753e799ea53d2..da0b96b46476f63cb86be1378878f7bb65dfc7fb 100644 (file)
@@ -1,51 +1,67 @@
-{
-  "webServices": [
-    {
-      "path": "api/metric",
-      "since": "3.2",
-      "description": "Metrics",
-      "actions": [
-        {
-          "key": "create",
-          "description": "Create metric",
-          "since": "4.1",
-          "internal": true,
-          "post": true,
-          "params": [
-            {
-              "key": "name",
-              "required": false
-            },
-            {
-              "key": "severity",
-              "description": "Severity",
-              "required": true,
-              "defaultValue": "BLOCKER",
-              "exampleValue": "INFO",
-              "possibleValues": ["BLOCKER", "INFO"]
-
-            }
-          ]
-        },
-        {
-          "key": "show",
-          "since": "3.2",
-          "internal": false,
-          "post": false
-        }
-      ]
-    },
-    {
-      "path": "api/webservices",
-      "since": "4.2",
-      "description": "List web services",
-      "actions": [
-        {
-          "key": "list",
-          "since": "4.2",
-          "internal": false,
-          "post": false
-        }
-      ]
-    }
-  ]}
+{"webServices": [
+  {
+    "path": "api/metric",
+    "since": "3.2",
+    "description": "Metrics",
+    "actions": [
+      {
+        "key": "create",
+        "description": "Create metric",
+        "since": "4.1",
+        "internal": true,
+        "post": true,
+        "hasResponseExample": true,
+        "params": [
+          {
+            "key": "name",
+            "required": false
+          },
+          {
+            "key": "severity",
+            "description": "Severity",
+            "required": true,
+            "defaultValue": "BLOCKER",
+            "exampleValue": "INFO",
+            "possibleValues": ["BLOCKER", "INFO"]
+          }
+        ]
+      },
+      {
+        "key": "show",
+        "since": "3.2",
+        "internal": false,
+        "post": false,
+        "hasResponseExample": false
+      }
+    ]
+  },
+  {
+    "path": "api/webservices",
+    "description": "List web services",
+    "actions": [
+      {
+        "key": "list",
+        "since": "4.2",
+        "internal": false,
+        "post": false,
+        "hasResponseExample": false
+      },
+      {
+        "key": "responseExample",
+        "internal": false,
+        "post": false,
+        "hasResponseExample": false,
+        "params": [
+          {
+            "key": "action",
+            "required": true
+          },
+          {
+            "key": "controller",
+            "required": true
+          }
+        ]
+      }
+    ]
+  }
+]}
diff --git a/sonar-server/src/test/resources/org/sonar/server/ws/ListingWsTest/metrics_example.json b/sonar-server/src/test/resources/org/sonar/server/ws/ListingWsTest/metrics_example.json
new file mode 100644 (file)
index 0000000..2516f68
--- /dev/null
@@ -0,0 +1 @@
+{"metrics":[]}
diff --git a/sonar-server/src/test/resources/org/sonar/server/ws/ListingWsTest/response_example.json b/sonar-server/src/test/resources/org/sonar/server/ws/ListingWsTest/response_example.json
new file mode 100644 (file)
index 0000000..dac39a0
--- /dev/null
@@ -0,0 +1 @@
+{"format":"json","example":"{\"metrics\":[]}\n"}