blob: 3941d64e5966e92ed9089a99d4dbf5935e1e8dc3 [file] [log] [blame]
/*
* 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.dubbo.admin.controller;
import org.apache.dubbo.admin.annotation.Authority;
import org.apache.dubbo.admin.controller.editors.CustomLocalDateEditor;
import org.apache.dubbo.admin.controller.editors.CustomLocalDateTimeEditor;
import org.apache.dubbo.admin.model.dto.docs.ApiInfoRequest;
import org.apache.dubbo.admin.model.dto.docs.CallDubboServiceRequest;
import org.apache.dubbo.admin.model.dto.docs.CallDubboServiceRequestInterfaceParam;
import org.apache.dubbo.admin.utils.ApiDocsDubboGenericUtil;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/**
* dubbo doc ui server api.
*/
@Authority(needLogin = true)
@Api(tags = {"dubbo-api-docs-api"})
@RestController
@RequestMapping("/api/{env}/docs")
public class ApiDocsController {
private static final Logger LOG = LoggerFactory.getLogger(ApiDocsController.class);
private static final SimplePropertyPreFilter CLASS_NAME_PRE_FILTER = new SimplePropertyPreFilter(HashMap.class);
static {
// Remove the "class" attribute from the returned result
CLASS_NAME_PRE_FILTER.getExcludes().add("class");
}
/**
* retries for dubbo provider
*/
@Value("${dubbo.consumer.retries:0}")
private int retries;
/**
* timeout
*/
@Value("${dubbo.consumer.timeout:1000}")
private int timeout;
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
binder.registerCustomEditor(LocalDate.class, new CustomLocalDateEditor());
binder.registerCustomEditor(LocalDateTime.class, new CustomLocalDateTimeEditor());
}
/**
* Set timeout and retries for {@link ApiDocsDubboGenericUtil}
* 2020-11-02 11:16:28
* @param:
* @return void
*/
@PostConstruct
public void setRetriesAndTimeout(){
ApiDocsDubboGenericUtil.setRetriesAndTimeout(retries, timeout);
}
@ApiOperation(value = "request dubbo api", notes = "request dubbo api", httpMethod = "POST", produces = "application/json")
@PostMapping("/requestDubbo")
public String callDubboService(CallDubboServiceRequest dubboCfg, @RequestBody List<CallDubboServiceRequestInterfaceParam> methodparams){
String[] paramTypes = null;
Object[] paramValues = null;
if(null != methodparams && !methodparams.isEmpty()){
paramTypes = new String[methodparams.size()];
paramValues = new Object[methodparams.size()];
for(int i = 0; i < methodparams.size(); i++){
CallDubboServiceRequestInterfaceParam param = methodparams.get(i);
paramTypes[i] = param.getParamType();
Object paramValue = param.getParamValue();
if(isBaseType(param.getParamType()) && null != paramValue){
if(paramValue instanceof Map){
Map<?, ?> tempMap = (Map<?, ?>) paramValue;
if(!tempMap.isEmpty()) {
Object tempParamValue = tempMap.values().stream().findFirst().orElse(null);
paramValues[i] = this.emptyString2Null(tempParamValue);
}
} else {
paramValues[i] = emptyString2Null(paramValue);
}
} else {
this.emptyString2Null(paramValue);
paramValues[i] = paramValue;
}
}
}
if (null == paramTypes) {
paramTypes = new String[0];
}
if (null == paramValues) {
paramValues = new Object[0];
}
CompletableFuture<Object> future = ApiDocsDubboGenericUtil.invoke(dubboCfg.getRegistryCenterUrl(), dubboCfg.getInterfaceClassName(),
dubboCfg.getMethodName(), dubboCfg.isAsync(), dubboCfg.getVersion(), paramTypes, paramValues, dubboCfg.getGroup());
try {
Object objResult = future.get();
return JSON.toJSONString(objResult, CLASS_NAME_PRE_FILTER);
} catch (InterruptedException | ExecutionException e) {
LOG.error(e.getMessage(), e);
return "Some exceptions have occurred, please check the log.";
}
}
private Object emptyString2Null(Object paramValue){
if(null != paramValue) {
if (paramValue instanceof String && StringUtils.isBlank((String) paramValue)) {
return null;
} else if (paramValue instanceof Map) {
Map<String, Object> tempMap = (Map<String, Object>) paramValue;
tempMap.forEach((k, v) -> {
if (v != null && v instanceof String && StringUtils.isBlank((String) v)) {
tempMap.put(k, null);
} else {
this.emptyString2Null(v);
}
});
}
}
return paramValue;
}
@ApiOperation(value = "Get basic information of all modules, excluding API parameter information", notes = "Get basic information of all modules, excluding API parameter information", httpMethod = "GET", produces = "application/json")
@GetMapping("/apiModuleList")
public String apiModuleList(ApiInfoRequest apiInfoRequest){
CallDubboServiceRequest req = new CallDubboServiceRequest();
req.setRegistryCenterUrl("dubbo://" + apiInfoRequest.getDubboIp() + ":" + apiInfoRequest.getDubboPort());
req.setInterfaceClassName("org.apache.dubbo.apidocs.core.providers.IDubboDocProvider");
req.setMethodName("apiModuleList");
req.setAsync(false);
return callDubboService(req, null);
}
@ApiOperation(value = "Get the parameter information of the specified API", notes = "Get the parameter information of the specified API", httpMethod = "GET", produces = "application/json")
@GetMapping("/apiParamsResp")
public String apiParamsResp(ApiInfoRequest apiInfoRequest){
CallDubboServiceRequest req = new CallDubboServiceRequest();
req.setRegistryCenterUrl("dubbo://" + apiInfoRequest.getDubboIp() + ":" + apiInfoRequest.getDubboPort());
req.setInterfaceClassName("org.apache.dubbo.apidocs.core.providers.IDubboDocProvider");
req.setMethodName("apiParamsResponseInfo");
req.setAsync(false);
List<CallDubboServiceRequestInterfaceParam> methodparams = new ArrayList<>(1);
CallDubboServiceRequestInterfaceParam param = new CallDubboServiceRequestInterfaceParam();
param.setParamType(String.class.getName());
param.setParamValue(apiInfoRequest.getApiName());
methodparams.add(param);
return callDubboService(req, methodparams);
}
private static boolean isBaseType(String typeStr) {
if ("java.lang.Integer".equals(typeStr) ||
"java.lang.Byte".equals(typeStr) ||
"java.lang.Long".equals(typeStr) ||
"java.lang.Double".equals(typeStr) ||
"java.lang.Float".equals(typeStr) ||
"java.lang.Character".equals(typeStr) ||
"java.lang.Short".equals(typeStr) ||
"java.lang.Boolean".equals(typeStr) ||
"java.lang.String".equals(typeStr) ||
"java.math.BigDecimal".equals(typeStr) ||
"java.math.BigInteger".equals(typeStr)) {
return true;
}
return false;
}
}