http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/controller2/HybridControllerV2.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller2/HybridControllerV2.java b/server-base/src/main/java/org/apache/kylin/rest/controller2/HybridControllerV2.java new file mode 100644 index 0000000..ddf745a --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller2/HybridControllerV2.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.apache.kylin.rest.controller2; + +import org.apache.kylin.rest.controller.BasicController; +import org.apache.kylin.rest.msg.MsgPicker; +import org.apache.kylin.rest.request.HybridRequest; +import org.apache.kylin.rest.response.EnvelopeResponse; +import org.apache.kylin.rest.response.ResponseCode; +import org.apache.kylin.rest.service.HybridService; +import org.apache.kylin.storage.hybrid.HybridInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping(value = "/hybrids") +public class HybridControllerV2 extends BasicController { + + @Autowired + private HybridService hybridService; + + @RequestMapping(value = "", method = RequestMethod.POST, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse createV2(@RequestHeader("Accept-Language") String lang, @RequestBody HybridRequest request) { + MsgPicker.setMsg(lang); + + checkRequiredArg("hybrid", request.getHybrid()); + checkRequiredArg("project", request.getProject()); + checkRequiredArg("model", request.getModel()); + checkRequiredArg("cubes", request.getCubes()); + HybridInstance instance = hybridService.createHybridCube(request.getHybrid(), request.getProject(), request.getModel(), request.getCubes()); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, instance, ""); + } + + @RequestMapping(value = "", method = RequestMethod.PUT, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse updateV2(@RequestHeader("Accept-Language") String lang, @RequestBody HybridRequest request) { + MsgPicker.setMsg(lang); + + checkRequiredArg("hybrid", request.getHybrid()); + checkRequiredArg("project", request.getProject()); + checkRequiredArg("model", request.getModel()); + checkRequiredArg("cubes", request.getCubes()); + HybridInstance instance = hybridService.updateHybridCube(request.getHybrid(), request.getProject(), request.getModel(), request.getCubes()); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, instance, ""); + } + + @RequestMapping(value = "", method = RequestMethod.DELETE, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public void deleteV2(@RequestHeader("Accept-Language") String lang, @RequestBody HybridRequest request) { + MsgPicker.setMsg(lang); + + checkRequiredArg("hybrid", request.getHybrid()); + checkRequiredArg("project", request.getProject()); + checkRequiredArg("model", request.getModel()); + hybridService.deleteHybridCube(request.getHybrid(), request.getProject(), request.getModel()); + } + + @RequestMapping(value = "", method = RequestMethod.GET, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse listV2(@RequestHeader("Accept-Language") String lang, @RequestParam(required = false) String project, @RequestParam(required = false) String model) { + MsgPicker.setMsg(lang); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, hybridService.listHybrids(project, model), ""); + } + + @RequestMapping(value = "{hybrid}", method = RequestMethod.GET, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getV2(@RequestHeader("Accept-Language") String lang, @PathVariable String hybrid) { + MsgPicker.setMsg(lang); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, hybridService.getHybridInstance(hybrid), ""); + } +}
http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/controller2/JobControllerV2.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller2/JobControllerV2.java b/server-base/src/main/java/org/apache/kylin/rest/controller2/JobControllerV2.java new file mode 100644 index 0000000..4c25071 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller2/JobControllerV2.java @@ -0,0 +1,222 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.apache.kylin.rest.controller2; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.kylin.job.JobInstance; +import org.apache.kylin.job.constant.JobStatusEnum; +import org.apache.kylin.job.constant.JobTimeFilterEnum; +import org.apache.kylin.job.exception.JobException; +import org.apache.kylin.rest.controller.BasicController; +import org.apache.kylin.rest.msg.MsgPicker; +import org.apache.kylin.rest.response.EnvelopeResponse; +import org.apache.kylin.rest.response.ResponseCode; +import org.apache.kylin.rest.service.JobService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping(value = "jobs") +public class JobControllerV2 extends BasicController { + private static final Logger logger = LoggerFactory.getLogger(JobControllerV2.class); + + @Autowired + @Qualifier("jobService") + private JobService jobService; + + /** + * get all cube jobs + * + * @return + * @throws IOException + */ + + @RequestMapping(value = "", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse listV2(@RequestHeader("Accept-Language") String lang, @RequestParam(value = "status", required = false) Integer[] status, @RequestParam(value = "timeFilter", required = true) Integer timeFilter, @RequestParam(value = "cubeName", required = false) String cubeName, @RequestParam(value = "projectName", required = false) String projectName, @RequestParam(value = "pageOffset", required = false, defaultValue = "0") Integer pageOffset, @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize) { + MsgPicker.setMsg(lang); + + HashMap<String, Object> data = new HashMap<String, Object>(); + List<JobStatusEnum> statusList = new ArrayList<JobStatusEnum>(); + + if (null != status) { + for (int sta : status) { + statusList.add(JobStatusEnum.getByCode(sta)); + } + } + + List<JobInstance> jobInstanceList = jobService.searchJobs(cubeName, projectName, statusList, JobTimeFilterEnum.getByCode(timeFilter)); + + int offset = pageOffset * pageSize; + int limit = pageSize; + + if (jobInstanceList.size() <= offset) { + offset = jobInstanceList.size(); + limit = 0; + } + + if ((jobInstanceList.size() - offset) < limit) { + limit = jobInstanceList.size() - offset; + } + + data.put("jobs", jobInstanceList.subList(offset, offset + limit)); + data.put("size", jobInstanceList.size()); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, data, ""); + } + + /** + * Get a cube job + * + * @return + * @throws JobException + * @throws IOException + */ + + @RequestMapping(value = "/{jobId}", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getV2(@RequestHeader("Accept-Language") String lang, @PathVariable String jobId) { + MsgPicker.setMsg(lang); + + JobInstance jobInstance = jobService.getJobInstance(jobId); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, jobInstance, ""); + } + + /** + * Get a job step output + * + * @return + * @throws IOException + */ + + @RequestMapping(value = "/{jobId}/steps/{stepId}/output", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getStepOutputV2(@RequestHeader("Accept-Language") String lang, @PathVariable String jobId, @PathVariable String stepId) { + MsgPicker.setMsg(lang); + + Map<String, String> result = new HashMap<String, String>(); + result.put("jobId", jobId); + result.put("stepId", String.valueOf(stepId)); + result.put("cmd_output", jobService.getExecutableManager().getOutput(stepId).getVerboseMsg()); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, result, ""); + } + + /** + * Resume a cube job + * + * @return + * @throws IOException + */ + + @RequestMapping(value = "/{jobId}/resume", method = { RequestMethod.PUT }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse resumeV2(@RequestHeader("Accept-Language") String lang, @PathVariable String jobId) { + MsgPicker.setMsg(lang); + + final JobInstance jobInstance = jobService.getJobInstance(jobId); + jobService.resumeJob(jobInstance); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, jobService.getJobInstance(jobId), ""); + } + + /** + * Cancel/discard a job + * + * @return + * @throws IOException + */ + + @RequestMapping(value = "/{jobId}/cancel", method = { RequestMethod.PUT }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse cancelV2(@RequestHeader("Accept-Language") String lang, @PathVariable String jobId) throws IOException { + MsgPicker.setMsg(lang); + + final JobInstance jobInstance = jobService.getJobInstance(jobId); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, jobService.cancelJob(jobInstance), ""); + } + + /** + * Pause a job + * + * @return + * @throws IOException + */ + + @RequestMapping(value = "/{jobId}/pause", method = { RequestMethod.PUT }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse pauseV2(@RequestHeader("Accept-Language") String lang, @PathVariable String jobId) { + MsgPicker.setMsg(lang); + + final JobInstance jobInstance = jobService.getJobInstance(jobId); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, jobService.pauseJob(jobInstance), ""); + } + + /** + * Rollback a job to the given step + * + * @return + * @throws IOException + */ + + @RequestMapping(value = "/{jobId}/steps/{stepId}/rollback", method = { RequestMethod.PUT }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse rollbackV2(@RequestHeader("Accept-Language") String lang, @PathVariable String jobId, @PathVariable String stepId) { + MsgPicker.setMsg(lang); + + final JobInstance jobInstance = jobService.getJobInstance(jobId); + jobService.rollbackJob(jobInstance, stepId); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, jobService.getJobInstance(jobId), ""); + } + + /** + * Drop a cube job + * + * @return + * @throws IOException + */ + + @RequestMapping(value = "/{jobId}/drop", method = { RequestMethod.DELETE }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse dropJobV2(@RequestHeader("Accept-Language") String lang, @PathVariable String jobId) throws IOException { + MsgPicker.setMsg(lang); + + JobInstance jobInstance = jobService.getJobInstance(jobId); + jobService.dropJob(jobInstance); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, jobInstance, ""); + } + + public void setJobService(JobService jobService) { + this.jobService = jobService; + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/controller2/ModelControllerV2.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller2/ModelControllerV2.java b/server-base/src/main/java/org/apache/kylin/rest/controller2/ModelControllerV2.java new file mode 100644 index 0000000..7dc2be7 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller2/ModelControllerV2.java @@ -0,0 +1,284 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.apache.kylin.rest.controller2; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.google.common.collect.Sets; +import org.apache.commons.lang.StringUtils; +import org.apache.kylin.common.KylinConfig; +import org.apache.kylin.common.util.JsonUtil; +import org.apache.kylin.cube.CubeInstance; +import org.apache.kylin.metadata.MetadataManager; +import org.apache.kylin.metadata.model.DataModelDesc; +import org.apache.kylin.metadata.model.TblColRef; +import org.apache.kylin.metadata.project.ProjectInstance; +import org.apache.kylin.rest.controller.BasicController; +import org.apache.kylin.rest.exception.BadRequestException; +import org.apache.kylin.rest.msg.Message; +import org.apache.kylin.rest.msg.MsgPicker; +import org.apache.kylin.rest.request.ModelRequest; +import org.apache.kylin.rest.response.DataModelDescResponse; +import org.apache.kylin.rest.response.EnvelopeResponse; +import org.apache.kylin.rest.response.GeneralResponse; +import org.apache.kylin.rest.response.ResponseCode; +import org.apache.kylin.rest.service.ModelServiceV2; +import org.apache.kylin.rest.service.ProjectServiceV2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * ModelController is defined as Restful API entrance for UI. + * + * @author jiazhong + */ +@Controller +@RequestMapping(value = "/models") +public class ModelControllerV2 extends BasicController { + private static final Logger logger = LoggerFactory.getLogger(ModelControllerV2.class); + + public static final char[] VALID_MODELNAME = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_".toCharArray(); + + @Autowired + @Qualifier("modelMgmtServiceV2") + private ModelServiceV2 modelServiceV2; + + @Autowired + @Qualifier("projectServiceV2") + private ProjectServiceV2 projectServiceV2; + + @RequestMapping(value = "", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getModelsPaging(@RequestHeader("Accept-Language") String lang, @RequestParam(value = "modelName", required = false) String modelName, @RequestParam(value = "projectName", required = false) String projectName, @RequestParam(value = "pageOffset", required = false, defaultValue = "0") Integer pageOffset, @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize) throws IOException { + MsgPicker.setMsg(lang); + + HashMap<String, Object> data = new HashMap<String, Object>(); + List<DataModelDesc> models = modelServiceV2.listAllModels(modelName, projectName); + + int offset = pageOffset * pageSize; + int limit = pageSize; + + if (models.size() <= offset) { + offset = models.size(); + limit = 0; + } + + if ((models.size() - offset) < limit) { + limit = models.size() - offset; + } + + List<DataModelDescResponse> dataModelDescResponses = new ArrayList<DataModelDescResponse>(); + for (DataModelDesc model : modelServiceV2.getModels(modelName, projectName, limit, offset)) { + DataModelDescResponse dataModelDescResponse = new DataModelDescResponse(model); + + if (projectName != null) + dataModelDescResponse.setProject(projectName); + else + dataModelDescResponse.setProject(projectServiceV2.getProjectOfModel(model.getName())); + + dataModelDescResponses.add(dataModelDescResponse); + } + data.put("models", dataModelDescResponses); + data.put("size", models.size()); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, data, ""); + } + + @RequestMapping(value = "", method = { RequestMethod.PUT }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse updateModelDescV2(@RequestHeader("Accept-Language") String lang, @RequestBody ModelRequest modelRequest) throws IOException { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + //Update Model + DataModelDesc modelDesc = deserializeDataModelDescV2(modelRequest); + modelServiceV2.validateModelDesc(modelDesc); + + boolean createNew = modelServiceV2.unifyModelDesc(modelDesc, false); + + String projectName = (null == modelRequest.getProject()) ? ProjectInstance.DEFAULT_PROJECT_NAME : modelRequest.getProject(); + + modelDesc = modelServiceV2.updateModelToResourceStore(modelDesc, projectName, createNew, false); + + String descData = JsonUtil.writeValueAsIndentString(modelDesc); + GeneralResponse data = new GeneralResponse(); + data.setProperty("uuid", modelDesc.getUuid()); + data.setProperty("modelDescData", descData); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, data, ""); + } + + @RequestMapping(value = "/draft", method = { RequestMethod.PUT }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse updateModelDescDraftV2(@RequestHeader("Accept-Language") String lang, @RequestBody ModelRequest modelRequest) throws IOException { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + DataModelDesc modelDesc = deserializeDataModelDescV2(modelRequest); + modelServiceV2.validateModelDesc(modelDesc); + + boolean createNew = modelServiceV2.unifyModelDesc(modelDesc, true); + + String projectName = (null == modelRequest.getProject()) ? ProjectInstance.DEFAULT_PROJECT_NAME : modelRequest.getProject(); + + modelDesc = modelServiceV2.updateModelToResourceStore(modelDesc, projectName, createNew, true); + + String descData = JsonUtil.writeValueAsIndentString(modelDesc); + GeneralResponse data = new GeneralResponse(); + data.setProperty("uuid", modelDesc.getUuid()); + data.setProperty("modelDescData", descData); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, data, ""); + } + + @RequestMapping(value = "/{modelName}", method = { RequestMethod.DELETE }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public void deleteModelV2(@RequestHeader("Accept-Language") String lang, @PathVariable String modelName) throws IOException { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + DataModelDesc desc = modelServiceV2.getMetadataManager().getDataModelDesc(modelName); + if (null == desc) { + throw new BadRequestException(String.format(msg.getMODEL_NOT_FOUND(), modelName)); + } + modelServiceV2.dropModel(desc); + } + + @RequestMapping(value = "/{modelName}/clone", method = { RequestMethod.PUT }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse cloneModelV2(@RequestHeader("Accept-Language") String lang, @PathVariable String modelName, @RequestBody ModelRequest modelRequest) throws IOException { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + String project = modelRequest.getProject(); + MetadataManager metaManager = MetadataManager.getInstance(KylinConfig.getInstanceFromEnv()); + DataModelDesc modelDesc = metaManager.getDataModelDesc(modelName); + String newModelName = modelRequest.getModelName(); + + if (StringUtils.isEmpty(project)) { + logger.info("Project name should not be empty."); + throw new BadRequestException(msg.getEMPTY_PROJECT_NAME()); + } + + if (modelDesc == null || StringUtils.isEmpty(modelName)) { + throw new BadRequestException(msg.getEMPTY_MODEL_NAME()); + } + + if (StringUtils.isEmpty(newModelName)) { + logger.info("New model name is empty."); + throw new BadRequestException(msg.getEMPTY_NEW_MODEL_NAME()); + } + if (!StringUtils.containsOnly(newModelName, VALID_MODELNAME)) { + logger.info("Invalid Model name {}, only letters, numbers and underline supported.", newModelName); + throw new BadRequestException(String.format(msg.getINVALID_MODEL_NAME(), newModelName)); + } + + DataModelDesc newModelDesc = DataModelDesc.getCopyOf(modelDesc); + newModelDesc.setName(newModelName); + + newModelDesc = modelServiceV2.createModelDesc(project, newModelDesc); + + //reload avoid shallow + metaManager.reloadDataModelDescAt(DataModelDesc.concatResourcePath(newModelName)); + + String descData = JsonUtil.writeValueAsIndentString(newModelDesc); + GeneralResponse data = new GeneralResponse(); + data.setProperty("uuid", newModelDesc.getUuid()); + data.setProperty("modelDescData", descData); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, data, ""); + } + + private DataModelDesc deserializeDataModelDescV2(ModelRequest modelRequest) throws IOException { + Message msg = MsgPicker.getMsg(); + + DataModelDesc desc = null; + try { + logger.debug("Saving MODEL " + modelRequest.getModelDescData()); + desc = JsonUtil.readValue(modelRequest.getModelDescData(), DataModelDesc.class); + } catch (JsonParseException e) { + logger.error("The data model definition is not valid.", e); + throw new BadRequestException(msg.getINVALID_MODEL_DEFINITION()); + } catch (JsonMappingException e) { + logger.error("The data model definition is not valid.", e); + throw new BadRequestException(msg.getINVALID_MODEL_DEFINITION()); + } + return desc; + } + + @RequestMapping(value = "/checkNameAvailability/{modelName}", method = RequestMethod.GET, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse checkNameAvailabilityV2(@RequestHeader("Accept-Language") String lang, @PathVariable String modelName) throws IOException { + MsgPicker.setMsg(lang); + + boolean ret = modelServiceV2.checkNameAvailability(modelName); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, ret, ""); + } + + @RequestMapping(value = "/{modelName}/usedCols", method = RequestMethod.GET, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getUsedColsV2(@RequestHeader("Accept-Language") String lang, @PathVariable String modelName) { + MsgPicker.setMsg(lang); + + Map<String, Set<String>> data = new HashMap<>(); + + for (Map.Entry<TblColRef, Set<CubeInstance>> entry : modelServiceV2.getUsedDimCols(modelName).entrySet()) { + populateUsedColResponse(entry.getKey(), entry.getValue(), data); + } + + for (Map.Entry<TblColRef, Set<CubeInstance>> entry : modelServiceV2.getUsedNonDimCols(modelName).entrySet()) { + populateUsedColResponse(entry.getKey(), entry.getValue(), data); + } + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, data, ""); + } + + private void populateUsedColResponse(TblColRef tblColRef, Set<CubeInstance> cubeInstances, Map<String, Set<String>> ret) { + String columnIdentity = tblColRef.getIdentity(); + if (!ret.containsKey(columnIdentity)) { + ret.put(columnIdentity, Sets.<String> newHashSet()); + } + + for (CubeInstance cubeInstance : cubeInstances) { + ret.get(columnIdentity).add(cubeInstance.getCanonicalName()); + } + } + + public void setModelService(ModelServiceV2 modelService) { + this.modelServiceV2 = modelService; + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/controller2/ModelDescControllerV2.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller2/ModelDescControllerV2.java b/server-base/src/main/java/org/apache/kylin/rest/controller2/ModelDescControllerV2.java new file mode 100644 index 0000000..14c9a88 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller2/ModelDescControllerV2.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.apache.kylin.rest.controller2; + +import static org.apache.kylin.metadata.model.DataModelDesc.STATUS_DRAFT; + +import java.io.IOException; +import java.util.HashMap; + +import org.apache.kylin.common.KylinConfig; +import org.apache.kylin.metadata.MetadataManager; +import org.apache.kylin.metadata.model.DataModelDesc; +import org.apache.kylin.rest.controller.BasicController; +import org.apache.kylin.rest.exception.BadRequestException; +import org.apache.kylin.rest.msg.Message; +import org.apache.kylin.rest.msg.MsgPicker; +import org.apache.kylin.rest.response.DataModelDescResponse; +import org.apache.kylin.rest.response.EnvelopeResponse; +import org.apache.kylin.rest.response.ResponseCode; +import org.apache.kylin.rest.service.ProjectServiceV2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * @author jiazhong + * + */ +@Controller +@RequestMapping(value = "/model") +public class ModelDescControllerV2 extends BasicController { + + @Autowired + @Qualifier("projectServiceV2") + private ProjectServiceV2 projectServiceV2; + + /** + * Get detail information of the "Model ID" + * + * @param modelName + * Model ID + * @return + * @throws IOException + */ + @RequestMapping(value = "/{modelName}", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getModelV2(@RequestHeader("Accept-Language") String lang, @PathVariable String modelName) { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + HashMap<String, DataModelDescResponse> data = new HashMap<String, DataModelDescResponse>(); + + MetadataManager metaManager = MetadataManager.getInstance(KylinConfig.getInstanceFromEnv()); + DataModelDesc modelDesc = metaManager.getDataModelDesc(modelName); + if (modelDesc == null) + throw new BadRequestException(String.format(msg.getMODEL_NOT_FOUND(), modelName)); + + DataModelDescResponse dataModelDescResponse = new DataModelDescResponse(modelDesc); + dataModelDescResponse.setProject(projectServiceV2.getProjectOfModel(modelName)); + + if (modelDesc.getStatus() == null) { + data.put("model", dataModelDescResponse); + + String draftName = modelName + "_draft"; + DataModelDesc draftDesc = metaManager.getDataModelDesc(draftName); + if (draftDesc != null && draftDesc.getStatus() != null && draftDesc.getStatus().equals(STATUS_DRAFT)) { + DataModelDescResponse draftModelDescResponse = new DataModelDescResponse(draftDesc); + draftModelDescResponse.setProject(projectServiceV2.getProjectOfModel(draftName)); + data.put("draft", draftModelDescResponse); + } + } else if (modelDesc.getStatus().equals(STATUS_DRAFT)) { + data.put("draft", dataModelDescResponse); + + String parentName = modelName.substring(0, modelName.lastIndexOf("_draft")); + DataModelDesc parentDesc = metaManager.getDataModelDesc(parentName); + if (parentDesc != null && parentDesc.getStatus() == null) { + DataModelDescResponse parentModelDescResponse = new DataModelDescResponse(parentDesc); + parentModelDescResponse.setProject(projectServiceV2.getProjectOfModel(parentName)); + data.put("model", parentModelDescResponse); + } + } + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, data, ""); + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/controller2/ProjectControllerV2.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller2/ProjectControllerV2.java b/server-base/src/main/java/org/apache/kylin/rest/controller2/ProjectControllerV2.java new file mode 100644 index 0000000..3ab0d66 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller2/ProjectControllerV2.java @@ -0,0 +1,269 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.apache.kylin.rest.controller2; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.kylin.common.persistence.AclEntity; +import org.apache.kylin.common.util.JsonUtil; +import org.apache.kylin.cube.CubeInstance; +import org.apache.kylin.metadata.project.ProjectInstance; +import org.apache.kylin.rest.constant.Constant; +import org.apache.kylin.rest.controller.BasicController; +import org.apache.kylin.rest.exception.BadRequestException; +import org.apache.kylin.rest.msg.Message; +import org.apache.kylin.rest.msg.MsgPicker; +import org.apache.kylin.rest.request.ProjectRequest; +import org.apache.kylin.rest.response.EnvelopeResponse; +import org.apache.kylin.rest.response.ResponseCode; +import org.apache.kylin.rest.service.AccessService; +import org.apache.kylin.rest.service.CubeServiceV2; +import org.apache.kylin.rest.service.ProjectServiceV2; +import org.apache.kylin.rest.util.AclUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.acls.domain.GrantedAuthoritySid; +import org.springframework.security.acls.domain.PrincipalSid; +import org.springframework.security.acls.model.AccessControlEntry; +import org.springframework.security.acls.model.Acl; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * @author xduo + */ +@Controller +@RequestMapping(value = "/projects") +public class ProjectControllerV2 extends BasicController { + private static final Logger logger = LoggerFactory.getLogger(ProjectControllerV2.class); + + private static final char[] VALID_PROJECTNAME = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_".toCharArray(); + + @Autowired + @Qualifier("projectServiceV2") + private ProjectServiceV2 projectServiceV2; + + @Autowired + @Qualifier("accessService") + private AccessService accessService; + + @Autowired + private AclUtil aclUtil; + + @Autowired + @Qualifier("cubeMgmtServiceV2") + private CubeServiceV2 cubeServiceV2; + + @RequestMapping(value = "", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getProjectsV2(@RequestHeader("Accept-Language") String lang, @RequestParam(value = "pageOffset", required = false, defaultValue = "0") Integer pageOffset, @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize) { + MsgPicker.setMsg(lang); + + int offset = pageOffset * pageSize; + int limit = pageSize; + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, projectServiceV2.listProjects(limit, offset), ""); + } + + @RequestMapping(value = "/readable", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getReadableProjectsV2(@RequestHeader("Accept-Language") String lang, @RequestParam(value = "pageOffset", required = false, defaultValue = "0") Integer pageOffset, @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize) { + MsgPicker.setMsg(lang); + + HashMap<String, Object> data = new HashMap<String, Object>(); + + List<ProjectInstance> readableProjects = new ArrayList<ProjectInstance>(); + int offset = pageOffset * pageSize; + int limit = pageSize; + + //list all projects first + List<ProjectInstance> projectInstances = projectServiceV2.getProjectManager().listAllProjects(); + + if (projectInstances.size() <= offset) { + offset = projectInstances.size(); + limit = 0; + } + + if ((projectInstances.size() - offset) < limit) { + limit = projectInstances.size() - offset; + } + + //get user infomation + UserDetails userDetails = aclUtil.getCurrentUser(); + String userName = userDetails.getUsername(); + + //check if ROLE_ADMIN return all,also get user role list + List<String> userAuthority = aclUtil.getAuthorityList(); + for (String auth : userAuthority) { + if (auth.equals(Constant.ROLE_ADMIN)) { + data.put("readableProjects", projectInstances.subList(offset, offset + limit)); + data.put("size", projectInstances.size()); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, data, ""); + } + } + + for (ProjectInstance projectInstance : projectInstances) { + if (projectInstance == null) { + continue; + } + + boolean hasProjectPermission = false; + AclEntity ae = accessService.getAclEntity("ProjectInstance", projectInstance.getId()); + Acl projectAcl = accessService.getAcl(ae); + //project no Acl info will be skipped + if (projectAcl != null) { + + //project owner has permission + if (((PrincipalSid) projectAcl.getOwner()).getPrincipal().equals(userName)) { + readableProjects.add(projectInstance); + continue; + } + + //check project permission and role + for (AccessControlEntry ace : projectAcl.getEntries()) { + if (ace.getSid() instanceof PrincipalSid && ((PrincipalSid) ace.getSid()).getPrincipal().equals(userName)) { + hasProjectPermission = true; + readableProjects.add(projectInstance); + break; + + } else if (ace.getSid() instanceof GrantedAuthoritySid) { + String projectAuthority = ((GrantedAuthoritySid) ace.getSid()).getGrantedAuthority(); + if (userAuthority.contains(projectAuthority)) { + hasProjectPermission = true; + readableProjects.add(projectInstance); + break; + } + + } + + } + } + + if (!hasProjectPermission) { + List<CubeInstance> cubeInstances = cubeServiceV2.listAllCubes(projectInstance.getName()); + + for (CubeInstance cubeInstance : cubeInstances) { + if (cubeInstance == null) { + continue; + } + + if (aclUtil.isHasCubePermission(cubeInstance)) { + hasProjectPermission = true; + break; + } + } + if (hasProjectPermission) { + readableProjects.add(projectInstance); + } + } + + } + + if (readableProjects.size() <= offset) { + offset = readableProjects.size(); + limit = 0; + } + + if ((readableProjects.size() - offset) < limit) { + limit = readableProjects.size() - offset; + } + data.put("readableProjects", readableProjects.subList(offset, offset + limit)); + data.put("size", readableProjects.size()); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, data, ""); + } + + @RequestMapping(value = "", method = { RequestMethod.POST }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse saveProjectV2(@RequestHeader("Accept-Language") String lang, @RequestBody ProjectRequest projectRequest) throws IOException { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + ProjectInstance projectDesc = deserializeProjectDescV2(projectRequest); + + if (StringUtils.isEmpty(projectDesc.getName())) { + throw new BadRequestException(msg.getEMPTY_PROJECT_NAME()); + } + + if (!StringUtils.containsOnly(projectDesc.getName(), VALID_PROJECTNAME)) { + logger.info("Invalid Project name {}, only letters, numbers and underline supported.", projectDesc.getName()); + throw new BadRequestException(String.format(msg.getINVALID_PROJECT_NAME(), projectDesc.getName())); + } + + ProjectInstance createdProj = null; + createdProj = projectServiceV2.createProject(projectDesc); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, createdProj, ""); + } + + @RequestMapping(value = "", method = { RequestMethod.PUT }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse updateProjectV2(@RequestHeader("Accept-Language") String lang, @RequestBody ProjectRequest projectRequest) throws IOException { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + String formerProjectName = projectRequest.getFormerProjectName(); + if (StringUtils.isEmpty(formerProjectName)) { + throw new BadRequestException(msg.getEMPTY_PROJECT_NAME()); + } + + ProjectInstance projectDesc = deserializeProjectDescV2(projectRequest); + + ProjectInstance updatedProj = null; + + ProjectInstance currentProject = projectServiceV2.getProjectManager().getProject(formerProjectName); + if (currentProject == null) { + throw new BadRequestException(String.format(msg.getPROJECT_NOT_FOUND(), formerProjectName)); + } + + updatedProj = projectServiceV2.updateProject(projectDesc, currentProject); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, updatedProj, ""); + } + + private ProjectInstance deserializeProjectDescV2(ProjectRequest projectRequest) throws IOException { + ProjectInstance projectDesc = null; + logger.debug("Saving project " + projectRequest.getProjectDescData()); + projectDesc = JsonUtil.readValue(projectRequest.getProjectDescData(), ProjectInstance.class); + return projectDesc; + } + + @RequestMapping(value = "/{projectName}", method = { RequestMethod.DELETE }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public void deleteProjectV2(@RequestHeader("Accept-Language") String lang, @PathVariable String projectName) throws IOException { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + ProjectInstance project = projectServiceV2.getProjectManager().getProject(projectName); + projectServiceV2.deleteProject(projectName, project); + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/controller2/QueryControllerV2.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller2/QueryControllerV2.java b/server-base/src/main/java/org/apache/kylin/rest/controller2/QueryControllerV2.java new file mode 100644 index 0000000..a279619 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller2/QueryControllerV2.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.apache.kylin.rest.controller2; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.apache.kylin.rest.controller.BasicController; +import org.apache.kylin.rest.exception.InternalErrorException; +import org.apache.kylin.rest.model.Query; +import org.apache.kylin.rest.model.SelectedColumnMeta; +import org.apache.kylin.rest.msg.MsgPicker; +import org.apache.kylin.rest.request.MetaRequest; +import org.apache.kylin.rest.request.PrepareSqlRequest; +import org.apache.kylin.rest.request.SQLRequest; +import org.apache.kylin.rest.request.SaveSqlRequest; +import org.apache.kylin.rest.response.EnvelopeResponse; +import org.apache.kylin.rest.response.ResponseCode; +import org.apache.kylin.rest.response.SQLResponse; +import org.apache.kylin.rest.service.QueryServiceV2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.supercsv.io.CsvListWriter; +import org.supercsv.io.ICsvListWriter; +import org.supercsv.prefs.CsvPreference; + +/** + * Handle query requests. + * + * @author xduo + */ +@Controller +public class QueryControllerV2 extends BasicController { + + private static final Logger logger = LoggerFactory.getLogger(QueryControllerV2.class); + + @Autowired + @Qualifier("queryServiceV2") + private QueryServiceV2 queryServiceV2; + + @RequestMapping(value = "/query", method = RequestMethod.POST, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse queryV2(@RequestHeader("Accept-Language") String lang, @RequestBody SQLRequest sqlRequest) { + MsgPicker.setMsg(lang); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, queryServiceV2.doQueryWithCache(sqlRequest), ""); + } + + // TODO should be just "prepare" a statement, get back expected ResultSetMetaData + + @RequestMapping(value = "/query/prestate", method = RequestMethod.POST, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse prepareQueryV2(@RequestHeader("Accept-Language") String lang, @RequestBody PrepareSqlRequest sqlRequest) { + MsgPicker.setMsg(lang); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, queryServiceV2.doQueryWithCache(sqlRequest), ""); + } + + @RequestMapping(value = "/saved_queries", method = RequestMethod.POST, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public void saveQueryV2(@RequestHeader("Accept-Language") String lang, @RequestBody SaveSqlRequest sqlRequest) throws IOException { + MsgPicker.setMsg(lang); + + String creator = SecurityContextHolder.getContext().getAuthentication().getName(); + Query newQuery = new Query(sqlRequest.getName(), sqlRequest.getProject(), sqlRequest.getSql(), sqlRequest.getDescription()); + + queryServiceV2.saveQuery(creator, newQuery); + } + + @RequestMapping(value = "/saved_queries/{id}", method = RequestMethod.DELETE, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public void removeQueryV2(@RequestHeader("Accept-Language") String lang, @PathVariable String id) throws IOException { + MsgPicker.setMsg(lang); + + String creator = SecurityContextHolder.getContext().getAuthentication().getName(); + queryServiceV2.removeQuery(creator, id); + } + + @RequestMapping(value = "/saved_queries/{project}", method = RequestMethod.GET, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getQueriesV2(@RequestHeader("Accept-Language") String lang, @PathVariable String project, @RequestParam(value = "pageOffset", required = false, defaultValue = "0") Integer pageOffset, @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize) throws IOException { + MsgPicker.setMsg(lang); + + HashMap<String, Object> data = new HashMap<String, Object>(); + String creator = SecurityContextHolder.getContext().getAuthentication().getName(); + List<Query> queries = new ArrayList<Query>(); + for (Query query : queryServiceV2.getQueries(creator)) { + if (query.getProject().equals(project)) + queries.add(query); + } + + int offset = pageOffset * pageSize; + int limit = pageSize; + + if (queries.size() <= offset) { + offset = queries.size(); + limit = 0; + } + + if ((queries.size() - offset) < limit) { + limit = queries.size() - offset; + } + + data.put("queries", queries.subList(offset, offset + limit)); + data.put("size", queries.size()); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, data, ""); + } + + @RequestMapping(value = "/query/format/{format}", method = RequestMethod.GET, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public void downloadQueryResultV2(@RequestHeader("Accept-Language") String lang, @PathVariable String format, SQLRequest sqlRequest, HttpServletResponse response) { + MsgPicker.setMsg(lang); + + SQLResponse result = queryServiceV2.doQueryWithCache(sqlRequest); + response.setContentType("text/" + format + ";charset=utf-8"); + response.setHeader("Content-Disposition", "attachment; filename=\"result." + format + "\""); + ICsvListWriter csvWriter = null; + + try { + csvWriter = new CsvListWriter(response.getWriter(), CsvPreference.STANDARD_PREFERENCE); + + List<String> headerList = new ArrayList<String>(); + + for (SelectedColumnMeta column : result.getColumnMetas()) { + headerList.add(column.getName()); + } + + String[] headers = new String[headerList.size()]; + csvWriter.writeHeader(headerList.toArray(headers)); + + for (List<String> row : result.getResults()) { + csvWriter.write(row); + } + } catch (IOException e) { + throw new InternalErrorException(e); + } finally { + IOUtils.closeQuietly(csvWriter); + } + } + + @RequestMapping(value = "/tables_and_columns", method = RequestMethod.GET, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getMetadataV2(@RequestHeader("Accept-Language") String lang, MetaRequest metaRequest) throws SQLException, IOException { + MsgPicker.setMsg(lang); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, queryServiceV2.getMetadataV2(metaRequest.getProject()), ""); + } + + public void setQueryService(QueryServiceV2 queryService) { + this.queryServiceV2 = queryService; + } +} http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/controller2/StreamingControllerV2.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller2/StreamingControllerV2.java b/server-base/src/main/java/org/apache/kylin/rest/controller2/StreamingControllerV2.java new file mode 100644 index 0000000..b810d0a --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller2/StreamingControllerV2.java @@ -0,0 +1,288 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.apache.kylin.rest.controller2; + +import java.io.IOException; +import java.util.UUID; + +import org.apache.commons.lang.StringUtils; +import org.apache.kylin.common.util.HadoopUtil; +import org.apache.kylin.common.util.JsonUtil; +import org.apache.kylin.metadata.model.TableDesc; +import org.apache.kylin.metadata.streaming.StreamingConfig; +import org.apache.kylin.rest.controller.BasicController; +import org.apache.kylin.rest.exception.BadRequestException; +import org.apache.kylin.rest.exception.ForbiddenException; +import org.apache.kylin.rest.exception.InternalErrorException; +import org.apache.kylin.rest.msg.Message; +import org.apache.kylin.rest.msg.MsgPicker; +import org.apache.kylin.rest.request.StreamingRequest; +import org.apache.kylin.rest.response.EnvelopeResponse; +import org.apache.kylin.rest.response.ResponseCode; +import org.apache.kylin.rest.service.KafkaConfigService; +import org.apache.kylin.rest.service.StreamingService; +import org.apache.kylin.rest.service.TableServiceV2; +import org.apache.kylin.source.kafka.config.KafkaConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; + +/** + * StreamingController is defined as Restful API entrance for UI. + * + * @author jiazhong + */ +@Controller +@RequestMapping(value = "/streaming") +public class StreamingControllerV2 extends BasicController { + private static final Logger logger = LoggerFactory.getLogger(StreamingControllerV2.class); + + @Autowired + @Qualifier("streamingMgmtService") + private StreamingService streamingService; + + @Autowired + @Qualifier("kafkaMgmtService") + private KafkaConfigService kafkaConfigService; + + @Autowired + @Qualifier("tableServiceV2") + private TableServiceV2 tableServiceV2; + + @RequestMapping(value = "/getConfig", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getStreamingsV2(@RequestHeader("Accept-Language") String lang, @RequestParam(value = "table", required = false) String table, @RequestParam(value = "pageOffset", required = false, defaultValue = "0") Integer pageOffset, @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize) throws IOException { + MsgPicker.setMsg(lang); + + int offset = pageOffset * pageSize; + int limit = pageSize; + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, streamingService.getStreamingConfigs(table, limit, offset), ""); + } + + @RequestMapping(value = "/getKfkConfig", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getKafkaConfigsV2(@RequestHeader("Accept-Language") String lang, @RequestParam(value = "kafkaConfigName", required = false) String kafkaConfigName, @RequestParam(value = "pageOffset", required = false, defaultValue = "0") Integer pageOffset, @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize) throws IOException { + MsgPicker.setMsg(lang); + + int offset = pageOffset * pageSize; + int limit = pageSize; + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, kafkaConfigService.getKafkaConfigs(kafkaConfigName, limit, offset), ""); + } + + /** + * + * create Streaming Schema + * @throws java.io.IOException + */ + + @RequestMapping(value = "", method = { RequestMethod.POST }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public void saveStreamingConfigV2(@RequestHeader("Accept-Language") String lang, @RequestBody StreamingRequest streamingRequest) throws IOException { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + String project = streamingRequest.getProject(); + TableDesc tableDesc = deserializeTableDescV2(streamingRequest); + if (null == tableDesc) { + throw new BadRequestException(msg.getINVALID_TABLE_DESC_DEFINITION()); + } + + StreamingConfig streamingConfig = deserializeSchemalDescV2(streamingRequest); + KafkaConfig kafkaConfig = deserializeKafkaSchemalDescV2(streamingRequest); + + boolean saveStreamingSuccess = false, saveKafkaSuccess = false; + + try { + tableServiceV2.addStreamingTable(tableDesc, project); + } catch (IOException e) { + throw new InternalErrorException(msg.getADD_STREAMING_TABLE_FAIL()); + } + + streamingConfig.setName(tableDesc.getIdentity()); + kafkaConfig.setName(tableDesc.getIdentity()); + try { + if (StringUtils.isEmpty(streamingConfig.getName())) { + logger.info("StreamingConfig should not be empty."); + throw new BadRequestException(msg.getEMPTY_STREAMING_CONFIG_NAME()); + } + try { + streamingConfig.setUuid(UUID.randomUUID().toString()); + streamingService.createStreamingConfig(streamingConfig); + saveStreamingSuccess = true; + } catch (IOException e) { + logger.error("Failed to save StreamingConfig:" + e.getLocalizedMessage(), e); + throw new InternalErrorException(msg.getSAVE_STREAMING_CONFIG_FAIL()); + } + try { + kafkaConfig.setUuid(UUID.randomUUID().toString()); + kafkaConfigService.createKafkaConfig(kafkaConfig); + saveKafkaSuccess = true; + } catch (IOException e) { + try { + streamingService.dropStreamingConfig(streamingConfig); + } catch (IOException e1) { + throw new InternalErrorException(msg.getCREATE_KAFKA_CONFIG_FAIL()); + } + logger.error("Failed to save KafkaConfig:" + e.getLocalizedMessage(), e); + throw new InternalErrorException(msg.getSAVE_KAFKA_CONFIG_FAIL()); + } + } finally { + if (saveKafkaSuccess == false || saveStreamingSuccess == false) { + + if (saveStreamingSuccess == true) { + StreamingConfig sConfig = streamingService.getStreamingManager().getStreamingConfig(streamingConfig.getName()); + try { + streamingService.dropStreamingConfig(sConfig); + } catch (IOException e) { + throw new InternalErrorException(msg.getROLLBACK_STREAMING_CONFIG_FAIL()); + } + } + if (saveKafkaSuccess == true) { + try { + KafkaConfig kConfig = kafkaConfigService.getKafkaConfig(kafkaConfig.getName()); + kafkaConfigService.dropKafkaConfig(kConfig); + } catch (IOException e) { + throw new InternalErrorException(msg.getROLLBACK_KAFKA_CONFIG_FAIL()); + } + } + } + + } + } + + @RequestMapping(value = "", method = { RequestMethod.PUT }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public void updateStreamingConfigV2(@RequestHeader("Accept-Language") String lang, @RequestBody StreamingRequest streamingRequest) throws IOException { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + StreamingConfig streamingConfig = deserializeSchemalDescV2(streamingRequest); + KafkaConfig kafkaConfig = deserializeKafkaSchemalDescV2(streamingRequest); + + if (streamingConfig == null) { + throw new BadRequestException(msg.getINVALID_STREAMING_CONFIG_DEFINITION()); + } + try { + streamingService.updateStreamingConfig(streamingConfig); + } catch (AccessDeniedException accessDeniedException) { + throw new ForbiddenException(msg.getUPDATE_STREAMING_CONFIG_NO_RIGHT()); + } + + try { + kafkaConfigService.updateKafkaConfig(kafkaConfig); + } catch (AccessDeniedException accessDeniedException) { + throw new ForbiddenException(msg.getUPDATE_KAFKA_CONFIG_NO_RIGHT()); + } + } + + @RequestMapping(value = "/{configName}", method = { RequestMethod.DELETE }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public void deleteConfigV2(@RequestHeader("Accept-Language") String lang, @PathVariable String configName) throws IOException { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + StreamingConfig config = streamingService.getStreamingManager().getStreamingConfig(configName); + KafkaConfig kafkaConfig = kafkaConfigService.getKafkaConfig(configName); + if (null == config) { + throw new BadRequestException(String.format(msg.getSTREAMING_CONFIG_NOT_FOUND(), configName)); + } + streamingService.dropStreamingConfig(config); + kafkaConfigService.dropKafkaConfig(kafkaConfig); + } + + private TableDesc deserializeTableDescV2(StreamingRequest streamingRequest) throws IOException { + Message msg = MsgPicker.getMsg(); + + TableDesc desc = null; + try { + logger.debug("Saving TableDesc " + streamingRequest.getTableData()); + desc = JsonUtil.readValue(streamingRequest.getTableData(), TableDesc.class); + } catch (JsonParseException e) { + logger.error("The TableDesc definition is invalid.", e); + throw new BadRequestException(msg.getINVALID_TABLE_DESC_DEFINITION()); + } catch (JsonMappingException e) { + logger.error("The data TableDesc definition is invalid.", e); + throw new BadRequestException(msg.getINVALID_TABLE_DESC_DEFINITION()); + } + + if (null != desc) { + String[] dbTable = HadoopUtil.parseHiveTableName(desc.getName()); + desc.setName(dbTable[1]); + desc.setDatabase(dbTable[0]); + desc.getIdentity(); + } + return desc; + } + + private StreamingConfig deserializeSchemalDescV2(StreamingRequest streamingRequest) throws IOException { + Message msg = MsgPicker.getMsg(); + + StreamingConfig desc = null; + try { + logger.debug("Saving StreamingConfig " + streamingRequest.getStreamingConfig()); + desc = JsonUtil.readValue(streamingRequest.getStreamingConfig(), StreamingConfig.class); + } catch (JsonParseException e) { + logger.error("The StreamingConfig definition is invalid.", e); + throw new BadRequestException(msg.getINVALID_STREAMING_CONFIG_DEFINITION()); + } catch (JsonMappingException e) { + logger.error("The data StreamingConfig definition is invalid.", e); + throw new BadRequestException(msg.getINVALID_STREAMING_CONFIG_DEFINITION()); + } + return desc; + } + + private KafkaConfig deserializeKafkaSchemalDescV2(StreamingRequest streamingRequest) throws IOException { + Message msg = MsgPicker.getMsg(); + + KafkaConfig desc = null; + try { + logger.debug("Saving KafkaConfig " + streamingRequest.getKafkaConfig()); + desc = JsonUtil.readValue(streamingRequest.getKafkaConfig(), KafkaConfig.class); + } catch (JsonParseException e) { + logger.error("The KafkaConfig definition is invalid.", e); + throw new BadRequestException(msg.getINVALID_KAFKA_CONFIG_DEFINITION()); + } catch (JsonMappingException e) { + logger.error("The data KafkaConfig definition is invalid.", e); + updateRequest(streamingRequest, false, e.getMessage()); + throw new BadRequestException(msg.getINVALID_KAFKA_CONFIG_DEFINITION()); + } + return desc; + } + + private void updateRequest(StreamingRequest request, boolean success, String message) { + request.setSuccessful(success); + request.setMessage(message); + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/controller2/TableControllerV2.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller2/TableControllerV2.java b/server-base/src/main/java/org/apache/kylin/rest/controller2/TableControllerV2.java new file mode 100644 index 0000000..4a4bf74 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller2/TableControllerV2.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.apache.kylin.rest.controller2; + +import org.apache.kylin.metadata.model.TableDesc; +import org.apache.kylin.rest.controller.BasicController; +import org.apache.kylin.rest.exception.BadRequestException; +import org.apache.kylin.rest.msg.Message; +import org.apache.kylin.rest.msg.MsgPicker; +import org.apache.kylin.rest.request.HiveTableRequestV2; +import org.apache.kylin.rest.response.EnvelopeResponse; +import org.apache.kylin.rest.response.ResponseCode; +import org.apache.kylin.rest.service.TableServiceV2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.io.IOException; + +/** + * @author xduo + */ +@Controller +@RequestMapping(value = "/tables") +public class TableControllerV2 extends BasicController { + + private static final Logger logger = LoggerFactory.getLogger(TableControllerV2.class); + + @Autowired + @Qualifier("tableServiceV2") + private TableServiceV2 tableServiceV2; + + /** + * Get available table list of the project + * + * @return Table metadata array + * @throws IOException + */ + + @RequestMapping(value = "", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getTableDescV2(@RequestHeader("Accept-Language") String lang, @RequestParam(value = "ext", required = false) boolean withExt, @RequestParam(value = "project", required = true) String project) throws IOException { + MsgPicker.setMsg(lang); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, tableServiceV2.getTableDescByProject(project, withExt), ""); + } + + /** + * Get available table list of the input database + * + * @return Table metadata array + * @throws IOException + */ + + @RequestMapping(value = "/{tableName:.+}", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getTableDescV2(@RequestHeader("Accept-Language") String lang, @PathVariable String tableName) { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + TableDesc table = tableServiceV2.getTableDescByName(tableName, false); + if (table == null) + throw new BadRequestException(String.format(msg.getHIVE_TABLE_NOT_FOUND(), tableName)); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, table, ""); + } + + @RequestMapping(value = "/load", method = { RequestMethod.POST }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse loadHiveTablesV2(@RequestHeader("Accept-Language") String lang, @RequestBody HiveTableRequestV2 requestV2) throws IOException { + MsgPicker.setMsg(lang); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, tableServiceV2.loadHiveTables(requestV2.getTables(), requestV2.getProject(), requestV2.isNeedProfile()), ""); + } + + @RequestMapping(value = "/load", method = { RequestMethod.DELETE }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse unLoadHiveTablesV2(@RequestHeader("Accept-Language") String lang, @RequestBody HiveTableRequestV2 requestV2) throws IOException { + MsgPicker.setMsg(lang); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, tableServiceV2.unloadHiveTables(requestV2.getTables(), requestV2.getProject()), ""); + } + + /** + * Regenerate table cardinality + * + * @return Table metadata array + * @throws IOException + */ + + @RequestMapping(value = "/cardinality", method = { RequestMethod.POST }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public void generateCardinalityV2(@RequestHeader("Accept-Language") String lang, @RequestBody HiveTableRequestV2 requestV2) throws IOException { + MsgPicker.setMsg(lang); + + String submitter = SecurityContextHolder.getContext().getAuthentication().getName(); + String[] tables = requestV2.getTables(); + + for (String table : tables) { + tableServiceV2.calculateCardinality(table.toUpperCase(), submitter); + } + } + + /** + * Show all databases in Hive + * + * @return Hive databases list + * @throws IOException + */ + + @RequestMapping(value = "/hive", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + private EnvelopeResponse showHiveDatabasesV2(@RequestHeader("Accept-Language") String lang) throws Exception { + MsgPicker.setMsg(lang); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, tableServiceV2.getHiveDbNames(), ""); + } + + /** + * Show all tables in a Hive database + * + * @return Hive table list + * @throws IOException + */ + + @RequestMapping(value = "/hive/{database}", method = { RequestMethod.GET }, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + private EnvelopeResponse showHiveTablesV2(@RequestHeader("Accept-Language") String lang, @PathVariable String database) throws Exception { + MsgPicker.setMsg(lang); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, tableServiceV2.getHiveTableNames(database), ""); + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/controller2/UserControllerV2.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller2/UserControllerV2.java b/server-base/src/main/java/org/apache/kylin/rest/controller2/UserControllerV2.java new file mode 100644 index 0000000..437caa1 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/controller2/UserControllerV2.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.apache.kylin.rest.controller2; + +import java.io.IOException; + +import org.apache.kylin.rest.controller.BasicController; +import org.apache.kylin.rest.exception.BadRequestException; +import org.apache.kylin.rest.msg.Message; +import org.apache.kylin.rest.msg.MsgPicker; +import org.apache.kylin.rest.response.EnvelopeResponse; +import org.apache.kylin.rest.response.ResponseCode; +import org.apache.kylin.rest.service.UserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * Handle user authentication request to protected kylin rest resources by + * spring security. + * + * @author xduo + * + */ +@Controller +@RequestMapping(value = "/user") +public class UserControllerV2 extends BasicController { + + private static final Logger logger = LoggerFactory.getLogger(UserControllerV2.class); + + @Autowired + @Qualifier("userService") + UserService userService; + + @RequestMapping(value = "/authentication", method = RequestMethod.POST, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse authenticateV2(@RequestHeader("Accept-Language") String lang) { + return authenticatedUserV2(lang); + } + + @RequestMapping(value = "/authentication", method = RequestMethod.GET, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse authenticatedUserV2(@RequestHeader("Accept-Language") String lang) { + MsgPicker.setMsg(lang); + Message msg = MsgPicker.getMsg(); + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + UserDetails data = null; + + if (authentication == null) { + logger.debug("authentication is null."); + throw new BadRequestException(msg.getAUTH_INFO_NOT_FOUND()); + } + + if (authentication.getPrincipal() instanceof UserDetails) { + logger.debug("authentication.getPrincipal() is " + authentication.getPrincipal()); + data = (UserDetails) authentication.getPrincipal(); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, data, ""); + } + + if (authentication.getDetails() instanceof UserDetails) { + logger.debug("authentication.getDetails() is " + authentication.getDetails()); + data = (UserDetails) authentication.getDetails(); + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, data, ""); + } + + throw new BadRequestException(msg.getAUTH_INFO_NOT_FOUND()); + } + + @RequestMapping(value = "/authentication/authorities", method = RequestMethod.GET, produces = { "application/vnd.apache.kylin-v2+json" }) + @ResponseBody + public EnvelopeResponse getAuthoritiesV2(@RequestHeader("Accept-Language") String lang) throws IOException { + MsgPicker.setMsg(lang); + + return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, userService.listUserAuthorities(), ""); + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/exception/BadRequestException.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/exception/BadRequestException.java b/server-base/src/main/java/org/apache/kylin/rest/exception/BadRequestException.java index af1995b..53d619a 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/exception/BadRequestException.java +++ b/server-base/src/main/java/org/apache/kylin/rest/exception/BadRequestException.java @@ -51,6 +51,7 @@ public class BadRequestException extends RuntimeException { this.code = code; } + public String getCode() { return code; } http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/model/ColumnMeta.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/model/ColumnMeta.java b/server-base/src/main/java/org/apache/kylin/rest/model/ColumnMeta.java index 15afb2e..ed52ae6 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/model/ColumnMeta.java +++ b/server-base/src/main/java/org/apache/kylin/rest/model/ColumnMeta.java @@ -24,29 +24,29 @@ import java.io.Serializable; */ public class ColumnMeta implements Serializable { private static final long serialVersionUID = 1L; - private String TABLE_CAT; - private String TABLE_SCHEM; - private String TABLE_NAME; - private String COLUMN_NAME; - private int DATA_TYPE; - private String TYPE_NAME; - private int COLUMN_SIZE; - private int BUFFER_LENGTH; - private int DECIMAL_DIGITS; - private int NUM_PREC_RADIX; - private int NULLABLE; - private String REMARKS; - private String COLUMN_DEF; - private int SQL_DATA_TYPE; - private int SQL_DATETIME_SUB; - private int CHAR_OCTET_LENGTH; - private int ORDINAL_POSITION; - private String IS_NULLABLE; - private String SCOPE_CATLOG; - private String SCOPE_SCHEMA; - private String SCOPE_TABLE; - private short SOURCE_DATA_TYPE; - private String IS_AUTOINCREMENT; + protected String TABLE_CAT; + protected String TABLE_SCHEM; + protected String TABLE_NAME; + protected String COLUMN_NAME; + protected int DATA_TYPE; + protected String TYPE_NAME; + protected int COLUMN_SIZE; + protected int BUFFER_LENGTH; + protected int DECIMAL_DIGITS; + protected int NUM_PREC_RADIX; + protected int NULLABLE; + protected String REMARKS; + protected String COLUMN_DEF; + protected int SQL_DATA_TYPE; + protected int SQL_DATETIME_SUB; + protected int CHAR_OCTET_LENGTH; + protected int ORDINAL_POSITION; + protected String IS_NULLABLE; + protected String SCOPE_CATLOG; + protected String SCOPE_SCHEMA; + protected String SCOPE_TABLE; + protected short SOURCE_DATA_TYPE; + protected String IS_AUTOINCREMENT; public ColumnMeta() { } http://git-wip-us.apache.org/repos/asf/kylin/blob/73a78dbe/server-base/src/main/java/org/apache/kylin/rest/model/ColumnMetaWithType.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/model/ColumnMetaWithType.java b/server-base/src/main/java/org/apache/kylin/rest/model/ColumnMetaWithType.java new file mode 100644 index 0000000..4d3abb8 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/model/ColumnMetaWithType.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.apache.kylin.rest.model; + +import java.io.Serializable; +import java.util.HashSet; + +/** + * Created by luwei on 17-4-26. + */ +public class ColumnMetaWithType extends ColumnMeta { + public static enum columnTypeEnum implements Serializable { + + DIMENSION, MEASURE, PK, FK + + } + + private HashSet<columnTypeEnum> TYPE; + + public ColumnMetaWithType(String tABLE_CAT, String tABLE_SCHEM, String tABLE_NAME, String cOLUMN_NAME, int dATA_TYPE, String tYPE_NAME, int cOLUMN_SIZE, int bUFFER_LENGTH, int dECIMAL_DIGITS, int nUM_PREC_RADIX, int nULLABLE, String rEMARKS, String cOLUMN_DEF, int sQL_DATA_TYPE, int sQL_DATETIME_SUB, int cHAR_OCTET_LENGTH, int oRDINAL_POSITION, String iS_NULLABLE, String sCOPE_CATLOG, String sCOPE_SCHEMA, String sCOPE_TABLE, short sOURCE_DATA_TYPE, String iS_AUTOINCREMENT) { + TABLE_CAT = tABLE_CAT; + TABLE_SCHEM = tABLE_SCHEM; + TABLE_NAME = tABLE_NAME; + COLUMN_NAME = cOLUMN_NAME; + DATA_TYPE = dATA_TYPE; + TYPE_NAME = tYPE_NAME; + COLUMN_SIZE = cOLUMN_SIZE; + BUFFER_LENGTH = bUFFER_LENGTH; + DECIMAL_DIGITS = dECIMAL_DIGITS; + NUM_PREC_RADIX = nUM_PREC_RADIX; + NULLABLE = nULLABLE; + REMARKS = rEMARKS; + COLUMN_DEF = cOLUMN_DEF; + SQL_DATA_TYPE = sQL_DATA_TYPE; + SQL_DATETIME_SUB = sQL_DATETIME_SUB; + CHAR_OCTET_LENGTH = cHAR_OCTET_LENGTH; + ORDINAL_POSITION = oRDINAL_POSITION; + IS_NULLABLE = iS_NULLABLE; + SCOPE_CATLOG = sCOPE_CATLOG; + SCOPE_SCHEMA = sCOPE_SCHEMA; + SCOPE_TABLE = sCOPE_TABLE; + SOURCE_DATA_TYPE = sOURCE_DATA_TYPE; + IS_AUTOINCREMENT = iS_AUTOINCREMENT; + TYPE = new HashSet<columnTypeEnum>(); + } + + public HashSet<columnTypeEnum> getTYPE() { + return TYPE; + } + + public void setTYPE(HashSet<columnTypeEnum> TYPE) { + this.TYPE = TYPE; + } +}