Revise Mastership REST API to use synchronous methods

- Add missing swagger docs
- Add missing unit tests

Change-Id: I21a20410ea322e7893c5c0c48f5d9fd0f2f9dfe1
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/MastershipWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/MastershipWebResource.java
index 4dfc28f..99cc28c 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/MastershipWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/MastershipWebResource.java
@@ -37,8 +37,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
 
 import static org.onlab.util.Tools.nullIsNotFound;
 
@@ -48,8 +46,7 @@
 @Path("mastership")
 public final class MastershipWebResource extends AbstractWebResource {
 
-    private static final String NODE = "node";
-    private static final String DEVICES = "devices";
+    private static final String DEVICE_IDS = "deviceIds";
     private static final String DEVICE_ID = "deviceId";
     private static final String NODE_ID = "nodeId";
 
@@ -59,7 +56,6 @@
     private static final String NODE_ID_NOT_FOUND = "Node id is not found";
     private static final String ROLE_INFO_NOT_FOUND = "Role info is not found";
     private static final String MASTERSHIP_ROLE_NOT_FOUND = "Mastership role is not found";
-    private static final String RESULT_NOT_FOUND = "Result is not found";
 
     private final MastershipService mastershipService = get(MastershipService.class);
     private final MastershipAdminService mastershipAdminService =
@@ -86,7 +82,7 @@
      *
      * @param deviceId device identifier
      * @return the identifier of the master controller for the device
-     * // TODO: add swagger doc
+     * @onos.rsModel NodeId
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
@@ -96,7 +92,7 @@
                     DeviceId.deviceId(deviceId)), NODE_ID_NOT_FOUND);
 
         ObjectNode root = mapper().createObjectNode();
-        root.put(NODE, id.id());
+        root.put(NODE_ID, id.id());
         return ok(root).build();
     }
 
@@ -123,14 +119,14 @@
      *
      * @param nodeId controller identifier
      * @return a set of device identifiers
