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

xxyu pushed a commit to branch kylin5
in repository https://gitbox.apache.org/repos/asf/kylin.git

commit ae851e66eb3ee7993346a765d8ff240f9c7ce889
Author: Jiale He <35652389+jial...@users.noreply.github.com>
AuthorDate: Tue Oct 11 10:38:12 2022 +0800

    KYLIN-5335 Collect CC names and expressions on conflict, and adjust CC names
---
 .../resources/kylin_error_msg_conf_cn.properties   |   2 +-
 .../resources/kylin_error_msg_conf_en.properties   |   2 +-
 .../metadata/model/util/ComputedColumnUtil.java    |   8 +-
 .../apache/kylin/rest/service/ModelService.java    |  60 +++
 .../kylin/rest/service/ModelServiceTest.java       | 500 +++++++++++++++++----
 5 files changed, 473 insertions(+), 99 deletions(-)

diff --git 
a/src/core-common/src/main/resources/kylin_error_msg_conf_cn.properties 
b/src/core-common/src/main/resources/kylin_error_msg_conf_cn.properties
index 759514ee40..327c28d18d 100644
--- a/src/core-common/src/main/resources/kylin_error_msg_conf_cn.properties
+++ b/src/core-common/src/main/resources/kylin_error_msg_conf_cn.properties
@@ -98,7 +98,7 @@ KE-010031201=因为查询结果行数超过最大值 ”%s”,无法完成查
 KE-010031202=SQL 语法或格式异常,请检查并修正后重试。
 
 ## 100102XX computed column
-KE-010010201=模型中定义的可计算列的名和表达式与其它模型存在冲突。
+KE-010010201=模型中定义的可计算列的名和表达式与其它模型存在冲突,请修改名称以保持一致,或使用其他的表达式。
 KE-010010202=重复的可计算列名,名为 “%s” 表达式为 “%s” 的可计算列,与模型 “%s” 中的可计算列名存在冲突。
 KE-010010203=重复的可计算列表达式,名为 “%s” 表达式为 “%s” 的可计算列,与模型 “%s” 中的可计算列表达式存在冲突。
 KE-010010204=名为 “%s” 表达式为 “%s” 的可计算列,与项目中名为 “%s” 表达式为 “%s” 
的可计算列表达式存在冲突,将当前可计算列重命名为 “%s” 。
diff --git 
a/src/core-common/src/main/resources/kylin_error_msg_conf_en.properties 
b/src/core-common/src/main/resources/kylin_error_msg_conf_en.properties
index 02ecccdb25..5a5f9ea5fb 100644
--- a/src/core-common/src/main/resources/kylin_error_msg_conf_en.properties
+++ b/src/core-common/src/main/resources/kylin_error_msg_conf_en.properties
@@ -96,7 +96,7 @@ KE-010031201=Can't get query result, as the rows of query 
result exceeds the max
 KE-010031202=SQL syntax or format is abnormal, please check and fix and try 
again.
 
 ## 100102XX computed column
-KE-010010201=The name and expression of the computed column defined in the 
model conflict with other models.
+KE-010010201=The name and expression of the computed column defined in the 
model conflict with other models. Please modify the name to be consistent, or 
use another expression.
 KE-010010202=Duplicate computed column name, defined computed column named 
"%s" with expression "%s", conflicts with a computed column name in model "%s".
 KE-010010203=Duplicate computed column expression, defined computed column 
named "%s" with expression "%s", conflicts with a computed column expression in 
model "%s".
 KE-010010204=Defined computed column named "%s" with expression "%s" is 
inconsistent with the name of the computed column named "%s" with the 
expression "%s" in the project. Renamed to "%s".
diff --git 
a/src/core-metadata/src/main/java/org/apache/kylin/metadata/model/util/ComputedColumnUtil.java
 
b/src/core-metadata/src/main/java/org/apache/kylin/metadata/model/util/ComputedColumnUtil.java
index ead8a3e8fd..b07c7cf5b0 100644
--- 
a/src/core-metadata/src/main/java/org/apache/kylin/metadata/model/util/ComputedColumnUtil.java
+++ 
b/src/core-metadata/src/main/java/org/apache/kylin/metadata/model/util/ComputedColumnUtil.java
@@ -626,10 +626,10 @@ public class ComputedColumnUtil {
             return exceptionList;
         }
 
-        public Pair<List<ComputedColumnDesc>, List<CCConflictDetail>> 
getAdjustedCCList(
+        public Pair<List<ComputedColumnDesc>, List<KylinException>> 
getAdjustedCCList(
                 List<ComputedColumnDesc> inputCCDescList) {
             List<ComputedColumnDesc> resultCCDescList = Lists.newArrayList();
-            List<CCConflictDetail> adjustDetails = Lists.newArrayList();
+            List<KylinException> adjustExceptionList = Lists.newArrayList();
 
             for (ComputedColumnDesc ccDesc : inputCCDescList) {
                 for (CCConflictDetail detail : this.sameExprDiffNameDetails) {
@@ -638,13 +638,13 @@ public class ComputedColumnUtil {
                     if (newCC.equals(ccDesc)) {
                         logger.info("adjust cc name {} to {}", 
newCC.getColumnName(), existingCC.getColumnName());
                         ccDesc.setColumnName(existingCC.getColumnName());
-                        adjustDetails.add(detail);
+                        
adjustExceptionList.add(detail.getAdjustKylinException());
                         break;
                     }
                 }
                 resultCCDescList.add(ccDesc);
             }
-            return Pair.newPair(resultCCDescList, adjustDetails);
+            return Pair.newPair(resultCCDescList, adjustExceptionList);
         }
     }
 
diff --git 
a/src/modeling-service/src/main/java/org/apache/kylin/rest/service/ModelService.java
 
b/src/modeling-service/src/main/java/org/apache/kylin/rest/service/ModelService.java
index 07cab15306..e1441556d7 100644
--- 
a/src/modeling-service/src/main/java/org/apache/kylin/rest/service/ModelService.java
+++ 
b/src/modeling-service/src/main/java/org/apache/kylin/rest/service/ModelService.java
@@ -4313,6 +4313,66 @@ public class ModelService extends AbstractModelService 
implements TableModelSupp
         return response;
     }
 
