diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index b4ce24c2bc9..d3bfcd66afc 100644 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -158,6 +158,7 @@ public class ApiConstants { public static final String RECEIVED_BYTES = "receivedbytes"; public static final String REQUIRES_HVM = "requireshvm"; public static final String RESOURCE_TYPE = "resourcetype"; + public static final String RESPONSE = "response"; public static final String QUERY_FILTER = "queryfilter"; public static final String SCHEDULE = "schedule"; public static final String SCOPE = "scope"; diff --git a/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java b/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java index feab20ac5cf..ed3e1751027 100644 --- a/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java +++ b/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java @@ -16,12 +16,12 @@ // under the License. package org.apache.cloudstack.api.command.user.discovery; -import com.cloud.user.Account; import com.cloud.user.UserContext; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.PlugService; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.ListResponse; @@ -30,8 +30,8 @@ import org.apache.cloudstack.api.response.ApiDiscoveryResponse; import org.apache.log4j.Logger; -@APICommand(name = "listApis", responseObject = ApiDiscoveryResponse.class, description = "lists all available apis on the server, provided by Api Discovery plugin", since = "4.1.0") -public class ListApisCmd extends BaseListCmd { +@APICommand(name = "listApis", responseObject = ApiDiscoveryResponse.class, description = "lists all available apis on the server, provided by the Api Discovery plugin", since = "4.1.0") +public class ListApisCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(ListApisCmd.class.getName()); private static final String s_name = "listapisresponse"; @@ -39,14 +39,16 @@ public class ListApisCmd extends BaseListCmd { @PlugService ApiDiscoveryService _apiDiscoveryService; + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, description="API name") + private String name; + @Override public void execute() throws ServerApiException { if (_apiDiscoveryService != null) { - Account caller = UserContext.current().getCaller(); RoleType roleType = _accountService.getRoleType(UserContext.current().getCaller()); - ListResponse response = (ListResponse) _apiDiscoveryService.listApis(roleType); + ListResponse response = (ListResponse) _apiDiscoveryService.listApis(roleType, name); if (response == null) { - throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Api Discovery plugin was unable to find and process any apis"); + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Api Discovery plugin was unable to find an api by that name or process any apis"); } response.setResponseName(getCommandName()); this.setResponseObject(response); @@ -57,4 +59,10 @@ public class ListApisCmd extends BaseListCmd { public String getCommandName() { return s_name; } + + @Override + public long getEntityOwnerId() { + // no owner is needed for list command + return 0; + } } diff --git a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java index a1d440e9ccf..611493bfc08 100644 --- a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java +++ b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java @@ -22,5 +22,5 @@ import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.response.ListResponse; public interface ApiDiscoveryService extends PluggableService { - ListResponse listApis(RoleType roleType); + ListResponse listApis(RoleType roleType, String apiName); } diff --git a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java index 9f4031c4769..5f84486ae49 100644 --- a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java +++ b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java @@ -16,13 +16,13 @@ // under the License. package org.apache.cloudstack.discovery; +import com.cloud.serializer.Param; import com.cloud.server.ManagementServer; import com.cloud.utils.ReflectUtil; -import com.cloud.utils.component.Adapters; +import com.cloud.utils.StringUtils; import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.component.Inject; import com.cloud.utils.component.PluggableService; -import org.apache.cloudstack.acl.APIChecker; +import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseCmd; @@ -32,6 +32,7 @@ import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.ApiDiscoveryResponse; import org.apache.cloudstack.api.response.ApiParameterResponse; +import org.apache.cloudstack.api.response.ApiResponseResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.log4j.Logger; @@ -39,6 +40,7 @@ import javax.ejb.Local; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -47,19 +49,24 @@ import java.util.Set; public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { private static final Logger s_logger = Logger.getLogger(ApiDiscoveryServiceImpl.class); + private static Map> _roleTypeDiscoveryResponseListMap; + private static Map _apiNameDiscoveryResponseMap = new HashMap(); - private static Map> _roleTypeDiscoveryResponseListMap = - new HashMap>(); - private static Map> _apiNameRoleTypeListMap = null; protected ApiDiscoveryServiceImpl() { super(); - for (RoleType roleType: RoleType.values()) - _roleTypeDiscoveryResponseListMap.put(roleType, new ArrayList()); - cacheListApiResponse(); + if (_roleTypeDiscoveryResponseListMap == null) { + long startTime = System.nanoTime(); + _roleTypeDiscoveryResponseListMap = new HashMap>(); + for (RoleType roleType: RoleType.values()) + _roleTypeDiscoveryResponseListMap.put(roleType, new ArrayList()); + cacheResponseMap(); + long endTime = System.nanoTime(); + s_logger.info("Api Discovery Service: Annotation, docstrings, api relation graph processed in " + (endTime - startTime) / 1000000.0 + " ms"); + } } private Map> getApiNameRoleTypeListMap() { @@ -86,10 +93,12 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { return apiNameRoleTypeMap; } - private void cacheListApiResponse() { + private void cacheResponseMap() { Set> cmdClasses = ReflectUtil.getClassesWithAnnotation(APICommand.class, new String[]{"org.apache.cloudstack.api", "com.cloud.api"}); + Map> responseApiNameListMap = new HashMap>(); + for(Class cmdClass: cmdClasses) { APICommand apiCmdAnnotation = cmdClass.getAnnotation(APICommand.class); if (apiCmdAnnotation == null) @@ -100,10 +109,32 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { continue; String apiName = apiCmdAnnotation.name(); + String responseName = apiCmdAnnotation.responseObject().getName(); + if (!responseName.contains("SuccessResponse")) { + if (!responseApiNameListMap.containsKey(responseName)) + responseApiNameListMap.put(responseName, new ArrayList()); + responseApiNameListMap.get(responseName).add(apiName); + } ApiDiscoveryResponse response = new ApiDiscoveryResponse(); response.setName(apiName); response.setDescription(apiCmdAnnotation.description()); - response.setSince(apiCmdAnnotation.since()); + if (!apiCmdAnnotation.since().isEmpty()) + response.setSince(apiCmdAnnotation.since()); + response.setRelated(responseName); + + Field[] responseFields = apiCmdAnnotation.responseObject().getDeclaredFields(); + for(Field responseField: responseFields) { + SerializedName serializedName = responseField.getAnnotation(SerializedName.class); + if(serializedName != null) { + ApiResponseResponse responseResponse = new ApiResponseResponse(); + responseResponse.setName(serializedName.value()); + Param param = responseField.getAnnotation(Param.class); + if (param != null) + responseResponse.setDescription(param.description()); + responseResponse.setType(responseField.getType().getSimpleName().toLowerCase()); + response.addApiResponse(responseResponse); + } + } Field[] fields = ReflectUtil.getAllFieldsForClass(cmdClass, new Class[] {BaseCmd.class, BaseAsyncCmd.class, BaseAsyncCreateCmd.class}); @@ -122,23 +153,50 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { ApiParameterResponse paramResponse = new ApiParameterResponse(); paramResponse.setName(parameterAnnotation.name()); paramResponse.setDescription(parameterAnnotation.description()); - paramResponse.setType(parameterAnnotation.type().toString()); + paramResponse.setType(parameterAnnotation.type().toString().toLowerCase()); paramResponse.setLength(parameterAnnotation.length()); paramResponse.setRequired(parameterAnnotation.required()); - paramResponse.setSince(parameterAnnotation.since()); + if (!parameterAnnotation.since().isEmpty()) + paramResponse.setSince(parameterAnnotation.since()); + paramResponse.setRelated(parameterAnnotation.entityType()[0].getName()); response.addParam(paramResponse); } } - response.setObjectName("apis"); + response.setObjectName("api"); + _apiNameDiscoveryResponseMap.put(apiName, response); + } + + for (String apiName: _apiNameDiscoveryResponseMap.keySet()) { + ApiDiscoveryResponse response = _apiNameDiscoveryResponseMap.get(apiName); + Set processedParams = new HashSet(); + for (ApiParameterResponse param: response.getParams()) { + if (responseApiNameListMap.containsKey(param.getRelated())) { + List relatedApis = responseApiNameListMap.get(param.getRelated()); + param.setRelated(StringUtils.join(relatedApis, ",")); + } else { + param.setRelated(null); + } + processedParams.add(param); + } + response.setParams(processedParams); + + if (responseApiNameListMap.containsKey(response.getRelated())) { + List relatedApis = responseApiNameListMap.get(response.getRelated()); + relatedApis.remove(apiName); + response.setRelated(StringUtils.join(relatedApis, ",")); + } else { + response.setRelated(null); + } _apiNameDiscoveryResponseMap.put(apiName, response); } } @Override - public ListResponse listApis(RoleType roleType) { + public ListResponse listApis(RoleType roleType, String name) { // Creates roles based response list cache the first time listApis is called // Due to how adapters work, this cannot be done when mgmt loads if (_apiNameRoleTypeListMap == null) { + long startTime = System.nanoTime(); _apiNameRoleTypeListMap = getApiNameRoleTypeListMap(); for (Map.Entry> entry: _apiNameRoleTypeListMap.entrySet()) { String apiName = entry.getKey(); @@ -147,9 +205,21 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { _apiNameDiscoveryResponseMap.get(apiName)); } } + long endTime = System.nanoTime(); + s_logger.info("Api Discovery Service: List apis cached in " + (endTime - startTime) / 1000000.0 + " ms"); } ListResponse response = new ListResponse(); - response.setResponses(_roleTypeDiscoveryResponseListMap.get(roleType)); + if (name != null) { + if (!_apiNameDiscoveryResponseMap.containsKey(name)) + return null; + + List singleResponse = new ArrayList(); + singleResponse.add(_apiNameDiscoveryResponseMap.get(name)); + response.setResponses(singleResponse); + + } else { + response.setResponses(_roleTypeDiscoveryResponseListMap.get(roleType)); + } return response; }