Fixing device key REST API bugs and adding json definitions.

Change-Id: I41a98702e781544fe6e0bebe3e281ff2bcd9a6fa
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DeviceKeyCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/DeviceKeyCodec.java
index 7802ebd..1419dda 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DeviceKeyCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DeviceKeyCodec.java
@@ -41,21 +41,20 @@
     private static final String USERNAME = "username";
     private static final String PASSWORD = "password";
 
-
     @Override
     public ObjectNode encode(DeviceKey deviceKey, CodecContext context) {
         checkNotNull(deviceKey, "Device key cannot be null");
         DeviceKeyService service = context.getService(DeviceKeyService.class);
         ObjectNode result = context.mapper().createObjectNode()
-                .put(ID, deviceKey.deviceKeyId().id().toString())
+                .put(ID, deviceKey.deviceKeyId().id())
                 .put(TYPE, deviceKey.type().toString())
-                .put(LABEL, deviceKey.label().toString());
+                .put(LABEL, deviceKey.label());
 
         if (deviceKey.type().equals(DeviceKey.Type.COMMUNITY_NAME)) {
             result.put(COMMUNITY_NAME, deviceKey.asCommunityName().name());
         } else if (deviceKey.type().equals(DeviceKey.Type.USERNAME_PASSWORD)) {
-            result.put(USERNAME, deviceKey.asUsernamePassword().username().toString());
-            result.put(PASSWORD, deviceKey.asUsernamePassword().password().toString());
+            result.put(USERNAME, deviceKey.asUsernamePassword().username());
+            result.put(PASSWORD, deviceKey.asUsernamePassword().password());
         }
 
         return annotate(result, deviceKey, context);
@@ -70,18 +69,29 @@
         DeviceKeyId id = DeviceKeyId.deviceKeyId(json.get(ID).asText());
 
         DeviceKey.Type type = DeviceKey.Type.valueOf(json.get(TYPE).asText());
-        String label = json.get(LABEL).asText();
+        String label = extract(json, LABEL);
 
         if (type.equals(DeviceKey.Type.COMMUNITY_NAME)) {
-            String communityName = json.get(COMMUNITY_NAME).asText();
+            String communityName = extract(json, COMMUNITY_NAME);
             return DeviceKey.createDeviceKeyUsingCommunityName(id, label, communityName);
         } else if (type.equals(DeviceKey.Type.USERNAME_PASSWORD)) {
-            String username = json.get(USERNAME).asText();
-            String password = json.get(PASSWORD).asText();
+            String username = extract(json, USERNAME);
+            String password = extract(json, PASSWORD);
             return DeviceKey.createDeviceKeyUsingUsernamePassword(id, label, username, password);
         } else {
             log.error("Unknown device key type: ", type);
             return null;
         }
     }
+
+    /**
+     * Extract the key from the json node.
+     *
+     * @param json json object
+     * @param key key to use extract the value from the json object
+     * @return extracted value from the json object
+     */
+    private String extract(ObjectNode json, String key) {
+        return json.get(key) == null ? null : json.get(key).asText();
+    }
 }
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/DeviceKeyWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/DeviceKeyWebResource.java
index 3e78825..c4020e1 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/DeviceKeyWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/DeviceKeyWebResource.java
@@ -56,9 +56,8 @@
      * Returns array of all device keys.
      *
      * @return 200 OK
+     * @onos.rsModel Devicekeys
      */