+    public void checkCCEmpty(ModelRequest modelRequest) {
+        List<ComputedColumnDesc> ccList = 
modelRequest.getComputedColumnDescs();
+        if (CollectionUtils.isEmpty(ccList)) {
+            return;
+        }
+        boolean matchEmpty = ccList.stream()
+                .anyMatch(cc -> StringUtils.isEmpty(cc.getColumnName()) || 
StringUtils.isEmpty(cc.getExpression()));
+        if (matchEmpty) {
+            throw new KylinException(COMPUTED_COLUMN_NAME_OR_EXPR_EMPTY);
+        }
+    }
+
+    public Pair<ModelRequest, ComputedColumnConflictResponse> 
checkCCConflict(ModelRequest modelRequest) {
+        String project = modelRequest.getProject();
+        validatePartitionDateColumn(modelRequest);
+
+        val dataModel = semanticUpdater.convertToDataModel(modelRequest);
+        val modelManager = getManager(NDataModelManager.class, project);
+        val ccRelatedModels = modelManager.getCCRelatedModels(dataModel);
+        // check cc conflict and return ccConflictInfo
+        val ccConflictInfo = dataModel.checkCCFailAtEnd(getConfig(), project, 
ccRelatedModels, true);
+        boolean autoAdjust = modelRequest.isComputedColumnNameAutoAdjust();
+
+        if (ccConflictInfo.noneConflict()) {
+            // No conflict, return
+            return Pair.newPair(modelRequest, new 
ComputedColumnConflictResponse());
+        }
+        if (ccConflictInfo.hasSameNameConflict()) {
+            // have sameNameDiffExpr Conflict, all conflict messages need to 
be thrown
+            val response = 
handleOnConflictResponse(ccConflictInfo.getAllConflictException());
+            throw new 
KylinException(COMPUTED_COLUMN_CONFLICT).withData(response);
+        }
+        // have sameExprDiffExprConflict Conflict
+        if (!autoAdjust) {
+            // AutoAdjust = false
+            // SameExpr conflict messages need to be thrown
+            val response = 
handleOnConflictResponse(ccConflictInfo.getSameExprConflictException());
+            throw new 
KylinException(COMPUTED_COLUMN_CONFLICT).withData(response);
+        }
+        // AutoAdjust = true
+        List<ComputedColumnDesc> inputCCDescList = 
Lists.newArrayList(modelRequest.getComputedColumnDescs());
+        // deal with conflicts
+        val pair = ccConflictInfo.getAdjustedCCList(inputCCDescList);
+        modelRequest.setComputedColumnDescs(pair.getFirst());
+        return Pair.newPair(modelRequest, 
handleOnConflictResponse(pair.getSecond()));
+    }
+
+    public ComputedColumnConflictResponse 
handleOnConflictResponse(List<KylinException> exceptionList) {
+        val response = new ComputedColumnConflictResponse();
+        exceptionList.stream() //
+                .filter(Objects::nonNull) //
+                .forEach(e -> {
+                    val producer = e.getErrorCodeProducer();
+                    val code = producer.getErrorCode().getCode();
+                    val msg = producer.getMsg(e.getArgs());
+                    response.addConflictDetail(code, msg);
+                });
+        return response;
+    }
+
     @Override
     public void onUpdateBrokenModel(NDataModel model, AffectedModelContext 
removeAffectedModel,
             AffectedModelContext changeTypeAffectedModel, String projectName) 
throws Exception {
diff --git 
a/src/modeling-service/src/test/java/org/apache/kylin/rest/service/ModelServiceTest.java
 
b/src/modeling-service/src/test/java/org/apache/kylin/rest/service/ModelServiceTest.java
index e9dc0f22c3..7d3b71bcbc 100644
--- 
a/src/modeling-service/src/test/java/org/apache/kylin/rest/service/ModelServiceTest.java
+++ 
b/src/modeling-service/src/test/java/org/apache/kylin/rest/service/ModelServiceTest.java
@@ -522,7 +522,8 @@ public class ModelServiceTest extends SourceTestCase {
         Assert.assertEquals(1, models.size());
         NDataModelResponse model = models.get(0);
         
Assert.assertTrue(model.getSimpleTables().stream().map(SimplifiedTableResponse::getColumns)
-                
.flatMap(List::stream).anyMatch(SimplifiedColumnResponse::isComputedColumn));
+                .flatMap(List::stream)
+                .anyMatch(SimplifiedColumnResponse::isComputedColumn));
     }
 
     @Test
@@ -563,7 +564,8 @@ public class ModelServiceTest extends SourceTestCase {
         NIndexPlanManager indexPlanManager = 
NIndexPlanManager.getInstance(getTestConfig(), getProject());
         val indexPlan = indexPlanManager.getIndexPlan(modelId);
         indexPlanManager.updateIndexPlan(modelId, copyForWrite -> {
-            copyForWrite.markIndexesToBeDeleted(modelId, new 
HashSet<>(indexPlan.getAllLayouts()));
+            copyForWrite.markIndexesToBeDeleted(modelId,
+                    new HashSet<>(indexPlan.getAllLayouts()));
             copyForWrite.getIndexes().clear();
         });
         NDataflowManager dataflowManager = 
NDataflowManager.getInstance(KylinConfig.getInstanceFromEnv(), "default");
@@ -5081,6 +5083,394 @@ public class ModelServiceTest extends SourceTestCase {
         }
     }
 
