This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/helix.git


The following commit(s) were added to refs/heads/master by this push:
     new c5dbb64  Implementation of stateModelDef modification in REST 2.0
c5dbb64 is described below

commit c5dbb640a72a1afeb72e2113d0a1ae8a48f9e6b6
Author: Kai Sun <[email protected]>
AuthorDate: Tue Jul 16 18:36:42 2019 -0700

    Implementation of stateModelDef modification in REST 2.0
    
    Current implementation of Rest 2.0 does not support stateModelDef 
modification. Here, we will implement
    
    delete -- remove the stateModelDef with the input id.
    
    put -- create new statemodeldef if no existing one with same input id
    
    set -- replace the content of node with input id
    
    We also add the following test cases:
    
    Test delete model one; expect success
    Test delete model one again; expect success
    Create the deleted model one; expect success
    Create the deleted model one again; expect failure as the same model id 
exists
    Set the model one with modified content; expect success
    Read the model one; expect the content would be same as modified content
    Set the model one to original content restore original state; expect success
---
 .../server/resources/helix/ClusterAccessor.java    | 95 ++++++++++++++++++++--
 .../helix/rest/server/TestClusterAccessor.java     | 76 ++++++++++++++++-
 .../helix/rest/server/TestNamespacedAPIAccess.java |  4 +-
 .../helix/rest/server/TestPerInstanceAccessor.java |  4 +-
 .../helix/rest/server/TestResourceAccessor.java    |  8 +-
 .../apache/helix/rest/server/TestTaskAccessor.java |  2 +-
 6 files changed, 168 insertions(+), 21 deletions(-)

diff --git 
a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
 
b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
index 2266f97..edfc847 100644
--- 
a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
+++ 
b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
@@ -35,12 +35,14 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.helix.AccessOption;
 import org.apache.helix.ConfigAccessor;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey;
+import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.ZNRecord;
 import org.apache.helix.manager.zk.ZKUtil;
 import org.apache.helix.manager.zk.client.HelixZkClient;
@@ -417,23 +419,98 @@ public class ClusterAccessor extends 
AbstractHelixResource {
   }
 
   @GET
-  @Path("{clusterId}/maintenance")
-  public Response getClusterMaintenanceMode(@PathParam("clusterId") String 
clusterId) {
-    return JSONRepresentation(
-        ImmutableMap.of(ClusterProperties.maintenance.name(), 
getHelixAdmin().isInMaintenanceMode(clusterId)));
-  }
-
-  @GET
   @Path("{clusterId}/statemodeldefs/{statemodel}")
   public Response getClusterStateModelDefinition(@PathParam("clusterId") 
String clusterId,
       @PathParam("statemodel") String statemodel) {
     HelixDataAccessor dataAccessor = getDataAccssor(clusterId);
-    StateModelDefinition stateModelDef =
-        
dataAccessor.getProperty(dataAccessor.keyBuilder().stateModelDef(statemodel));
+    StateModelDefinition stateModelDef = 
dataAccessor.getProperty(dataAccessor.keyBuilder().stateModelDef(statemodel));
 
+    if (stateModelDef == null) {
+      return badRequest("Statemodel not found!");
+    }
     return JSONRepresentation(stateModelDef.getRecord());
   }
 
