[ONOS-4530] Allow to specify appId when insert FlowRule through REST

- Augment FlowRuleCodec to encode FlowRule
- Add unit test for encode method of FlowRuleCodec
- Add getFlowByAppId and removeFlowByAppId methods in FlowsWebResource
- Add more unit tests for FlowWebResource
- Add FlowRules.json swagger doc
- Rename Flows.json to FlowEntries.json, correct FlowEntries.json

Change-Id: Ic3ec390c13a349e51ae4208adbc478564b6724ba
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
index fc99c49..42e9590 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
@@ -21,6 +21,8 @@
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ListMultimap;
 import org.onlab.util.ItemNotFoundException;
+import org.onosproject.app.ApplicationService;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.device.DeviceService;
@@ -36,6 +38,7 @@
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -61,6 +64,7 @@
 
     private static final String DEVICE_NOT_FOUND = "Device is not found";
     private static final String FLOW_NOT_FOUND = "Flow is not found";
+    private static final String APP_ID_NOT_FOUND = "Application Id is not found";
     private static final String FLOWS = "flows";
     private static final String DEVICE_ID = "deviceId";
     private static final String FLOW_ID = "flowId";
@@ -73,7 +77,7 @@
      * Gets all flow entries. Returns array of all flow rules in the system.
      *
      * @return 200 OK with a collection of flows
-     * @onos.rsModel Flows
+     * @onos.rsModel FlowEntries
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
@@ -107,10 +111,15 @@
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response createFlows(InputStream stream) {
+    public Response createFlows(@QueryParam("appId") String appId, InputStream stream) {
         try {
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
             ArrayNode flowsArray = (ArrayNode) jsonTree.get(FLOWS);
+
+            if (appId != null) {
+                flowsArray.forEach(flowJson -> ((ObjectNode) flowJson).put("appId", appId));
+            }
+
             List<FlowRule> rules = codec(FlowRule.class).decode(flowsArray, this);
             service.applyFlowRules(rules.toArray(new FlowRule[rules.size()]));
             rules.forEach(flowRule -> {
@@ -131,10 +140,11 @@
      *
      * @param deviceId device identifier
      * @return 200 OK with a collection of flows of given device
-     * @onos.rsModel Flows
+     * @onos.rsModel FlowEntries
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
+    // TODO: we need to add "/device" suffix to the path to differentiate with appId
     @Path("{deviceId}")
     public Response getFlowByDeviceId(@PathParam("deviceId") String deviceId) {
         final Iterable<FlowEntry> flowEntries =
@@ -150,13 +160,13 @@
     }
 
     /**
-     * Gets flow rule. Returns the flow entry specified by the device id and
+     * Gets flow rules. Returns the flow entry specified by the device id and
      * flow rule id.
      *
      * @param deviceId device identifier
      * @param flowId   flow rule identifier
-     * @return 200 OK with a flows of given device and flow
-     * @onos.rsModel Flows
+     * @return 200 OK with a collection of flows of given device and flow
+     * @onos.rsModel FlowEntries
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
@@ -178,6 +188,43 @@
     }
 
     /**
+     * Gets flow rules generated by an application.
+     * Returns the flow rule specified by the application id.
+     *
+     * @param appId application identifier
+     * @return 200 OK with a collection of flows of given application id
+     * @onos.rsModel FlowRules
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("application/{appId}")
+    public Response getFlowByAppId(@PathParam("appId") String appId) {
+        final ApplicationService appService = get(ApplicationService.class);
+        final ApplicationId idInstant = nullIsNotFound(appService.getId(appId), APP_ID_NOT_FOUND);
+        final Iterable<FlowRule> flowRules = service.getFlowRulesById(idInstant);
+
+        flowRules.forEach(flow -> flowsNode.add(codec(FlowRule.class).encode(flow, this)));
+        return ok(root).build();
+    }
+
+    /**
+     * Removes flow rules by application ID.
+     * Removes a collection of flow rules generated by the given application.
+     *
+     * @param appId application identifier
+     * @return 204 NO CONTENT
+     */
+    @DELETE
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("application/{appId}")
+    public Response removeFlowByAppId(@PathParam("appId") String appId) {
+        final ApplicationService appService = get(ApplicationService.class);
+        final ApplicationId idInstant = nullIsNotFound(appService.getId(appId), APP_ID_NOT_FOUND);
+        service.removeFlowRulesById(idInstant);
+        return Response.noContent().build();
+    }
+
+    /**
      * Creates new flow rule. Creates and installs a new flow rule for the
      * specified device. <br>
      * Instructions description:
@@ -187,6 +234,7 @@
      * https://wiki.onosproject.org/display/ONOS/Flow+Rule+Criteria
      *
      * @param deviceId device identifier
+     * @param appId    application identifier
      * @param stream   flow rule JSON
      * @return status of the request - CREATED if the JSON is correct,
      * BAD_REQUEST if the JSON is invalid
@@ -197,6 +245,7 @@
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response createFlow(@PathParam("deviceId") String deviceId,
+                               @QueryParam("appId") String appId,
                                InputStream stream) {
         try {
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
@@ -207,6 +256,11 @@
                         "Invalid deviceId in flow creation request");
             }
             jsonTree.put("deviceId", deviceId);
+
+            if (appId != null) {
+                jsonTree.put("appId", appId);
+            }
+
             FlowRule rule = codec(FlowRule.class).decode(jsonTree, this);
             service.applyFlowRules(rule);
             UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
@@ -223,7 +277,7 @@
     }
 
     /**
-     * Remove flow rule. Removes the specified flow rule.
+     * Removes flow rule. Removes the specified flow rule.
      *
      * @param deviceId device identifier
      * @param flowId   flow rule identifier