+    @Test
+    public void testExportTDSByAdmin() throws Exception {
+        val project = "default";
+        val modelId = "cb596712-3a09-46f8-aea1-988b43fe9b6c";
+        prepareBasic(project);
+        List<String> dimensions = Lists.newArrayList();
+        dimensions.add("DEFAULT.TEST_MEASURE.FLAG");
+        dimensions.add("DEFAULT.TEST_MEASURE.PRICE1");
+        dimensions.add("DEFAULT.TEST_MEASURE.ID1");
+        List<String> measurs = Lists.newArrayList();
+        measurs.add("COUNT_STAR");
+        measurs.add("SUM_1");
+        SyncContext syncContext = modelService.getADMINSyncContext(project, 
modelId,
+                SyncContext.BI.TABLEAU_CONNECTOR_TDS, 
SyncContext.ModelElement.CUSTOM_COLS, "localhost", 8080);
+        TableauDatasourceModel datasource1 = (TableauDatasourceModel) 
modelService
+                .exportTDSDimensionsAndMeasuresByAdmin(syncContext, 
dimensions, measurs);
+        ByteArrayOutputStream outStream4 = new ByteArrayOutputStream();
+        datasource1.dump(outStream4);
+        
Assert.assertEquals(getExpectedTds("/bisync_tableau/nmodel_full_measure_test.connector_admin.tds"),
+                outStream4.toString(Charset.defaultCharset().name()));
+    }
+
+    @Test
+    public void testExportTDSByUser() throws Exception {
+        val project = "default";
+        val modelId = "cb596712-3a09-46f8-aea1-988b43fe9b6c";
+        prepareBasic(project);
+        List<String> dimensions = Lists.newArrayList();
+        dimensions.add("TEST_MEASURE.ID1");
+        dimensions.add("TEST_MEASURE.ID2");
+        dimensions.add("TEST_MEASURE.ID3");
+        dimensions.add("TEST_MEASURE1.ID1");
+        dimensions.add("TEST_MEASURE1.NAME1");
+        dimensions.add("TEST_MEASURE1.NAME2");
+        dimensions.add("TEST_MEASURE1.NAME3");
+        List<String> measurs = Lists.newArrayList();
+        measurs.add("COUNT_STAR");
+        measurs.add("SUM_1");
+        SecurityContextHolder.getContext()
+                .setAuthentication(new TestingAuthenticationToken("u1", 
"ANALYST", Constant.ROLE_ANALYST));
+        SyncContext syncContext = modelService.getSyncContext(project, 
modelId, SyncContext.BI.TABLEAU_CONNECTOR_TDS,
+                SyncContext.ModelElement.CUSTOM_COLS, "localhost", 8080);
+        TableauDatasourceModel datasource1 = (TableauDatasourceModel) 
modelService
+                .exportTDSDimensionsAndMeasuresByNormalUser(syncContext, 
dimensions, measurs);
+        ByteArrayOutputStream outStream4 = new ByteArrayOutputStream();
+        datasource1.dump(outStream4);
+        
Assert.assertEquals(getExpectedTds("/bisync_tableau/nmodel_full_measure_test.connector_user.tds"),
+                outStream4.toString(Charset.defaultCharset().name()));
+    }
+
+    @Test
+    public void testExportTDSByUserAndElement() throws Exception {
+        val project = "default";
+        val modelId = "cb596712-3a09-46f8-aea1-988b43fe9b6c";
+        prepareBasic(project);
+        try {
+            SecurityContextHolder.getContext()
+                    .setAuthentication(new TestingAuthenticationToken("u1", 
"ANALYST", Constant.ROLE_ANALYST));
+            SyncContext syncContext = modelService.getSyncContext(project, 
modelId,
+                    SyncContext.BI.TABLEAU_CONNECTOR_TDS, 
SyncContext.ModelElement.AGG_INDEX_COL, "localhost", 8080);
+            TableauDatasourceModel datasource1 = (TableauDatasourceModel) 
modelService
+                    .exportTDSDimensionsAndMeasuresByNormalUser(syncContext, 
null, null);
+            ByteArrayOutputStream outStream4 = new ByteArrayOutputStream();
+            datasource1.dump(outStream4);
+            Assert.assertEquals(
+                    
getExpectedTds("/bisync_tableau/nmodel_full_measure_test.connector_user_agg_index_col.tds"),
+                    outStream4.toString(Charset.defaultCharset().name()));
+
+            TableauDatasourceModel datasource = (TableauDatasourceModel) 
modelService
+                    .exportTDSDimensionsAndMeasuresByNormalUser(syncContext, 
new ArrayList<>(), new ArrayList<>());
+        } finally {
+            SecurityContextHolder.getContext()
+                    .setAuthentication(new TestingAuthenticationToken("ADMIN", 
"ADMIN", Constant.ROLE_ADMIN));
+        }
+    }
+
+    @Test
+    public void testCheckModelExportPermissionException() {
+        val project = "default";
+        val modelId = "cb596712-3a09-46f8-aea1-988b43fe9b6c";
+        prepareBasic(project);
+        modelService.getADMINSyncContext(project, modelId, 
SyncContext.BI.TABLEAU_CONNECTOR_TDS,
+                SyncContext.ModelElement.AGG_INDEX_COL, "localhost", 8080);
+        try {
+            
Mockito.when(accessService.getGroupsOfExecuteUser(Mockito.any(String.class)))
+                    .thenReturn(Sets.newHashSet("ROLE_ANALYST"));
+            SecurityContextHolder.getContext()
+                    .setAuthentication(new TestingAuthenticationToken("u1", 
"ANALYST", Constant.ROLE_ANALYST));
+            thrown.expect(KylinException.class);
+            thrown.expectMessage("current user does not have full permission 
on requesting model");
+            modelService.getADMINSyncContext(project, modelId, 
SyncContext.BI.TABLEAU_CONNECTOR_TDS,
+                    SyncContext.ModelElement.AGG_INDEX_COL, "localhost", 8080);
+        } finally {
+            SecurityContextHolder.getContext()
+                    .setAuthentication(new TestingAuthenticationToken("ADMIN", 
"ADMIN", Constant.ROLE_ADMIN));
+        }
+    }
+
+    @Test
+    public void testCheckModelExportPermission() {
+        val project = "default";
+        val modelId = "cb596712-3a09-46f8-aea1-988b43fe9b6c";
+        prepareBasic(project);
+        modelService.getADMINSyncContext(project, modelId, 
SyncContext.BI.TABLEAU_CONNECTOR_TDS,
+                SyncContext.ModelElement.AGG_INDEX_COL, "localhost", 8080);
+        modelService.getADMINSyncContext(project, modelId, 
SyncContext.BI.TABLEAU_CONNECTOR_TDS,
+                SyncContext.ModelElement.AGG_INDEX_COL, "localhost", 8080);
+    }
+
+    @Test
+    public void testCheckModelExportPermissionWithCC() {
+        val project = "cc_test";
+        val modelId = "0d146f1a-bdd3-4548-87ac-21c2c6f9a0da";
+        AclTCRManager manager = AclTCRManager.getInstance(getTestConfig(), 
project);
+        {
+            AclTCR u1a1 = new AclTCR();
+            manager.updateAclTCR(u1a1, "u1", true);
+            SecurityContextHolder.getContext()
+                    .setAuthentication(new TestingAuthenticationToken("u1", 
"ANALYST", Constant.ROLE_ANALYST));
+            
Mockito.when(accessService.getGroupsOfExecuteUser(Mockito.any(String.class)))
+                    .thenReturn(Sets.newHashSet("ROLE_ANALYST"));
+            modelService.getADMINSyncContext(project, modelId, 
SyncContext.BI.TABLEAU_CONNECTOR_TDS,
+                    SyncContext.ModelElement.AGG_INDEX_COL, "localhost", 8080);
+        }
+        {
+            try {
+                AclTCR u1a1 = new AclTCR();
+                AclTCR.Table u1t1 = new AclTCR.Table();
+                AclTCR.ColumnRow u1cr1 = new AclTCR.ColumnRow();
+                AclTCR.Column u1c1 = new AclTCR.Column();
+                u1c1.add("ORDER_ID");
+                u1cr1.setColumn(u1c1);
+                u1t1.put("SSB.LINEORDER", u1cr1);
+                u1a1.setTable(u1t1);
+                manager.updateAclTCR(u1a1, "u1", true);
+                thrown.expect(KylinException.class);
+                thrown.expectMessage("current user does not have full 
permission on requesting model");
+                modelService.getADMINSyncContext(project, modelId, 
SyncContext.BI.TABLEAU_CONNECTOR_TDS,
+                        SyncContext.ModelElement.AGG_INDEX_COL, "localhost", 
8080);
+            } finally {
+                SecurityContextHolder.getContext()
+                        .setAuthentication(new 
TestingAuthenticationToken("ADMIN", "ADMIN", Constant.ROLE_ADMIN));
+            }
+        }
+
+    }
+
+    @Test
+    public void testExportTDSByBroken() {
+        val project = "test_broken_project";
+        val modelId = "4b93b131-824e-6966-c4dd-5a4268d27095";
+        List<String> dimensions = Lists.newArrayList();
+        List<String> measurs = Lists.newArrayList();
+        SyncContext syncContext = modelService.getSyncContext(project, 
modelId, SyncContext.BI.TABLEAU_CONNECTOR_TDS,
+                SyncContext.ModelElement.CUSTOM_COLS, "localhost", 8080);
+        Assert.assertThrows(KylinException.class,
+                () -> 
modelService.exportTDSDimensionsAndMeasuresByNormalUser(syncContext, 
dimensions, measurs));
+
+        Assert.assertThrows(KylinException.class,
+                () -> 
modelService.exportTDSDimensionsAndMeasuresByAdmin(syncContext, dimensions, 
measurs));
+    }
+
+    @Test
+    public void testExportTDSMeasurePermission() {
+        val project = "default";
+        val modelId = "82fa7671-a935-45f5-8779-85703601f49a";
+        prepareBasicByMeasure(project);
+        List<String> dimensions = Lists.newArrayList();
+        //"ORDER_ID", "PRICE", "CAL_DT", "PRICE", "ITEM_COUNT", "LEAF_CATEG_ID"
+        dimensions.add("TEST_KYLIN_FACT.ORDER_ID");
+        dimensions.add("TEST_KYLIN_FACT.PRICE");
+        dimensions.add("TEST_KYLIN_FACT.CAL_DT");
+        dimensions.add("TEST_KYLIN_FACT.PRICE");
+        dimensions.add("TEST_KYLIN_FACT.ITEM_COUNT");
+        dimensions.add("TEST_KYLIN_FACT.LEAF_CATEG_ID");
+        //"ORDER_ID", "TEST_TIME_ENC", "TEST_DATE_ENC"
+        dimensions.add("TEST_ORDER.ORDER_ID");
+        dimensions.add("TEST_ORDER.TEST_TIME_ENC");
+        dimensions.add("TEST_ORDER.TEST_DATE_ENC");
+        //"ORDER_ID", "PRICE", "CAL_DT", "TRANS_ID"
+        dimensions.add("TEST_MEASURE.ORDER_ID");
+        dimensions.add("TEST_MEASURE.PRICE");
+        dimensions.add("TEST_MEASURE.CAL_DT");
+        dimensions.add("TEST_MEASURE.TRANS_ID");
+
+        List<String> measures = Lists.newArrayList();
+        measures.add("TRANS_CNT");
+        measures.add("GMV_SUM");
+        measures.add("GMV_MIN");
+        measures.add("GMV_MAX");
+        measures.add("ITEM_COUNT_SUM");
+        measures.add("ITEM_COUNT_MAX");
+        measures.add("ITEM_COUNT_MIN");
+        measures.add("SELLER_HLL");
+        measures.add("COUNT_DISTINCT");
+        measures.add("TOP_SELLER");
+        measures.add("TEST_COUNT_DISTINCT_BITMAP");
+        measures.add("GVM_PERCENTILE");
+        SecurityContextHolder.getContext()
+                .setAuthentication(new TestingAuthenticationToken("u1", 
"ANALYST", Constant.ROLE_ANALYST));
+        SyncContext syncContext = modelService.getSyncContext(project, 
modelId, SyncContext.BI.TABLEAU_CONNECTOR_TDS,
+                SyncContext.ModelElement.CUSTOM_COLS, "localhost", 8080);
+        Assert.assertThrows(KylinException.class,
+                () -> 
modelService.exportTDSDimensionsAndMeasuresByNormalUser(syncContext, 
dimensions, measures));
+    }
+
+    private void prepareBasicByMeasure(String project) {
+        AclTCRManager manager = AclTCRManager.getInstance(getTestConfig(), 
project);
+
+        AclTCR u1a1 = new AclTCR();
+        AclTCR.Table u1t1 = new AclTCR.Table();
+        AclTCR.ColumnRow u1cr1 = new AclTCR.ColumnRow();
+        AclTCR.Column u1c1 = new AclTCR.Column();
+        u1c1.addAll(Arrays.asList("ORDER_ID", "PRICE", "CAL_DT", "PRICE", 
"ITEM_COUNT", "LEAF_CATEG_ID"));
+        u1cr1.setColumn(u1c1);
+
+        AclTCR.ColumnRow u1cr2 = new AclTCR.ColumnRow();
+        AclTCR.Column u1c2 = new AclTCR.Column();
+        u1c2.addAll(Arrays.asList("ORDER_ID", "TEST_TIME_ENC", 
"TEST_DATE_ENC"));
+        u1cr2.setColumn(u1c2);
+        u1t1.put("DEFAULT.TEST_KYLIN_FACT", u1cr1);
+        u1t1.put("DEFAULT.TEST_ORDER", u1cr2);
+        u1a1.setTable(u1t1);
+        manager.updateAclTCR(u1a1, "u1", true);
+    }
+
+    @Test
+    public void testExportModel() throws Exception {
+        val project = "default";
+        val modelId = "cb596712-3a09-46f8-aea1-988b43fe9b6c";
+        prepareBasic(project);
+        TableauDatasourceModel datasource1 = (TableauDatasourceModel) 
modelService.exportModel(project, modelId,
+                SyncContext.BI.TABLEAU_CONNECTOR_TDS, 
SyncContext.ModelElement.AGG_INDEX_AND_TABLE_INDEX_COL,
+                "localhost", 8080);
+        ByteArrayOutputStream outStream4 = new ByteArrayOutputStream();
+        datasource1.dump(outStream4);
+        
Assert.assertEquals(getExpectedTds("/bisync_tableau/nmodel_full_measure_test.connector.tds"),
+                outStream4.toString(Charset.defaultCharset().name()));
+    }
+
+    private String getExpectedTds(String path) throws IOException {
+        return CharStreams.toString(new 
InputStreamReader(getClass().getResourceAsStream(path), Charsets.UTF_8));
+    }
+
+    private void prepareBasic(String project) {
+        AclTCRManager manager = AclTCRManager.getInstance(getTestConfig(), 
project);
+
+        AclTCR u1a1 = new AclTCR();
+        AclTCR.Table u1t1 = new AclTCR.Table();
+        AclTCR.ColumnRow u1cr1 = new AclTCR.ColumnRow();
+        AclTCR.Column u1c1 = new AclTCR.Column();
+        u1c1.addAll(Arrays.asList("ID1", "ID2", "ID3"));
+        u1cr1.setColumn(u1c1);
+
+        AclTCR.ColumnRow u1cr2 = new AclTCR.ColumnRow();
+        AclTCR.Column u1c2 = new AclTCR.Column();
+        u1c2.addAll(Arrays.asList("ID1", "NAME1", "NAME2", "NAME3"));
+        u1cr2.setColumn(u1c2);
+        u1t1.put("DEFAULT.TEST_MEASURE", u1cr1);
+        u1t1.put("DEFAULT.TEST_MEASURE1", u1cr2);
+        u1a1.setTable(u1t1);
+        manager.updateAclTCR(u1a1, "u1", true);
+
+        AclTCR g1a1 = new AclTCR();
+        AclTCR.Table g1t1 = new AclTCR.Table();
+        AclTCR.ColumnRow g1cr1 = new AclTCR.ColumnRow();
+        AclTCR.Column g1c1 = new AclTCR.Column();
+        g1c1.addAll(Arrays.asList("ID1", "ID2", "ID3", "ID4"));
+        g1cr1.setColumn(g1c1);
+        g1t1.put("DEFAULT.TEST_MEASURE", g1cr1);
+        g1a1.setTable(g1t1);
+        manager.updateAclTCR(g1a1, "g1", false);
+    }
+
+    @Test
+    public void testCheckTablePermission() {
+        val project = "default";
+        val modelId = "cb596712-3a09-46f8-aea1-988b43fe9b6c";
+        thrown.expect(KylinException.class);
+        thrown.expectMessage(MsgPicker.getMsg().getTableNoColumnsPermission());
+
+        AclTCRManager manager = AclTCRManager.getInstance(getTestConfig(), 
project);
+        Set<String> columns = new HashSet<>();
+        columns.add("DEFAULT.TEST_MEASURE1.NAME1");
+        columns.add("DEFAULT.TEST_MEASURE1.NAME2");
+        columns.add("DEFAULT.TEST_MEASURE1.NAME3");
+
+        AclTCR u1a1 = new AclTCR();
+        AclTCR.Table u1t1 = new AclTCR.Table();
+        AclTCR.ColumnRow u1cr1 = new AclTCR.ColumnRow();
+        AclTCR.Column u1c1 = new AclTCR.Column();
+        u1cr1.setColumn(u1c1);
+
+        AclTCR.ColumnRow u1cr2 = new AclTCR.ColumnRow();
+        AclTCR.Column u1c2 = new AclTCR.Column();
+        u1c2.addAll(Arrays.asList("NAME1", "NAME2", "NAME3"));
+        u1cr2.setColumn(u1c2);
+        u1t1.put("DEFAULT.TEST_MEASURE", u1cr1);
+        u1t1.put("DEFAULT.TEST_MEASURE1", u1cr2);
+        u1a1.setTable(u1t1);
+        manager.updateAclTCR(u1a1, "u1", true);
+        SecurityContextHolder.getContext()
+                .setAuthentication(new TestingAuthenticationToken("u1", 
"ANALYST", Constant.ROLE_ANALYST));
+        List<String> dimensions = Lists.newArrayList();
+        dimensions.add("TEST_MEASURE.FLAG");
+        dimensions.add("TEST_MEASURE.PRICE1");
+        dimensions.add("TEST_MEASURE.ID1");
+        List<String> measurs = Lists.newArrayList();
+        measurs.add("COUNT_STAR");
+        measurs.add("SUM_1");
+        
modelService.checkTableHasColumnPermission(SyncContext.ModelElement.CUSTOM_COLS,
 project, modelId, columns,
+                dimensions, measurs);
+
+        dimensions.add("TEST_MEASURE.ID4");
+        Assert.assertThrows(KylinException.class,
+                () -> 
modelService.checkTableHasColumnPermission(SyncContext.ModelElement.CUSTOM_COLS,
 project, modelId,
+                        columns, dimensions, measurs));
+    }
+
+    @Test
+    public void testExportTDSCheckColumnPermission() {
+        val project = "default";
+        val modelId = "89af4ee2-2cdb-4b07-b39e-4c29856309aa";
+
+        NDataModelManager modelManager = 
NDataModelManager.getInstance(KylinConfig.getInstanceFromEnv(), project);
+        NDataModel dataModel = modelManager.getDataModelDesc(modelId);
+
+        Set<String> authColumns = Sets.newHashSet();
+        List<String> dimensions = Lists.newArrayList();
+        List<String> measurs = Lists.newArrayList();
+
+        Assert.assertTrue(modelService.checkColumnPermission(dataModel, 
authColumns, null, measurs));
+        Assert.assertTrue(modelService.checkColumnPermission(dataModel, 
authColumns, null, null));
+        Assert.assertTrue(modelService.checkColumnPermission(dataModel, 
authColumns, dimensions, null));
+        Assert.assertTrue(modelService.checkColumnPermission(dataModel, 
authColumns, dimensions, measurs));
+
+        authColumns.add("DEFAULT.TEST_KYLIN_FACT.PRICE");
+        authColumns.add("DEFAULT.TEST_KYLIN_FACT.ITEM_COUNT");
+        authColumns.add("EDW.TEST_CAL_DT.CAL_DT");
+        authColumns.add("DEFAULT.TEST_ACCOUNT.ACCOUNT_ID");
+
+        Set<String> newAuthColumns = Sets.newHashSet();
+        dataModel.getAllTables().forEach(tableRef -> {
+            List<TblColRef> collect = tableRef.getColumns().stream()
+                    .filter(column -> 
authColumns.contains(column.getCanonicalName())).collect(Collectors.toList());
+            collect.forEach(x -> newAuthColumns.add(x.getAliasDotName()));
+        });
+
+        dimensions.add("TEST_KYLIN_FACT.DEAL_AMOUNT");
+        dimensions.add("TEST_KYLIN_FACT.TRANS_ID");
+
+        Assert.assertFalse(modelService.checkColumnPermission(dataModel, 
newAuthColumns, dimensions, measurs));
+
+        newAuthColumns.add("TEST_KYLIN_FACT.TRANS_ID");
+
+        measurs.add("SUM_NEST4");
+        measurs.add("COUNT_CAL_DT");
+        Assert.assertTrue(modelService.checkColumnPermission(dataModel, 
newAuthColumns, dimensions, measurs));
+
+        Assert.assertTrue(modelService.checkColumnPermission(dataModel, 
newAuthColumns, dimensions, measurs));
+
+    }
+
+    @Test
+    public void testConvertCCToNormalCols() {
+        val project = "default";
+        val modelId = "89af4ee2-2cdb-4b07-b39e-4c29856309aa";
+        NDataModelManager modelManager = 
NDataModelManager.getInstance(KylinConfig.getInstanceFromEnv(), project);
+        NDataModel dataModel = modelManager.getDataModelDesc(modelId);
+        NDataModel.Measure measure = 
dataModel.getEffectiveMeasures().values().stream()
+                .filter(x -> 
x.getName().equals("SUM_NEST4")).findFirst().get();
+        Set<String> measureColumns = 
measure.getFunction().getParameters().stream()
+                .filter(parameterDesc -> parameterDesc.getColRef() != null)
+                .map(parameterDesc -> 
parameterDesc.getColRef().getCanonicalName()).collect(Collectors.toSet());
+        ComputedColumnDesc sumNest4 = 
dataModel.getComputedColumnDescs().stream()
+                .filter(x -> 
measureColumns.contains(x.getIdentName())).findFirst().get();
+        Set<String> strings = modelService.convertCCToNormalCols(dataModel, 
sumNest4);
+        Assert.assertEquals("TEST_KYLIN_FACT.PRICE, 
TEST_KYLIN_FACT.ITEM_COUNT", String.join(", ", strings));
+
+        sumNest4.setInnerExpression("1 + 2");
+        Set<String> set = modelService.convertCCToNormalCols(dataModel, 
sumNest4);
+        Assert.assertEquals(Collections.emptySet(), set);
+
+        HashSet<Object> authColumns = Sets.newHashSet();
+        authColumns.add("DEFAULT.TEST_KYLIN_FACT.PRICE");
+        Assert.assertTrue(authColumns.containsAll(set));
+    }
+
     @Test
     public void testBuildExceptionMessage() {
         NDataModelManager modelManager = 
NDataModelManager.getInstance(KylinConfig.getInstanceFromEnv(), "default");
@@ -5117,8 +5507,8 @@ public class ModelServiceTest extends SourceTestCase {
     public void testBuildDuplicateCCException() {
         Set<String> set = Sets.newHashSet("test");
         Assert.assertThrows("The computed column name \"test\" has been used 
in the current model. Please rename it.\n",
-                KylinException.class,
-                () -> ReflectionTestUtils.invokeMethod(modelService, 
"buildDuplicateCCException", set));
+                KylinException.class, () -> 
ReflectionTestUtils.invokeMethod(modelService, "buildDuplicateCCException",
+                        set));
     }
 
     @Test
@@ -5276,8 +5666,8 @@ public class ModelServiceTest extends SourceTestCase {
         testCheckCCConflictAllExprConflict(originRequest);
         testCheckCCConflictExprAndNameConflict(originRequest);
         testCheckCCConflictExprAndNameConflict2(originRequest);
-        testNoCCConflict(originRequest);
         testCheckCCConflictAdjust(originRequest);
+        testNoCCConflict(originRequest);
     }
 
     private void testCheckCCConflictAllExprConflict(ModelRequest 
originRequest) {
@@ -5369,78 +5759,18 @@ public class ModelServiceTest extends SourceTestCase {
     }
 
     private void testCheckCCConflictAdjust(ModelRequest originRequest) {
-        {
-            val ccList = Lists.newArrayList(//
-                    getComputedColumnDesc("CC_1", "CUSTOMER.C_NAME +'USA'", 
"DOUBLE"),
-                    getComputedColumnDesc("CC_LTAX", "LINEORDER.LO_TAX + 1", 
"BIGINT"));
-            originRequest.setComputedColumnDescs(ccList);
-            originRequest.setComputedColumnNameAutoAdjust(true);
-            val pair = modelService.checkCCConflict(originRequest);
-            val details = pair.getSecond().getConflictDetails();
-            Assert.assertEquals(1, details.size());
-            
Assert.assertEquals(COMPUTED_COLUMN_CONFLICT_ADJUST_INFO.getErrorCode().getCode(),
-                    details.get(0).getDetailCode());
-            
Assert.assertEquals(COMPUTED_COLUMN_CONFLICT_ADJUST_INFO.getMsg("CC_1", 
"CUSTOMER.C_NAME +'USA'",
-                    "CC_CNAME", "CUSTOMER.C_NAME +'USA'", "CC_CNAME"), 
details.get(0).getDetailMsg());
-        }
-
-        {
-            val ccList = Lists.newArrayList(//
-                    getComputedColumnDesc("CC_1", "CUSTOMER.C_NAME +'USA'", 
"DOUBLE"),
-                    getComputedColumnDesc("CC_LTAX", "LINEORDER.LO_TAX + 1", 
"BIGINT"));
-            originRequest.setComputedColumnDescs(ccList);
-            originRequest.setComputedColumnNameAutoAdjust(true);
-            originRequest.setFilterCondition("LINEORDER.LO_TAX = 'Kylin' or 
LINEORDER.LO_TAX = 'Kylin2'");
-            val pair = modelService.checkCCConflict(originRequest);
-            val details = pair.getSecond().getConflictDetails();
-            Assert.assertEquals(1, details.size());
-            
Assert.assertEquals(COMPUTED_COLUMN_CONFLICT_ADJUST_INFO.getErrorCode().getCode(),
-                    details.get(0).getDetailCode());
-            
Assert.assertEquals(COMPUTED_COLUMN_CONFLICT_ADJUST_INFO.getMsg("CC_1", 
"CUSTOMER.C_NAME +'USA'",
-                    "CC_CNAME", "CUSTOMER.C_NAME +'USA'", "CC_CNAME"), 
details.get(0).getDetailMsg());
-            Assert.assertEquals("LINEORDER.LO_TAX = 'Kylin' or 
LINEORDER.LO_TAX = 'Kylin2'",
-                    pair.getFirst().getFilterCondition());
-        }
-
-        {
-            val dimList = Lists.newArrayList(getNamedColumn("CC_1", 
"LINEORDER.CC_1"));
-            val measureList = Lists.newArrayList(//
-                    getSimplifiedMeasure("cc_count", "COUNT", "column", 
"LINEORDER.CC_1"),
-                    getSimplifiedMeasure("COUNT_ALL", "COUNT", "constant", 
"1"));
-            val ccList = Lists.newArrayList(//
-                    getComputedColumnDesc("CC_1", "CUSTOMER.C_NAME +'USA'", 
"DOUBLE"),
-                    getComputedColumnDesc("CC_LTAX", "LINEORDER.LO_TAX + 1", 
"BIGINT"));
-            originRequest.setComputedColumnDescs(ccList);
-            originRequest.setComputedColumnNameAutoAdjust(true);
-            originRequest.setSimplifiedDimensions(dimList);
-            originRequest.setSimplifiedMeasures(measureList);
-            originRequest.setFilterCondition("LINEORDER.Cc_1 = 'Kylin' or 
LINEORDER.cC_1 = 'Kylin2'");
-            val pair = modelService.checkCCConflict(originRequest);
-            val details = pair.getSecond().getConflictDetails();
-            Assert.assertEquals(1, details.size());
-            
Assert.assertEquals(COMPUTED_COLUMN_CONFLICT_ADJUST_INFO.getErrorCode().getCode(),
-                    details.get(0).getDetailCode());
-            
Assert.assertEquals(COMPUTED_COLUMN_CONFLICT_ADJUST_INFO.getMsg("CC_1", 
"CUSTOMER.C_NAME +'USA'",
-                    "CC_CNAME", "CUSTOMER.C_NAME +'USA'", "CC_CNAME"), 
details.get(0).getDetailMsg());
-
-            ModelRequest modelRequest = pair.getFirst();
-            val simplifiedDimensions = modelRequest.getSimplifiedDimensions();
-            Assert.assertEquals(1, simplifiedDimensions.size());
-            Assert.assertEquals("LINEORDER.CC_CNAME", 
simplifiedDimensions.get(0).getAliasDotColumn());
-            Assert.assertEquals("CC_1", simplifiedDimensions.get(0).getName());
-
-            List<SimplifiedMeasure> simplifiedMeasures = 
modelRequest.getSimplifiedMeasures();
-            Assert.assertEquals(2, simplifiedMeasures.size());
-            simplifiedMeasures = simplifiedMeasures.stream().filter(measure -> 
measure.getName().equals("cc_count"))
-                    .collect(Collectors.toList());
-            Assert.assertEquals(1, simplifiedMeasures.size());
-            Assert.assertEquals("COUNT", 
simplifiedMeasures.get(0).getExpression());
-            Assert.assertEquals("column", 
simplifiedMeasures.get(0).getParameterValue().get(0).getType());
-            Assert.assertEquals("LINEORDER.CC_CNAME", 
simplifiedMeasures.get(0).getParameterValue().get(0).getValue());
-
-            Assert.assertEquals("LINEORDER.CC_CNAME = 'Kylin' or 
LINEORDER.CC_CNAME = 'Kylin2'",
-                    modelRequest.getFilterCondition());
-        }
+        val ccList = Lists.newArrayList(//
+                getComputedColumnDesc("CC_1", "CUSTOMER.C_NAME +'USA'", 
"DOUBLE"),
+                getComputedColumnDesc("CC_LTAX", "LINEORDER.LO_TAX + 1", 
"BIGINT"));
+        originRequest.setComputedColumnDescs(ccList);
+        originRequest.setComputedColumnNameAutoAdjust(true);
+        val pair = modelService.checkCCConflict(originRequest);
+        val details = pair.getSecond().getConflictDetails();
+        Assert.assertEquals(1, details.size());
+        
Assert.assertEquals(COMPUTED_COLUMN_CONFLICT_ADJUST_INFO.getErrorCode().getCode(),
+                details.get(0).getDetailCode());
+        
Assert.assertEquals(COMPUTED_COLUMN_CONFLICT_ADJUST_INFO.getMsg("CC_1", 
"CUSTOMER.C_NAME +'USA'",
+                "CC_CNAME", "CUSTOMER.C_NAME +'USA'", "CC_CNAME"), 
details.get(0).getDetailMsg());
     }
 
     private void testNoCCConflict(ModelRequest originRequest) {
@@ -5498,20 +5828,4 @@ public class ModelServiceTest extends SourceTestCase {
         return ccDesc;
     }
 
-    private NamedColumn getNamedColumn(String name, String aliasDotName) {
-        NamedColumn namedColumn = new NamedColumn();
-        namedColumn.setName(name);
-        namedColumn.setAliasDotColumn(aliasDotName);
-        namedColumn.setStatus(NDataModel.ColumnStatus.DIMENSION);
-        return namedColumn;
-    }
-
-    private SimplifiedMeasure getSimplifiedMeasure(String name, String expr, 
String type, String value) {
-        ParameterResponse parameterResponse = new ParameterResponse(type, 
value);
-        SimplifiedMeasure measure = new SimplifiedMeasure();
-        measure.setName(name);
-        measure.setExpression(expr);
-        measure.setParameterValue(Lists.newArrayList(parameterResponse));
-        return measure;
-    }
 }


Reply via email to