+  @PUT
+  @Path("{clusterId}/statemodeldefs/{statemodel}")
+  public Response createClusterStateModelDefinition(@PathParam("clusterId") 
String clusterId,
+      @PathParam("statemodel") String statemodel, String content) {
+    ZNRecord record;
+    try {
+      record = toZNRecord(content);
+    } catch (IOException e) {
+      _logger.error("Failed to deserialize user's input " + content + ", 
Exception: " + e);
+      return badRequest("Input is not a valid ZNRecord!");
+    }
+    HelixZkClient zkClient = getHelixZkClient();
+    String path = PropertyPathBuilder.stateModelDef(clusterId);
+    try {
+      ZKUtil.createChildren(zkClient, path, record);
+    } catch (Exception e) {
+      _logger.error("Failed to create zk node with path " + path + ", 
Exception:" + e);
+      return badRequest("Failed to create a Znode for stateModel! " + e);
+    }
+
+    return OK();
+  }
+
+  @POST
+  @Path("{clusterId}/statemodeldefs/{statemodel}")
+  public Response setClusterStateModelDefinition(@PathParam("clusterId") 
String clusterId,
+      @PathParam("statemodel") String statemodel, String content) {
+    ZNRecord record;
+    try {
+      record = toZNRecord(content);
+    } catch (IOException e) {
+      _logger.error("Failed to deserialize user's input " + content + ", 
Exception: " + e);
+      return badRequest("Input is not a valid ZNRecord!");
+    }
+
+    StateModelDefinition stateModelDefinition = new 
StateModelDefinition(record);
+    HelixDataAccessor dataAccessor = getDataAccssor(clusterId);
+
+    PropertyKey key = 
dataAccessor.keyBuilder().stateModelDef(stateModelDefinition.getId());
+    boolean retcode = true;
+    try {
+      retcode = dataAccessor.setProperty(key, stateModelDefinition);
+    } catch (Exception e) {
+      _logger.error("Failed to set StateModelDefinition key:" + key + ", 
Exception: " + e);
+      return badRequest("Failed to set the content " + content);
+    }
+
+    return OK();
+  }
+
+  @DELETE
+  @Path("{clusterId}/statemodeldefs/{statemodel}")
+  public Response removeClusterStateModelDefinition(@PathParam("clusterId") 
String clusterId,
+      @PathParam("statemodel") String statemodel) {
+    //Shall we validate the statemodel string not having special character 
such as ../ etc?
+    if (!StringUtils.isAlphanumeric(statemodel)) {
+      return badRequest("Invalid statemodel name!");
+    }
+
+    HelixDataAccessor dataAccessor = getDataAccssor(clusterId);
+    PropertyKey key = dataAccessor.keyBuilder().stateModelDef(statemodel);
+    boolean retcode = true;
+    try {
+      retcode = dataAccessor.removeProperty(key);
+    } catch (Exception e) {
+      _logger.error("Failed to remove StateModelDefinition key:" + key + ", 
Exception: " + e);
+      retcode = false;
+    }
+    if (!retcode) {
+      return badRequest("Failed to remove!");
+    }
+    return OK();
+  }
+
+  @GET
+  @Path("{clusterId}/maintenance")
+  public Response getClusterMaintenanceMode(@PathParam("clusterId") String 
clusterId) {
+    return JSONRepresentation(
+        ImmutableMap.of(ClusterProperties.maintenance.name(), 
getHelixAdmin().isInMaintenanceMode(clusterId)));
+  }
   private boolean doesClusterExist(String cluster) {
     HelixZkClient zkClient = getHelixZkClient();
     return ZKUtil.isClusterSetup(cluster, zkClient);
diff --git 
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
 
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
index ab43ae0..6a28937 100644
--- 
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
+++ 
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
@@ -29,6 +29,7 @@ import java.util.Map;
 import java.util.Set;
 
 import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
@@ -320,7 +321,7 @@ public class TestClusterAccessor extends AbstractTestClass {
         Response.Status.NOT_FOUND.getStatusCode(), false);
   }
 
-  @Test(dependsOnMethods = "testEnableDisableMaintenanceModeWithCustomFields")
+  @Test(dependsOnMethods = "testEnableDisableMaintenanceMode")
   public void testGetControllerLeadershipHistory() throws IOException {
     System.out.println("Start test :" + TestHelper.getTestMethodName());
     String cluster = _clusters.iterator().next();
@@ -380,7 +381,7 @@ public class TestClusterAccessor extends AbstractTestClass {
     Assert.assertTrue(lastMaintenanceEntry.contains(reason));
   }
 
-  @Test(dependsOnMethods = "testEnableDisableMaintenanceMode")
+  @Test(dependsOnMethods = "testGetMaintenanceHistory")
   public void testEnableDisableMaintenanceModeWithCustomFields() {
     System.out.println("Start test :" + TestHelper.getTestMethodName());
     String cluster = _clusters.iterator().next();
@@ -405,7 +406,76 @@ public class TestClusterAccessor extends AbstractTestClass 
{
         
accessor.getBaseDataAccessor().exists(accessor.keyBuilder().maintenance().getPath(),
 0));
   }
 
-  @Test()
+  @Test(dependsOnMethods = "testEnableDisableMaintenanceModeWithCustomFields")
+  public void testGetStateModelDef() throws IOException {
+
+    System.out.println("Start test :" + TestHelper.getTestMethodName());
+    String cluster = "TestCluster_1";
+    String urlBase = "clusters/TestCluster_1/statemodeldefs/";
+    String stateModelDefs =
+        get(urlBase, null, Response.Status.OK.getStatusCode(), true);
+    Map<String, Object> defMap = OBJECT_MAPPER.readValue(stateModelDefs, new 
TypeReference<HashMap<String, Object>>() {
+    });
+
+    Assert.assertTrue(defMap.size() == 2);
+    Assert.assertTrue(defMap.get("stateModelDefinitions") instanceof List);
+    List<String> stateModelNames = (List<String>) 
defMap.get("stateModelDefinitions");
+    Assert.assertEquals(stateModelNames.size(), 6);
+
+    String oneModel = stateModelNames.get(1);
+    String twoModel = stateModelNames.get(2);
+
+    String oneModelUri = urlBase + oneModel;
+    String oneResult = get(oneModelUri, null, 
Response.Status.OK.getStatusCode(), true);
+    ZNRecord oneRecord = OBJECT_MAPPER.readValue(oneResult, ZNRecord.class);
+
+    String twoResult =
+        get("clusters/" + cluster + "/statemodeldefs/" + twoModel, null, 
Response.Status.OK.getStatusCode(), true);
+    ZNRecord twoRecord = OBJECT_MAPPER.readValue(twoResult, ZNRecord.class);
+
+    // delete one, expect success
+    String deleteOneUri = urlBase + oneRecord.getId();
+    Response deleteOneRsp = target(deleteOneUri).request().delete();
+    Assert.assertEquals(deleteOneRsp.getStatus(), 
Response.Status.OK.getStatusCode());
+
+    Response queryRsp = target(oneModelUri).request().get();
+    Assert.assertTrue(queryRsp.getStatus() == 
Response.Status.BAD_REQUEST.getStatusCode());
+
+    // delete one again, expect success
+    Response deleteOneRsp2 = target(deleteOneUri).request().delete();
+    Assert.assertTrue(deleteOneRsp2.getStatus() == 
Response.Status.OK.getStatusCode());
+
+    // create the delete one, expect success
+    Response createOneRsp = target(oneModelUri).request()
+        .put(Entity.entity(OBJECT_MAPPER.writeValueAsString(oneRecord), 
MediaType.APPLICATION_JSON_TYPE));
+    Assert.assertTrue(createOneRsp.getStatus() == 
Response.Status.OK.getStatusCode());
+
+    // create the delete one again, expect failure
+    Response createOneRsp2 = target(oneModelUri).request()
+        .put(Entity.entity(OBJECT_MAPPER.writeValueAsString(oneRecord), 
MediaType.APPLICATION_JSON_TYPE));
+    Assert.assertTrue(createOneRsp2.getStatus() == 
Response.Status.BAD_REQUEST.getStatusCode());
+
+    // set the delete one with a modification
+    ZNRecord newRecord = new ZNRecord(twoRecord, oneRecord.getId());
+    Response setOneRsp = target(oneModelUri).request()
+        .post(Entity.entity(OBJECT_MAPPER.writeValueAsString(newRecord), 
MediaType.APPLICATION_JSON_TYPE));
+    Assert.assertTrue(setOneRsp.getStatus() == 
Response.Status.OK.getStatusCode());
+
+    String oneResult2 = get(oneModelUri, null, 
Response.Status.OK.getStatusCode(), true);
+    ZNRecord oneRecord2 = OBJECT_MAPPER.readValue(oneResult2, ZNRecord.class);
+    Assert.assertEquals(oneRecord2, newRecord);
+
+    // set the delete one with original; namely restore the original condition
+    Response setOneRsp2 = target(oneModelUri).request()
+        .post(Entity.entity(OBJECT_MAPPER.writeValueAsString(oneRecord), 
MediaType.APPLICATION_JSON_TYPE));
+    Assert.assertTrue(setOneRsp2.getStatus() == 
Response.Status.OK.getStatusCode());
+
+    String oneResult3 = get(oneModelUri, null, 
Response.Status.OK.getStatusCode(), true);
+    ZNRecord oneRecord3 = OBJECT_MAPPER.readValue(oneResult3, ZNRecord.class);
+    Assert.assertEquals(oneRecord3, oneRecord);
+  }
+
+  @Test(dependsOnMethods = "testGetStateModelDef")
   public void testActivateSuperCluster() throws Exception {
     System.out.println("Start test :" + TestHelper.getTestMethodName());
     final String ACTIVATE_SUPER_CLUSTER = 
"RestSuperClusterActivationTest_SuperCluster";
diff --git 
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestNamespacedAPIAccess.java
 
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestNamespacedAPIAccess.java
index 9ebad96..ffee3e1 100644
--- 
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestNamespacedAPIAccess.java
+++ 
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestNamespacedAPIAccess.java
@@ -58,7 +58,7 @@ public class TestNamespacedAPIAccess extends 
AbstractTestClass {
   }
 
 
-  @Test
+  @Test(dependsOnMethods = "testDefaultNamespaceCompatibility")
   public void testNamespacedCRUD() throws IOException {
     String testClusterName = "testClusterForNamespacedCRUD";
 
@@ -91,7 +91,7 @@ public class TestNamespacedAPIAccess extends 
AbstractTestClass {
     get(String.format("/clusters/%s", testClusterName), null, 
Response.Status.OK.getStatusCode(), false);
   }
 
-  @Test
+  @Test(dependsOnMethods = "testNamespacedCRUD")
   public void testNamespaceServer() throws IOException {
     // Default endpoints should not have any namespace information returned
     get("/", null, Response.Status.NOT_FOUND.getStatusCode(), false);
diff --git 
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
 
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
index b3acea8..d9ef426 100644
--- 
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
+++ 
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
@@ -161,7 +161,7 @@ public class TestPerInstanceAccessor extends 
AbstractTestClass {
         _configAccessor.getInstanceConfig(CLUSTER_NAME, INSTANCE_NAME));
   }
 
-  @Test(dependsOnMethods = "updateInstance")
+  @Test(dependsOnMethods = "testGetInstanceById")
   public void testAddInstance() throws IOException {
     System.out.println("Start test :" + TestHelper.getTestMethodName());
     InstanceConfig instanceConfig = new InstanceConfig(INSTANCE_NAME + "TEST");
@@ -183,7 +183,7 @@ public class TestPerInstanceAccessor extends 
AbstractTestClass {
     _configAccessor.getInstanceConfig(CLUSTER_NAME, INSTANCE_NAME + "TEST");
   }
 
-  @Test(dependsOnMethods = "testGetInstanceById")
+  @Test(dependsOnMethods = "testDeleteInstance")
   public void updateInstance() throws IOException {
     System.out.println("Start test :" + TestHelper.getTestMethodName());
     // Disable instance
diff --git 
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestResourceAccessor.java
 
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestResourceAccessor.java
index f136d9f..1ad638a 100644
--- 
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestResourceAccessor.java
+++ 
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestResourceAccessor.java
@@ -133,7 +133,7 @@ public class TestResourceAccessor extends AbstractTestClass 
{
         _configAccessor.getResourceConfig(CLUSTER_NAME, RESOURCE_NAME));
   }
 
-  @Test(dependsOnMethods = "testAddResources")
+  @Test(dependsOnMethods = "testResourceConfig")
   public void testIdealState() throws IOException {
     System.out.println("Start test :" + TestHelper.getTestMethodName());
 
@@ -144,7 +144,7 @@ public class TestResourceAccessor extends AbstractTestClass 
{
         
_gSetupTool.getClusterManagementTool().getResourceIdealState(CLUSTER_NAME, 
RESOURCE_NAME));
   }
 
-  @Test(dependsOnMethods = "testAddResources")
+  @Test(dependsOnMethods = "testIdealState")
   public void testExternalView() throws IOException {
     System.out.println("Start test :" + TestHelper.getTestMethodName());
 
@@ -155,7 +155,7 @@ public class TestResourceAccessor extends AbstractTestClass 
{
         .getResourceExternalView(CLUSTER_NAME, RESOURCE_NAME));
   }
 
-  @Test
+  @Test(dependsOnMethods = "testExternalView")
   public void testPartitionHealth() throws Exception {
     System.out.println("Start test :" + TestHelper.getTestMethodName());
 
@@ -380,7 +380,7 @@ public class TestResourceAccessor extends AbstractTestClass 
{
    * Test "update" command of updateResourceIdealState.
    * @throws Exception
    */
-  @Test(dependsOnMethods = "testResourceHealth")
+  @Test(dependsOnMethods = "deleteFromResourceConfig")
   public void updateResourceIdealState() throws Exception {
     // Get IdealState ZNode
     String zkPath = PropertyPathBuilder.idealState(CLUSTER_NAME, 
RESOURCE_NAME);
diff --git 
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestTaskAccessor.java 
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestTaskAccessor.java
index 3f9a0e2..4753e27 100644
--- 
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestTaskAccessor.java
+++ 
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestTaskAccessor.java
@@ -58,7 +58,7 @@ public class TestTaskAccessor extends AbstractTestClass {
     Assert.assertEquals(contentStore, map1);
   }
 
-  @Test
+  @Test(dependsOnMethods = "testGetAddTaskUserContent")
   public void testInvalidGetAddTaskUserContent() {
     System.out.println("Start test :" + TestHelper.getTestMethodName());
     String validURI = "clusters/" + CLUSTER_NAME + 
"/workflows/Workflow_0/jobs/Job_0/tasks/0/userContent";

Reply via email to