-     * // TODO: add swagger doc
+     * @onos.rsModel DeviceIds
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     @Path("{nodeId}/device")
     public Response getDeviceOf(@PathParam("nodeId") String nodeId) {
         ObjectNode root = mapper().createObjectNode();
-        ArrayNode devicesNode = root.putArray(DEVICES);
+        ArrayNode devicesNode = root.putArray(DEVICE_IDS);
 
         Set<DeviceId> devices = mastershipService.getDevicesOf(NodeId.nodeId(nodeId));
         if (devices != null) {
@@ -152,20 +148,10 @@
     @Produces(MediaType.APPLICATION_JSON)
     @Path("{deviceId}/request")
     public Response requestRoleFor(@PathParam("deviceId") String deviceId) {
-
-        // TODO: will not use CompletableFuture when MastershipService
-        // provides a non CompletableFuture object as an output
-        CompletableFuture<MastershipRole> result =
-                nullIsNotFound(mastershipService.requestRoleFor(
-                        DeviceId.deviceId(deviceId)), MASTERSHIP_ROLE_NOT_FOUND);
-
-        try {
-            MastershipRole role = result.get();
-            ObjectNode root = codec(MastershipRole.class).encode(role, this);
-            return ok(root).build();
-        } catch (InterruptedException | ExecutionException e) {
-            throw new IllegalArgumentException(e);
-        }
+        MastershipRole role = nullIsNotFound(mastershipService.requestRoleForSync(
+                                DeviceId.deviceId(deviceId)), MASTERSHIP_ROLE_NOT_FOUND);
+        ObjectNode root = codec(MastershipRole.class).encode(role, this);
+        return ok(root).build();
     }
 
     /**
@@ -181,18 +167,8 @@
     @Path("{deviceId}/relinquish")
     public Response relinquishMastership(@PathParam("deviceId") String deviceId) {
         DeviceId id = DeviceId.deviceId(deviceId);
-
-        // TODO: will not use CompletableFuture when MastershipService
-        // provides a non CompletableFuture object as an output
-        CompletableFuture<Void> result =
-                nullIsNotFound(mastershipService.relinquishMastership(id), RESULT_NOT_FOUND);
-
-        try {
-            result.get();
-            return Response.created(id.uri()).build();
-        } catch (InterruptedException | ExecutionException e) {
-            throw new IllegalArgumentException(e);
-        }
+        mastershipService.relinquishMastershipSync(id);
+        return Response.created(id.uri()).build();
     }
 
     /**
@@ -222,15 +198,11 @@
                 throw new IllegalArgumentException(NODE_ID_INVALID);
             }
 
-            // TODO: will not use CompletableFuture when MastershipAdminService
-            // provides a non CompletableFuture object as an output
-            CompletableFuture<Void> result =
-                    nullIsNotFound(mastershipAdminService.setRole(NodeId.nodeId(nodeIdJson.asText()),
-                    DeviceId.deviceId(deviceIdJson.asText()), role), RESULT_NOT_FOUND);
-            result.get();
+            mastershipAdminService.setRoleSync(NodeId.nodeId(nodeIdJson.asText()),
+                    DeviceId.deviceId(deviceIdJson.asText()), role);
 
             return Response.ok().build();
-        } catch (InterruptedException | ExecutionException | IOException e) {
+        } catch (IOException e) {
             throw new IllegalArgumentException(e);
         }
     }
diff --git a/web/api/src/main/resources/definitions/DeviceIds.json b/web/api/src/main/resources/definitions/DeviceIds.json
new file mode 100644
index 0000000..9911022
--- /dev/null
+++ b/web/api/src/main/resources/definitions/DeviceIds.json
@@ -0,0 +1,20 @@
+{
+  "type": "object",
+  "title": "deviceIds",
+  "required": [
+    "deviceIds"
+  ],
+  "properties": {
+    "deviceIds": {
+      "type": "array",
+      "xml": {
+        "name": "deviceId",
+        "wrapped": true
+      },
+      "items": {
+        "type": "string",
+        "example": "of:0000000000000001"
+      }
+    }
+  }
+}
diff --git a/web/api/src/main/resources/definitions/NodeId.json b/web/api/src/main/resources/definitions/NodeId.json
new file mode 100644
index 0000000..43deb94
--- /dev/null
+++ b/web/api/src/main/resources/definitions/NodeId.json
@@ -0,0 +1,13 @@
+{
+  "type": "object",
+  "title": "nodeId",
+  "required": [
+    "nodeId"
+  ],
+  "properties": {
+    "nodeId": {
+      "type": "string",
+      "example": "1"
+    }
+  }
+}
\ No newline at end of file
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/MastershipResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/MastershipResourceTest.java
index de92cd5..994e138 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/MastershipResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/MastershipResourceTest.java
@@ -22,6 +22,7 @@
 import com.google.common.collect.ImmutableSet;
 import org.apache.commons.lang3.StringUtils;
 import org.hamcrest.Description;
+import org.hamcrest.Matchers;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.Before;
 import org.junit.Test;
@@ -37,8 +38,10 @@
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.MastershipRole;
 
+import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.Response;
+import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.util.List;
 import java.util.Set;
@@ -214,9 +217,9 @@
         assertThat(result, notNullValue());
 
         assertThat(result.names(), hasSize(1));
-        assertThat(result.names().get(0), is("node"));
+        assertThat(result.names().get(0), is("nodeId"));
 
-        final String node = result.get("node").asString();
+        final String node = result.get("nodeId").asString();
         assertThat(node, notNullValue());
         assertThat(node, is("node:1"));
     }
@@ -269,9 +272,9 @@
         assertThat(result, notNullValue());
 
         assertThat(result.names(), hasSize(1));
-        assertThat(result.names().get(0), is("devices"));
+        assertThat(result.names().get(0), is("deviceIds"));
 
-        final JsonArray jsonDevices = result.get("devices").asArray();
+        final JsonArray jsonDevices = result.get("deviceIds").asArray();
         assertThat(jsonDevices, notNullValue());
         assertThat(jsonDevices.size(), is(3));
     }
@@ -281,7 +284,21 @@
      */
     @Test
     public void testRequestRoleFor() {
-        // TODO: will be added when CompletableFuture is removed
+        expect(mockService.requestRoleForSync(anyObject())).andReturn(role1).anyTimes();
+        replay(mockService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("mastership/" + deviceId1.toString() +
+                "/request").request().get(String.class);
+        final JsonObject result = Json.parse(response).asObject();
+        assertThat(result, notNullValue());
+
+        assertThat(result.names(), hasSize(1));
+        assertThat(result.names().get(0), is("role"));
+
+        final String role = result.get("role").asString();
+        assertThat(role, notNullValue());
+        assertThat(role, is("MASTER"));
     }
 
     /**
@@ -289,7 +306,16 @@
      */
     @Test
     public void testRelinquishMastership() {
-        // TODO: will be added when CompletableFuture is removed
+        mockService.relinquishMastershipSync(anyObject());
+        expectLastCall();
+        replay(mockService);
+
+        final WebTarget wt = target();
+        final Response response = wt.path("mastership/" + deviceId1.toString() +
+                "/relinquish").request().get();
+        assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
+        String location = response.getLocation().toString();
+        assertThat(location, Matchers.startsWith(deviceId1.toString()));
     }
 
     /**
@@ -297,7 +323,16 @@
      */
     @Test
     public void testSetRole() {
-        // TODO: will be added when CompletableFuture is removed
+        mockAdminService.setRoleSync(anyObject(), anyObject(), anyObject());
+        expectLastCall();
+        replay(mockAdminService);
+
+        final WebTarget wt = target();
+        final InputStream jsonStream = MetersResourceTest.class
+                .getResourceAsStream("put-set-roles.json");
+        final Response response = wt.path("mastership")
+                                    .request().put(Entity.json(jsonStream));
+        assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
     }
 
     /**