-// FIXME DeviceKeysGet.json not found
-//   * @onos.rsModel DeviceKeysGet
     @GET
     public Response getDeviceKeys() {
         Iterable<DeviceKey> deviceKeys = get(DeviceKeyService.class).getDeviceKeys();
@@ -69,11 +68,10 @@
      * Get a single device key by device key unique identifier.
      * Returns the specified device key.
      *
-     * @param id device identifier
-     * @return 200 OK
+     * @param id device key identifier
+     * @return 200 OK, 404 not found
+     * @onos.rsModel Devicekey
      */
-// FIXME DeviceKeyGet.json not found
-//   * @onos.rsModel DeviceKeyGet
     @GET
     @Path("{id}")
     public Response getDeviceKey(@PathParam("id") String id) {
@@ -83,14 +81,13 @@
     }
 
     /**
-     * Adds a new device key from the JSON request.
+     * Adds a new device key from the JSON input stream.
      *
-     * @param stream input JSON
+     * @param stream device key JSON stream
      * @return status of the request - CREATED if the JSON is correct,
      * BAD_REQUEST if the JSON is invalid
+     * @onos.rsModel Devicekey
      */
-// FIXME wrong schema definition?
-//   * @onos.rsModel IntentHost
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
@@ -117,8 +114,8 @@
     /**
      * Removes a device key by device key identifier.
      *
-     * @param id device identifier
-     * @return 200 OK
+     * @param id device key identifier
+     * @return 200 OK, 404 not found
      */
     @DELETE
     @Path("{id}")
diff --git a/web/api/src/main/resources/definitions/Devicekey.json b/web/api/src/main/resources/definitions/Devicekey.json
new file mode 100644
index 0000000..a8bee2d
--- /dev/null
+++ b/web/api/src/main/resources/definitions/Devicekey.json
@@ -0,0 +1,35 @@
+{
+  "type": "object",
+  "title": "Devicekey",
+  "required": [
+    "id",
+    "type",
+    "label"
+  ],
+  "properties": {
+    "id": {
+      "type": "String",
+      "example": "Device key unique identifier"
+    },
+    "type": {
+      "type": "String",
+      "example": "COMMUNITY_NAME, OR USERNAME_PASSWORD"
+    },
+    "label": {
+      "type": "String",
+      "example": "Device key user label"
+    },
+    "community_name": {
+      "type": "String",
+      "example": "Device key community name"
+    },
+    "username": {
+      "type": "String",
+      "example": "Device key username"
+    },
+    "password": {
+      "type": "String",
+      "example": "Device key password"
+    }
+  }
+}
diff --git a/web/api/src/main/resources/definitions/Devicekeys.json b/web/api/src/main/resources/definitions/Devicekeys.json
new file mode 100644
index 0000000..57871f5
--- /dev/null
+++ b/web/api/src/main/resources/definitions/Devicekeys.json
@@ -0,0 +1,51 @@
+{
+  "type": "object",
+  "title": "Devicekeys",
+  "required": [
+    "keys"
+  ],
+  "properties": {
+    "keys": {
+      "type": "array",
+      "xml": {
+        "name": "keys",
+        "wrapped": true
+      },
+      "items": {
+        "type": "object",
+        "title": "key",
+        "required": [
+          "id",
+          "type",
+          "label"
+        ],
+        "properties": {
+          "id": {
+            "type": "String",
+            "example": "Device key unique identifier"
+          },
+          "type": {
+            "type": "String",
+            "example": "COMMUNITY_NAME, OR USERNAME_PASSWORD"
+          },
+          "label": {
+            "type": "String",
+            "example": "Device key user label"
+          },
+          "community_name": {
+            "type": "String",
+            "example": "Device key community name"
+          },
+          "username": {
+            "type": "String",
+            "example": "Device key username"
+          },
+          "password": {
+            "type": "String",
+            "example": "Device key password"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/web/api/src/test/java/org/onosproject/rest/DeviceKeyWebResourceTest.java b/web/api/src/test/java/org/onosproject/rest/DeviceKeyWebResourceTest.java
index 886674c..54f1bea 100644
--- a/web/api/src/test/java/org/onosproject/rest/DeviceKeyWebResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/DeviceKeyWebResourceTest.java
@@ -25,7 +25,6 @@
 import org.hamcrest.Description;
 import org.hamcrest.Matchers;
 import org.hamcrest.TypeSafeMatcher;
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
@@ -67,6 +66,7 @@
     private final String deviceKeyId1 = "DeviceKeyId1";
     private final String deviceKeyId2 = "DeviceKeyId2";
     private final String deviceKeyId3 = "DeviceKeyId3";
+    private final String deviceKeyId4 = "DeviceKeyId4";
     private final String deviceKeyLabel = "DeviceKeyLabel";
     private final String deviceKeyCommunityName = "DeviceKeyCommunityName";
     private final String deviceKeyUsername = "DeviceKeyUsername";
@@ -75,7 +75,11 @@
     private final DeviceKey deviceKey1 = DeviceKey.createDeviceKeyUsingCommunityName(
             DeviceKeyId.deviceKeyId(deviceKeyId1), deviceKeyLabel, deviceKeyCommunityName);
     private final DeviceKey deviceKey2 = DeviceKey.createDeviceKeyUsingUsernamePassword(
-            DeviceKeyId.deviceKeyId(deviceKeyId2), deviceKeyLabel, deviceKeyUsername, deviceKeyPassword);
+            DeviceKeyId.deviceKeyId(deviceKeyId2), null, deviceKeyUsername, deviceKeyPassword);
+    private final DeviceKey deviceKey3 = DeviceKey.createDeviceKeyUsingUsernamePassword(
+            DeviceKeyId.deviceKeyId(deviceKeyId3), null, null, null);
+    private final DeviceKey deviceKey4 = DeviceKey.createDeviceKeyUsingCommunityName(
+            DeviceKeyId.deviceKeyId(deviceKeyId4), null, null);
 
     /**
      * Initializes test mocks and environment.
@@ -97,15 +101,6 @@
     }
 
     /**
-     * Verifies test mocks.
-     */
-    @After
-    public void tearDownMocks() {
-        verify(mockDeviceKeyService);
-        verify(mockDeviceKeyAdminService);
-    }
-
-    /**
      * Hamcrest matcher to check that a device key representation in JSON matches
      * the actual device key.
      */
@@ -127,10 +122,12 @@
             }
 
             // Check the device key label
-            final String jsonLabel = jsonHost.get(LABEL).asString();
-            if (!jsonLabel.equals(deviceKey.label().toString())) {
-                reason = LABEL + " " + deviceKey.label().toString();
-                return false;
+            final String jsonLabel = (jsonHost.get(LABEL).isNull()) ? null : jsonHost.get(LABEL).asString();
+            if (deviceKey.label() != null) {
+                if ((jsonLabel == null) || !jsonLabel.equals(deviceKey.label())) {
+                    reason = LABEL + " " + deviceKey.label();
+                    return false;
+                }
             }
 
             // Check the device key type
@@ -142,24 +139,33 @@
 
             if (jsonType.equals(DeviceKey.Type.COMMUNITY_NAME.toString())) {
                 // Check the device key community name
-                final String jsonCommunityName = jsonHost.get(COMMUNITY_NAME).asString();
-                if (!jsonCommunityName.equals(deviceKey.asCommunityName().name().toString())) {
-                    reason = COMMUNITY_NAME + " " + deviceKey.asCommunityName().name().toString();
-                    return false;
+                final String jsonCommunityName = jsonHost.get(COMMUNITY_NAME).isNull() ?
+                        null : jsonHost.get(COMMUNITY_NAME).asString();
+                if (deviceKey.asCommunityName().name() != null) {
+                    if (!jsonCommunityName.equals(deviceKey.asCommunityName().name().toString())) {
+                        reason = COMMUNITY_NAME + " " + deviceKey.asCommunityName().name().toString();
+                        return false;
+                    }
                 }
             } else if (jsonType.equals(DeviceKey.Type.USERNAME_PASSWORD.toString())) {
                 // Check the device key username
-                final String jsonUsername = jsonHost.get(USERNAME).asString();
-                if (!jsonUsername.equals(deviceKey.asUsernamePassword().username().toString())) {
-                    reason = USERNAME + " " + deviceKey.asUsernamePassword().username().toString();
-                    return false;
+                final String jsonUsername = jsonHost.get(USERNAME).isNull() ?
+                        null : jsonHost.get(USERNAME).asString();
+                if (deviceKey.asUsernamePassword().username() != null) {
+                    if (!jsonUsername.equals(deviceKey.asUsernamePassword().username().toString())) {
+                        reason = USERNAME + " " + deviceKey.asUsernamePassword().username().toString();
+                        return false;
+                    }
                 }
 
                 // Check the device key password
-                final String jsonPassword = jsonHost.get(PASSWORD).asString();
-                if (!jsonPassword.equals(deviceKey.asUsernamePassword().password().toString())) {
-                    reason = PASSWORD + " " + deviceKey.asUsernamePassword().password().toString();
-                    return false;
+                final String jsonPassword = jsonHost.get(PASSWORD).isNull() ?
+                        null : jsonHost.get(PASSWORD).asString();
+                if (deviceKey.asUsernamePassword().password() != null) {
+                    if (!jsonPassword.equals(deviceKey.asUsernamePassword().password().toString())) {
+                        reason = PASSWORD + " " + deviceKey.asUsernamePassword().password().toString();
+                        return false;
+                    }
                 }
             } else {
                 reason = "Unknown " + TYPE + " " + deviceKey.type().toString();
@@ -251,11 +257,12 @@
     @Test
     public void testGetDeviceKeysEmptyArray() {
         replay(mockDeviceKeyService);
-        replay(mockDeviceKeyAdminService);
 
         WebResource rs = resource();
         String response = rs.path("keys").get(String.class);
         assertThat(response, is("{\"keys\":[]}"));
+
+        verify(mockDeviceKeyService);
     }
 
     /**
@@ -264,9 +271,10 @@
     @Test
     public void testGetDeviceKeysArray() {
         replay(mockDeviceKeyService);
-        replay(mockDeviceKeyAdminService);
         deviceKeySet.add(deviceKey1);
         deviceKeySet.add(deviceKey2);
+        deviceKeySet.add(deviceKey3);
+        deviceKeySet.add(deviceKey4);
 
         WebResource rs = resource();
         String response = rs.path("keys").get(String.class);
@@ -280,10 +288,14 @@
 
         final JsonArray deviceKeys = result.get("keys").asArray();
         assertThat(deviceKeys, notNullValue());
-        assertEquals("Device keys array is not the correct size.", 2, deviceKeys.size());
+        assertEquals("Device keys array is not the correct size.", 4, deviceKeys.size());
 
         assertThat(deviceKeys, hasDeviceKey(deviceKey1));
         assertThat(deviceKeys, hasDeviceKey(deviceKey2));
+        assertThat(deviceKeys, hasDeviceKey(deviceKey3));
+        assertThat(deviceKeys, hasDeviceKey(deviceKey4));
+
+        verify(mockDeviceKeyService);
     }
 
     /**
@@ -297,7 +309,6 @@
                 .andReturn(deviceKey1)
                 .anyTimes();
         replay(mockDeviceKeyService);
-        replay(mockDeviceKeyAdminService);
 
         WebResource rs = resource();
         String response = rs.path("keys/" + deviceKeyId1).get(String.class);
@@ -305,6 +316,8 @@
         assertThat(result, notNullValue());
 
         assertThat(result, matchesDeviceKey(deviceKey1));
+
+        verify(mockDeviceKeyService);
     }
 
     /**
@@ -317,7 +330,6 @@
                 .andReturn(null)
                 .anyTimes();
         replay(mockDeviceKeyService);
-        replay(mockDeviceKeyAdminService);
 
         WebResource rs = resource();
         try {
@@ -327,6 +339,8 @@
             assertThat(ex.getMessage(),
                        containsString("returned a response status of"));
         }
+
+        verify(mockDeviceKeyService);
     }
 
     /**
@@ -338,7 +352,6 @@
         mockDeviceKeyAdminService.addKey(anyObject());
         expectLastCall();
 
-        replay(mockDeviceKeyService);
         replay(mockDeviceKeyAdminService);
 
         WebResource rs = resource();
@@ -352,6 +365,8 @@
 
         String location = response.getLocation().getPath();
         assertThat(location, Matchers.startsWith("/keys/" + deviceKeyId3));
+
+        verify(mockDeviceKeyAdminService);
     }
 
     /**
@@ -360,7 +375,6 @@
     @Test
     public void testPostNullDeviceKey() {
 
-        replay(mockDeviceKeyService);
         replay(mockDeviceKeyAdminService);
 
         WebResource rs = resource();
@@ -373,6 +387,8 @@
             assertThat(ex.getMessage(),
                        containsString("returned a response status of"));
         }
+
+        verify(mockDeviceKeyAdminService);
     }
 
     /**
@@ -395,6 +411,9 @@
                 .type(MediaType.APPLICATION_JSON_TYPE)
                 .delete(ClientResponse.class);
         assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
+
+        verify(mockDeviceKeyService);
+        verify(mockDeviceKeyAdminService);
     }
 
     /**
@@ -421,5 +440,8 @@
             assertThat(ex.getMessage(),
                        containsString("returned a response status of"));
         }
+
+        verify(mockDeviceKeyService);
+        verify(mockDeviceKeyAdminService);
     }
 }
\ No newline at end of file