Return-Path: X-Original-To: apmail-kylin-commits-archive@minotaur.apache.org Delivered-To: apmail-kylin-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 3D93817644 for ; Wed, 2 Sep 2015 01:53:42 +0000 (UTC) Received: (qmail 73201 invoked by uid 500); 2 Sep 2015 01:53:42 -0000 Delivered-To: apmail-kylin-commits-archive@kylin.apache.org Received: (qmail 73173 invoked by uid 500); 2 Sep 2015 01:53:42 -0000 Mailing-List: contact commits-help@kylin.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@kylin.incubator.apache.org Delivered-To: mailing list commits@kylin.incubator.apache.org Received: (qmail 73164 invoked by uid 99); 2 Sep 2015 01:53:42 -0000 Received: from Unknown (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 02 Sep 2015 01:53:42 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id 9C780F0A07 for ; Wed, 2 Sep 2015 01:53:41 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.771 X-Spam-Level: * X-Spam-Status: No, score=1.771 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, T_RP_MATCHES_RCVD=-0.01, URIBL_BLOCKED=0.001] autolearn=disabled Received: from mx1-eu-west.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id Ev0F3uCPPd1h for ; Wed, 2 Sep 2015 01:53:29 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-eu-west.apache.org (ASF Mail Server at mx1-eu-west.apache.org) with SMTP id 68AC824C0E for ; Wed, 2 Sep 2015 01:53:27 +0000 (UTC) Received: (qmail 73115 invoked by uid 99); 2 Sep 2015 01:53:26 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 02 Sep 2015 01:53:26 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 823A0E0967; Wed, 2 Sep 2015 01:53:26 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: shaofengshi@apache.org To: commits@kylin.incubator.apache.org Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: incubator-kylin git commit: KYLIN-958 disallow changing data model in the backend Date: Wed, 2 Sep 2015 01:53:26 +0000 (UTC) Repository: incubator-kylin Updated Branches: refs/heads/0.7-staging 6ba5b51f2 -> dbbbc1e55 KYLIN-958 disallow changing data model in the backend Signed-off-by: shaofengshi Project: http://git-wip-us.apache.org/repos/asf/incubator-kylin/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-kylin/commit/dbbbc1e5 Tree: http://git-wip-us.apache.org/repos/asf/incubator-kylin/tree/dbbbc1e5 Diff: http://git-wip-us.apache.org/repos/asf/incubator-kylin/diff/dbbbc1e5 Branch: refs/heads/0.7-staging Commit: dbbbc1e5541ee6f2cce272ec48c518624bc5392f Parents: 6ba5b51 Author: gaodayue Authored: Thu Aug 27 14:00:25 2015 +0800 Committer: shaofengshi Committed: Wed Sep 2 09:51:54 2015 +0800 ---------------------------------------------------------------------- .../common/persistence/HBaseResourceStore.java | 2 +- .../org/apache/kylin/cube/CubeDescManager.java | 14 +- .../kylin/metadata/model/DataModelDesc.java | 41 +++++ .../kylin/rest/controller/CubeController.java | 76 +++++---- .../apache/kylin/rest/service/CubeService.java | 9 +- .../rest/controller/CubeControllerTest.java | 171 ++++++++++++++----- 6 files changed, 228 insertions(+), 85 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/dbbbc1e5/common/src/main/java/org/apache/kylin/common/persistence/HBaseResourceStore.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/kylin/common/persistence/HBaseResourceStore.java b/common/src/main/java/org/apache/kylin/common/persistence/HBaseResourceStore.java index 37b8f8d..1c4a7ba 100644 --- a/common/src/main/java/org/apache/kylin/common/persistence/HBaseResourceStore.java +++ b/common/src/main/java/org/apache/kylin/common/persistence/HBaseResourceStore.java @@ -237,7 +237,7 @@ public class HBaseResourceStore extends ResourceStore { boolean ok = table.checkAndPut(row, B_FAMILY, B_COLUMN_TS, bOldTS, put); if (!ok) { long real = getResourceTimestamp(resPath); - throw new IllegalStateException("Overwriting conflict " + resPath + ", expect old TS " + oldTS + ", but it is " + real); + throw new IllegalStateException("Overwriting conflict " + resPath + ", expect old TS " + real + ", but it is " + oldTS); } table.flushCommits(); http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/dbbbc1e5/cube/src/main/java/org/apache/kylin/cube/CubeDescManager.java ---------------------------------------------------------------------- diff --git a/cube/src/main/java/org/apache/kylin/cube/CubeDescManager.java b/cube/src/main/java/org/apache/kylin/cube/CubeDescManager.java index cd270cf..dfb1b88 100644 --- a/cube/src/main/java/org/apache/kylin/cube/CubeDescManager.java +++ b/cube/src/main/java/org/apache/kylin/cube/CubeDescManager.java @@ -22,7 +22,7 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.ConcurrentHashMap; -import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang.StringUtils; import org.apache.kylin.common.KylinConfig; import org.apache.kylin.common.persistence.JsonSerializer; import org.apache.kylin.common.persistence.ResourceStore; @@ -253,6 +253,14 @@ public class CubeDescManager { desc.setSignature(desc.calculateSignature()); + // drop cube segments if signature changes + CubeInstance cube = getCubeManager().getCube(desc.getName()); + if (cube != null && !StringUtils.equals(desc.getSignature(), cube.getDescriptor().getSignature())) { + logger.info("Detect signature change of [" + desc.getName() + "], drop all existing segments"); + cube.getSegments().clear(); + getCubeManager().updateCube(cube); + } + // Save Source String path = desc.getResourcePath(); getStore().putResource(path, desc, CUBE_DESC_SERIALIZER); @@ -269,6 +277,10 @@ public class CubeDescManager { return MetadataManager.getInstance(config); } + private CubeManager getCubeManager() { + return CubeManager.getInstance(config); + } + private ResourceStore getStore() { return ResourceStore.getStore(this.config); } http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/dbbbc1e5/metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java ---------------------------------------------------------------------- diff --git a/metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java b/metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java index a37d4c6..cb1e784 100644 --- a/metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java +++ b/metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java @@ -18,14 +18,17 @@ package org.apache.kylin.metadata.model; +import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Map; import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; import org.apache.kylin.common.persistence.ResourceStore; import org.apache.kylin.common.persistence.RootPersistentEntity; +import org.apache.kylin.common.util.JsonUtil; import org.apache.kylin.common.util.StringUtil; import org.apache.kylin.metadata.MetadataConstants; @@ -33,9 +36,12 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.Sets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE) public class DataModelDesc extends RootPersistentEntity { + private static final Logger logger = LoggerFactory.getLogger(DataModelDesc.class); public static enum RealizationCapacity { SMALL, MEDIUM, LARGE @@ -213,6 +219,41 @@ public class DataModelDesc extends RootPersistentEntity { } } + /** + * Check whether two data model are compatible or not. Compatible means + * having the same structure. Tow models could be compatible even they + * have different UUID or last modified time. + * @param that model to compare with + * @return true if compatible, false otherwise. + */ + public boolean compatibleWith(DataModelDesc that) { + if (this == that) + return true; + + if (that == null) + return false; + + try { + String thisRepr = excludeHeaderInfo(this); + String thatRepr = excludeHeaderInfo(that); + return StringUtils.equals(thisRepr, thatRepr); + + } catch (IOException e) { + logger.error("Failed to serialize DataModelDesc to string", e); + return false; + } + } + + private String excludeHeaderInfo(DataModelDesc modelDesc) throws IOException { + // make a copy + String repr = JsonUtil.writeValueAsString(modelDesc); + DataModelDesc copy = JsonUtil.readValue(repr, DataModelDesc.class); + + copy.setUuid(null); + copy.setLastModified(0); + return JsonUtil.writeValueAsString(copy); + } + @Override public String toString() { return "DataModelDesc [name=" + name + "]"; http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/dbbbc1e5/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java ---------------------------------------------------------------------- diff --git a/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java b/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java index 9f29753..734ef32 100644 --- a/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java +++ b/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java @@ -330,56 +330,57 @@ public class CubeController extends BasicController { } /** - * Get available table list of the input database + * Update cube description. If cube signature has changed, all existing cube segments are dropped. * * @return Table metadata array * @throws JsonProcessingException - * @throws IOException */ @RequestMapping(value = "", method = { RequestMethod.PUT }) @ResponseBody public CubeRequest updateCubeDesc(@RequestBody CubeRequest cubeRequest) throws JsonProcessingException { - - //Update Model - MetadataManager metaManager = MetadataManager.getInstance(KylinConfig.getInstanceFromEnv()); - DataModelDesc modelDesc = deserializeDataModelDesc(cubeRequest); - if (modelDesc == null) { + CubeDesc desc = deserializeCubeDesc(cubeRequest); + if (desc == null) { return cubeRequest; } - try { - DataModelDesc existingModel = metaManager.getDataModelDesc(modelDesc.getName()); - if (existingModel == null) { - metaManager.createDataModelDesc(modelDesc); - } else { + final String cubeName = desc.getName(); + if (StringUtils.isEmpty(cubeName)) { + return errorRequest(cubeRequest, "Missing cubeName"); + } - //ignore overwriting conflict checking before splict MODEL & CUBE - modelDesc.setLastModified(existingModel.getLastModified()); - metaManager.updateDataModelDesc(modelDesc); + MetadataManager metadataManager = MetadataManager.getInstance(KylinConfig.getInstanceFromEnv()); + // KYLIN-958: disallow data model change + if (StringUtils.isNotEmpty(cubeRequest.getModelDescData())) { + DataModelDesc modelDesc = deserializeDataModelDesc(cubeRequest); + if (modelDesc == null) { + return cubeRequest; } - } catch (IOException e) { - // TODO Auto-generated catch block - logger.error("Failed to deal with the request:" + e.getLocalizedMessage(), e); - throw new InternalErrorException("Failed to deal with the request: " + e.getLocalizedMessage()); - } + final String modeName = modelDesc.getName(); - //update cube - CubeDesc desc = deserializeCubeDesc(cubeRequest); + if (!StringUtils.equals(desc.getModelName(), modeName)) { + return errorRequest(cubeRequest, "CubeDesc.model_name " + desc.getModelName() + " not consistent with model " + modeName); + } + + DataModelDesc oldModelDesc = metadataManager.getDataModelDesc(modeName); + if (oldModelDesc == null) { + return errorRequest(cubeRequest, "Data model " + modeName + " not found"); + } + + if (!modelDesc.compatibleWith(oldModelDesc)) { + return errorRequest(cubeRequest, "Update data model is not allowed! Please create a new cube if needed"); + } - if (desc == null) { - return cubeRequest; } // Check if the cube is editable if (!cubeService.isCubeDescEditable(desc)) { String error = "Cube desc " + desc.getName().toUpperCase() + " is not editable."; - updateRequest(cubeRequest, false, error); - return cubeRequest; + return errorRequest(cubeRequest, error); } try { - CubeInstance cube = cubeService.getCubeManager().getCube(cubeRequest.getCubeName()); + CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); cube.setRetentionRange(desc.getRetentionRange()); String projectName = (null == cubeRequest.getProject()) ? ProjectInstance.DEFAULT_PROJECT_NAME : cubeRequest.getProject(); desc = cubeService.updateCubeAndDesc(cube, desc, projectName); @@ -395,7 +396,7 @@ public class CubeController extends BasicController { cubeRequest.setSuccessful(true); } else { logger.warn("Cube " + desc.getName() + " fail to create because " + desc.getError()); - updateRequest(cubeRequest, false, omitMessage(desc.getError())); + errorRequest(cubeRequest, omitMessage(desc.getError())); } String descData = JsonUtil.writeValueAsIndentString(desc); cubeRequest.setCubeDescData(descData); @@ -449,15 +450,15 @@ public class CubeController extends BasicController { private CubeDesc deserializeCubeDesc(CubeRequest cubeRequest) { CubeDesc desc = null; try { - logger.debug("Saving cube " + cubeRequest.getCubeDescData()); + logger.debug("Deserialize cube desc " + cubeRequest.getCubeDescData()); desc = JsonUtil.readValue(cubeRequest.getCubeDescData(), CubeDesc.class); // desc.setRetentionRange(cubeRequest.getRetentionRange()); } catch (JsonParseException e) { logger.error("The cube definition is not valid.", e); - updateRequest(cubeRequest, false, e.getMessage()); + errorRequest(cubeRequest, e.getMessage()); } catch (JsonMappingException e) { logger.error("The cube definition is not valid.", e); - updateRequest(cubeRequest, false, e.getMessage()); + errorRequest(cubeRequest, e.getMessage()); } catch (IOException e) { logger.error("Failed to deal with the request.", e); throw new InternalErrorException("Failed to deal with the request:" + e.getMessage(), e); @@ -468,14 +469,14 @@ public class CubeController extends BasicController { private DataModelDesc deserializeDataModelDesc(CubeRequest cubeRequest) { DataModelDesc desc = null; try { - logger.debug("Saving MODEL " + cubeRequest.getModelDescData()); + logger.debug("Deserialize data model " + cubeRequest.getModelDescData()); desc = JsonUtil.readValue(cubeRequest.getModelDescData(), DataModelDesc.class); } catch (JsonParseException e) { logger.error("The data model definition is not valid.", e); - updateRequest(cubeRequest, false, e.getMessage()); + errorRequest(cubeRequest, e.getMessage()); } catch (JsonMappingException e) { logger.error("The data model definition is not valid.", e); - updateRequest(cubeRequest, false, e.getMessage()); + errorRequest(cubeRequest, e.getMessage()); } catch (IOException e) { logger.error("Failed to deal with the request.", e); throw new InternalErrorException("Failed to deal with the request:" + e.getMessage(), e); @@ -496,10 +497,11 @@ public class CubeController extends BasicController { return buffer.toString(); } - private void updateRequest(CubeRequest request, boolean success, String message) { + private CubeRequest errorRequest(CubeRequest request, String errmsg) { request.setCubeDescData(""); - request.setSuccessful(success); - request.setMessage(message); + request.setSuccessful(false); + request.setMessage(errmsg); + return request; } public void setCubeService(CubeService cubeService) { http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/dbbbc1e5/server/src/main/java/org/apache/kylin/rest/service/CubeService.java ---------------------------------------------------------------------- diff --git a/server/src/main/java/org/apache/kylin/rest/service/CubeService.java b/server/src/main/java/org/apache/kylin/rest/service/CubeService.java index 21c6ec7..be356af 100644 --- a/server/src/main/java/org/apache/kylin/rest/service/CubeService.java +++ b/server/src/main/java/org/apache/kylin/rest/service/CubeService.java @@ -236,11 +236,11 @@ public class CubeService extends BasicService { } try { - if (!cube.getDescriptor().calculateSignature().equals(cube.getDescriptor().getSignature())) { - this.releaseAllSegments(cube); + CubeDesc updatedCubeDesc = getCubeDescManager().updateCubeDesc(desc); + if (!updatedCubeDesc.getError().isEmpty()) { + return updatedCubeDesc; } - CubeDesc updatedCubeDesc = getCubeDescManager().updateCubeDesc(desc); cube = getCubeManager().updateCube(cube); int cuboidCount = CuboidCLI.simulateCuboidGeneration(updatedCubeDesc); @@ -388,9 +388,6 @@ public class CubeService extends BasicService { if (!cubingJobs.isEmpty()) { throw new JobException("Enable is not allowed with a running job."); } - if (!cube.getDescriptor().calculateSignature().equals(cube.getDescriptor().getSignature())) { - this.releaseAllSegments(cube); - } cube.setStatus(RealizationStatusEnum.READY); try { http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/dbbbc1e5/server/src/test/java/org/apache/kylin/rest/controller/CubeControllerTest.java ---------------------------------------------------------------------- diff --git a/server/src/test/java/org/apache/kylin/rest/controller/CubeControllerTest.java b/server/src/test/java/org/apache/kylin/rest/controller/CubeControllerTest.java index cf1a718..2f12851 100644 --- a/server/src/test/java/org/apache/kylin/rest/controller/CubeControllerTest.java +++ b/server/src/test/java/org/apache/kylin/rest/controller/CubeControllerTest.java @@ -19,32 +19,34 @@ package org.apache.kylin.rest.controller; import java.io.IOException; -import java.io.StringWriter; import java.util.List; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.apache.kylin.common.util.JsonUtil; import org.apache.kylin.cube.CubeInstance; import org.apache.kylin.cube.model.CubeDesc; +import org.apache.kylin.cube.model.RowKeyColDesc; import org.apache.kylin.metadata.model.DataModelDesc; import org.apache.kylin.rest.request.CubeRequest; import org.apache.kylin.rest.service.CubeService; import org.apache.kylin.rest.service.JobService; import org.apache.kylin.rest.service.ServiceTestBase; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import org.springframework.beans.factory.annotation.Autowired; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Lists; /** * @author xduo */ public class CubeControllerTest extends ServiceTestBase { + private static final String SRC_CUBE_NAME = "test_kylin_cube_with_slr_ready"; + private static final String TEST_CUBE_NAME = SRC_CUBE_NAME + "_test_save"; private CubeController cubeController; private CubeDescController cubeDescController; private ModelController modelController; + private CubeDesc srcCubeDesc; @Autowired CubeService cubeService; @@ -52,29 +54,39 @@ public class CubeControllerTest extends ServiceTestBase { JobService jobService; @Before - public void setup() throws Exception { + public void setUp() throws Exception { super.setUp(); - cubeController = new CubeController(); cubeController.setCubeService(cubeService); cubeController.setJobService(jobService); + cubeDescController = new CubeDescController(); cubeDescController.setCubeService(cubeService); modelController = new ModelController(); modelController.setCubeService(cubeService); + + srcCubeDesc = getCubeDescByName(SRC_CUBE_NAME); + + saveTestCube(TEST_CUBE_NAME); } - @Test - public void testBasics() throws IOException { - CubeDesc[] cubes = (CubeDesc[]) cubeDescController.getCube("test_kylin_cube_with_slr_ready"); - Assert.assertNotNull(cubes); - Assert.assertNotNull(cubeController.getSql("test_kylin_cube_with_slr_ready", "20130331080000_20131212080000")); - Assert.assertNotNull(cubeController.getCubes(null, null, 0, 5)); + @After + public void tearDown() throws Exception { + cubeController.deleteCube(TEST_CUBE_NAME); + super.after(); + } + + private CubeDesc getCubeDescByName(String cubeDescName) { + CubeDesc[] cubes = cubeDescController.getCube(cubeDescName); + if (cubes == null || cubes.length < 1) { + throw new IllegalStateException("cube desc " + cubeDescName + " not existed"); + } + return cubes[0]; + } - CubeDesc cube = cubes[0]; + private void saveTestCube(final String newCubeName) throws IOException { CubeDesc newCube = new CubeDesc(); - String newCubeName = cube.getName() + "_test_save"; try { cubeController.deleteCube(newCubeName); @@ -83,45 +95,124 @@ public class CubeControllerTest extends ServiceTestBase { } newCube.setName(newCubeName); - newCube.setModelName(cube.getModelName()); - newCube.setModel(cube.getModel()); - newCube.setDimensions(cube.getDimensions()); - newCube.setHBaseMapping(cube.getHBaseMapping()); - newCube.setMeasures(cube.getMeasures()); - newCube.setConfig(cube.getConfig()); - newCube.setRowkey(cube.getRowkey()); - - String newModelName = newCubeName + "_model_desc"; - newCube.getModel().setName(newModelName);//generate a random model + newCube.setModelName(newCubeName); + newCube.setModel(srcCubeDesc.getModel()); + newCube.setDimensions(srcCubeDesc.getDimensions()); + newCube.setHBaseMapping(srcCubeDesc.getHBaseMapping()); + newCube.setMeasures(srcCubeDesc.getMeasures()); + newCube.setConfig(srcCubeDesc.getConfig()); + newCube.setRowkey(srcCubeDesc.getRowkey()); + + newCube.getModel().setName(newCubeName); newCube.getModel().setLastModified(0); - ObjectMapper cubeDescMapper = new ObjectMapper(); - StringWriter cubeDescWriter = new StringWriter(); - cubeDescMapper.writeValue(cubeDescWriter, newCube); + CubeRequest cubeRequest = new CubeRequest(); + cubeRequest.setCubeDescData(JsonUtil.writeValueAsIndentString(newCube)); + cubeRequest.setModelDescData(JsonUtil.writeValueAsIndentString(newCube.getModel())); - ObjectMapper modelDescMapper = new ObjectMapper(); - StringWriter modelDescWriter = new StringWriter(); - modelDescMapper.writeValue(modelDescWriter, newCube.getModel()); + CubeRequest result = cubeController.saveCubeDesc(cubeRequest); + Assert.assertTrue(result.getSuccessful()); + } - CubeRequest cubeRequest = new CubeRequest(); - cubeRequest.setCubeDescData(cubeDescWriter.toString()); - cubeRequest.setModelDescData(modelDescWriter.toString()); - cubeRequest = cubeController.saveCubeDesc(cubeRequest); + @Test + public void testBasics() throws IOException { + + Assert.assertNotNull(cubeController.getSql(SRC_CUBE_NAME, "20130331080000_20131212080000")); + Assert.assertNotNull(cubeController.getCubes(null, null, 0, 5)); - DataModelDesc model = modelController.getModel(newModelName); + DataModelDesc model = modelController.getModel(TEST_CUBE_NAME); Assert.assertNotNull(model); List notifyList = Lists.newArrayList(); notifyList.add("john@example.com"); - cubeController.updateNotifyList(newCubeName, notifyList); - cubeController.updateCubeCost(newCubeName, 80); + cubeController.updateNotifyList(TEST_CUBE_NAME, notifyList); + cubeController.updateCubeCost(TEST_CUBE_NAME, 80); - List cubeInstances = cubeController.getCubes(newCubeName, "default", 1, 0); + List cubeInstances = cubeController.getCubes(TEST_CUBE_NAME, "default", 1, 0); CubeInstance cubeInstance = cubeInstances.get(0); Assert.assertTrue(cubeInstance.getDescriptor().getNotifyList().contains("john@example.com")); Assert.assertTrue(cubeInstance.getCost() == 80); - cubeController.deleteCube(newCubeName); } + @Test + public void testUpdateCubeDesc() throws IOException { + CubeDesc newCubeDesc = getCubeDescByName(TEST_CUBE_NAME); + + // ------------------------------------------------------- + // negative case + // ------------------------------------------------------- + + // invalid cube desc + CubeRequest req = new CubeRequest(); + req.setCubeDescData("invalid"); + assertUpdateFail(req); + + // invalid data model + req = new CubeRequest(); + req.setCubeDescData(JsonUtil.writeValueAsIndentString(newCubeDesc)); + req.setModelDescData("invalid"); + assertUpdateFail(req); + + // data model's model_name not consistent with model name + req = new CubeRequest(); + req.setCubeDescData("{\"name\" : \"myCube\", \"model_name\" : \"anotherModelName\"}"); + req.setModelDescData("{\"name\" : \"myCube\"}"); + assertUpdateFail(req); + + // non-existed data model + req = new CubeRequest(); + req.setCubeDescData("{\"name\" : \"noSuchCube\", \"model_name\" : \"noSuchModel\"}"); + req.setModelDescData("{\"name\" : \"noSuchModel\"}"); + assertUpdateFail(req); + + // modified data model + req = new CubeRequest(); + req.setCubeDescData(JsonUtil.writeValueAsIndentString(newCubeDesc)); + + DataModelDesc modifiedModel = new DataModelDesc(); + modifiedModel.setName(TEST_CUBE_NAME); + modifiedModel.setFactTable("anotherFactTable"); + req.setModelDescData(JsonUtil.writeValueAsIndentString(modifiedModel)); + + assertUpdateFail(req); + + // ------------------------------------------------------- + // positive case + // ------------------------------------------------------- + req = new CubeRequest(); + req.setModelDescData(JsonUtil.writeValueAsIndentString(newCubeDesc.getModel())); + + // no signature change + newCubeDesc.setDescription("hello cube"); + req.setCubeDescData(JsonUtil.writeValueAsIndentString(newCubeDesc)); + CubeRequest res = cubeController.updateCubeDesc(req); + Assert.assertTrue(res.getSuccessful()); + + CubeDesc resultDesc = getCubeDescByName(TEST_CUBE_NAME); + Assert.assertEquals("hello cube", resultDesc.getDescription()); + Assert.assertEquals(newCubeDesc.getSignature(), resultDesc.getSignature()); + + // signature change (reverse row key column order) + newCubeDesc = getCubeDescByName(TEST_CUBE_NAME); + RowKeyColDesc[] rowkeyColumns = newCubeDesc.getRowkey().getRowKeyColumns(); + for (int i = 0, j = rowkeyColumns.length - 1; i < j; i++, j--) { + RowKeyColDesc tmp = rowkeyColumns[i]; + rowkeyColumns[i] = rowkeyColumns[j]; + rowkeyColumns[j] = tmp; + } + req.setCubeDescData(JsonUtil.writeValueAsIndentString(newCubeDesc)); + res = cubeController.updateCubeDesc(req); + Assert.assertTrue(res.getSuccessful()); + + resultDesc = getCubeDescByName(TEST_CUBE_NAME); + ; + Assert.assertNotEquals(newCubeDesc.getSignature(), resultDesc.getSignature()); + Assert.assertEquals(newCubeDesc.calculateSignature(), resultDesc.getSignature()); + } + + private void assertUpdateFail(CubeRequest req) throws JsonProcessingException { + CubeRequest res = cubeController.updateCubeDesc(req); + Assert.assertFalse(res.getSuccessful()); + } }