/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.wso2.carbon.mediation.transport.handlers.requestprocessors.swagger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.rest.API;
import org.apache.synapse.rest.Resource;
import org.apache.synapse.rest.dispatch.DispatcherHelper;
import org.apache.synapse.rest.dispatch.URLMappingBasedDispatcher;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
/**
* Generalized object structure for Swagger definitions of APIs. This structure contains set of Maps which compatible
* with both JSON and YAML formats.
*/
public class GenericApiObjectDefinition {
private static final Log log = LogFactory.getLog(GenericApiObjectDefinition.class);
private API api;
public GenericApiObjectDefinition(API api) {
this.api = api;
}
/**
* Provides a map which represents the structure of swagger definition.
*
* @return Map containing information for swagger definition
*/
public Map<String, Object> getDefinitionMap() {
Map<String, Object> apiMap = new LinkedHashMap<>();
apiMap.put(SwaggerConstants.SWAGGER, SwaggerConstants.SWAGGER_VERSION);
apiMap.put(SwaggerConstants.INFO, getInfoMap());
if (api.getHost() != null) {
apiMap.put(SwaggerConstants.HOST, api.getHost());
}
apiMap.put(SwaggerConstants.BASE_PATH, api.getContext());
apiMap.put(SwaggerConstants.SCHEMES, getSchemes());
if (getPathMap() != null && !getPathMap().isEmpty()) {
apiMap.put(SwaggerConstants.PATHS, getPathMap());
}
return apiMap;
}
/**
* Provides structure for the "responses" element in swagger definition.
*
* @return Map containing information for responses element
*/
private Map<String, Object> getResponsesMap() {
Map<String, Object> responsesMap = new LinkedHashMap<>();
Map<String, Object> responseDetailsMap = new LinkedHashMap<>();
/* Use a default response since these information is not available in synapse configuration for APIs */
responseDetailsMap.put(SwaggerConstants.DESCRIPTION, SwaggerConstants.DEFAULT_RESPONSE);
responsesMap.put(SwaggerConstants.DEFAULT_VALUE, responseDetailsMap);
if(log.isDebugEnabled()){
log.debug("Response map created with size " + responsesMap.size());
}
return responsesMap;
}
/**
* Provides structure for the "info" element in swagger definition.
*
* @return Map containing information for info element
*/
private Map<String, Object> getInfoMap() {
Map<String, Object> infoMap = new LinkedHashMap<>();
infoMap.put(SwaggerConstants.DESCRIPTION, (SwaggerConstants.API_DESC_PREFIX + api.getAPIName()));
infoMap.put(SwaggerConstants.TITLE, api.getName());
infoMap.put(SwaggerConstants.VERSION, (api.getVersion() != null && !api.getVersion().equals(""))
? api.getVersion() : SwaggerConstants.DEFAULT_API_VERSION);
if(log.isDebugEnabled()){
log.debug("Info map created with size " + infoMap.size());
}
return infoMap;
}
/**
* Provides structure for the "paths" element in swagger definition.
*
* @return Map containing information for paths element
*/
private Map<String, Object> getPathMap() {
Map<String, Object> pathsMap = new LinkedHashMap<>();
for (Resource resource : api.getResources()) {
Map<String, Object> methodMap = new LinkedHashMap<>();
DispatcherHelper resourceDispatcherHelper = resource.getDispatcherHelper();
for (String method : resource.getMethods()) {
if (method != null) {
Map<String, Object> methodInfoMap = new LinkedHashMap<>();
methodInfoMap.put(SwaggerConstants.RESPONSES, getResponsesMap());
if (resourceDispatcherHelper != null) {
Object[] parameters = getResourceParameters(resource);
if (parameters.length > 0) {
methodInfoMap.put(SwaggerConstants.PARAMETERS, parameters);
}
}
methodMap.put(method.toLowerCase(), methodInfoMap);
}
}
pathsMap.put(getPathFromUrl(resourceDispatcherHelper == null ? SwaggerConstants.PATH_SEPARATOR
: resourceDispatcherHelper.getString()), methodMap);
}
if(log.isDebugEnabled()){
log.debug("Paths map created with size " + pathsMap.size());
}
return pathsMap;
}
/**
* Provides list of schemas support by the API.
*
* @return Array of String containing schemas list
*/
private String[] getSchemes() {
String[] protocols;
switch (api.getProtocol()) {
case SwaggerConstants.PROTOCOL_HTTP_ONLY:
protocols = new String[]{SwaggerConstants.PROTOCOL_HTTP};
break;
case SwaggerConstants.PROTOCOL_HTTPS_ONLY:
protocols = new String[]{SwaggerConstants.PROTOCOL_HTTPS};
break;
default:
protocols = new String[]{SwaggerConstants.PROTOCOL_HTTP, SwaggerConstants.PROTOCOL_HTTPS};
break;
}
return protocols;
}
/**
* Generate resource parameters for the given resource.
*
* @param resource instance of Resource in the API
* @return Array of parameter objects supported by the API
*/
private Object[] getResourceParameters(Resource resource) {
ArrayList<Map<String, Object>> parameterList = new ArrayList<>();
String uri = resource.getDispatcherHelper().getString();
if (resource.getDispatcherHelper() instanceof URLMappingBasedDispatcher) {
generateParameterList(parameterList, uri, false);
} else {
generateParameterList(parameterList, uri, true);
}
if(log.isDebugEnabled()){
log.debug("Parameters processed for the URI + " + uri + " size " + parameterList.size());
}
return parameterList.toArray();
}
/**
* Generate URI and Path parameters for the given URI.
*
* @param parameterList List of maps to be populated with parameters
* @param uriString URI string to be used to extract parameters
* @param generateBothTypes Indicates whether to consider both query and uri parameters. True if both to be
* considered.
*/
private void generateParameterList(ArrayList<Map<String, Object>> parameterList, String uriString, boolean
generateBothTypes) {
if (uriString == null) {
return;
}
if (generateBothTypes) {
String[] params = getQueryStringFromUrl(uriString).split("&");
for (String parameter : params) {
if (parameter != null) {
int pos = parameter.indexOf('=');
if (pos > 0) {
parameterList.add(getParametersMap(parameter.substring(0, pos), SwaggerConstants.PARAMETER_IN_QUERY));
}
}
}
}
Matcher matcher = SwaggerConstants.PATH_PARAMETER_PATTERN.matcher(getPathFromUrl(uriString));
while (matcher.find()) {
parameterList.add(getParametersMap(matcher.group(1), SwaggerConstants.PARAMETER_IN_PATH));
}
}
/**
* Create map of parameters from give name and type. Default values are used for other fields since those are not
* provided by the synapse configuration of the API.
*
* @param parameterName Name of the parameter
* @param parameterType Type of the parameter
* @return Map containing parameter properties
*/
private Map<String, Object> getParametersMap(String parameterName, String parameterType) {
Map<String, Object> parameterMap = new LinkedHashMap<>();
parameterMap.put(SwaggerConstants.PARAMETER_DESCRIPTION, parameterName);
parameterMap.put(SwaggerConstants.PARAMETER_IN, parameterType);
parameterMap.put(SwaggerConstants.PARAMETER_NAME, parameterName);
parameterMap.put(SwaggerConstants.PARAMETER_REQUIRED, true);
/* Type will be "string" for all parameters since synapse configuration does
not contain suc information for APIs. */
parameterMap.put(SwaggerConstants.PARAMETER_TYPE, SwaggerConstants.PARAMETER_TYPE_STRING);
return parameterMap;
}
/**
* Get the path portion from the URI.
*
* @param uri String URI to be analysed
* @return String containing the path portion of the URI
*/
private String getPathFromUrl(String uri) {
int pos = uri.indexOf("?");
if (pos > 0) {
return uri.substring(0, pos);
}
return uri;
}
/**
* Get query parameter portion from the URI.
*
* @param uri String URI to be analysed
* @return String containing the URI parameter portion of the URI
*/
private String getQueryStringFromUrl(String uri) {
int pos = uri.indexOf("?");
if (pos > 0) {
return uri.substring(pos + 1);
}
return "";
}
}