org.wso2.carbon.apimgt.core.impl.APIDefinitionFromSwagger20.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.apimgt.core.impl.APIDefinitionFromSwagger20.java

Source

/***********************************************************************************************************************
 * *
 * *   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.apimgt.core.impl;

import com.google.gson.Gson;
import io.swagger.models.Contact;
import io.swagger.models.HttpMethod;
import io.swagger.models.Info;
import io.swagger.models.Model;
import io.swagger.models.ModelImpl;
import io.swagger.models.Operation;
import io.swagger.models.Path;
import io.swagger.models.Response;
import io.swagger.models.Swagger;
import io.swagger.models.parameters.BodyParameter;
import io.swagger.models.parameters.FormParameter;
import io.swagger.models.parameters.Parameter;
import io.swagger.models.parameters.PathParameter;
import io.swagger.models.parameters.QueryParameter;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.StringProperty;
import io.swagger.parser.SwaggerParser;
import io.swagger.util.Json;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.apimgt.core.api.APIDefinition;
import org.wso2.carbon.apimgt.core.exception.APIManagementException;
import org.wso2.carbon.apimgt.core.exception.ExceptionCodes;
import org.wso2.carbon.apimgt.core.internal.ServiceReferenceHolder;
import org.wso2.carbon.apimgt.core.models.API;
import org.wso2.carbon.apimgt.core.models.APIResource;
import org.wso2.carbon.apimgt.core.models.BusinessInformation;
import org.wso2.carbon.apimgt.core.models.CompositeAPI;
import org.wso2.carbon.apimgt.core.models.Scope;
import org.wso2.carbon.apimgt.core.models.URITemplateParam;
import org.wso2.carbon.apimgt.core.models.UriTemplate;
import org.wso2.carbon.apimgt.core.util.APIMgtConstants;
import org.wso2.carbon.apimgt.core.util.APIUtils;
import org.wso2.msf4j.Request;
import org.wso2.msf4j.ServiceMethodInfo;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Implementation for Swagger 2.0
 */
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "DM_CONVERT_CASE", justification = "Didn't need to do "
        + "as String already did internally")
public class APIDefinitionFromSwagger20 implements APIDefinition {

    private static final Logger log = LoggerFactory.getLogger(APIDefinitionFromSwagger20.class);
    private static Map<String, Map<String, Object>> localConfigMap = new ConcurrentHashMap<>();

    @Override
    public String getScopeOfResourcePath(String resourceConfigsJSON, Request request,
            ServiceMethodInfo serviceMethodInfo) throws APIManagementException {
        SwaggerParser swaggerParser = new SwaggerParser();
        Swagger swagger = swaggerParser.parse(resourceConfigsJSON);
        String basepath = swagger.getBasePath();
        String verb = (String) request.getProperty(APIMgtConstants.HTTP_METHOD);
        //TODO change to this if msf4j2.3.0-m2 or higher
        //        Method resourceMethod = (Method) request.getProperty("method");
        Method resourceMethod = serviceMethodInfo.getMethod();

        if (resourceMethod == null || verb == null) {
            String message = "Could not read required properties from HTTP Request. HTTP_METHOD=" + verb
                    + " resourceTemplate=" + resourceMethod;
            log.error(message);
            throw new APIManagementException(message, ExceptionCodes.SWAGGER_URL_MALFORMED);
        }
        String apiPrefix = resourceMethod.getDeclaringClass().getAnnotation(javax.ws.rs.ApplicationPath.class)
                .value();
        String pathTemplate = "";
        if (resourceMethod.getAnnotation(javax.ws.rs.Path.class) != null) {
            pathTemplate = resourceMethod.getAnnotation(javax.ws.rs.Path.class).value();
        }
        String nameSpace = getNamespaceFromBasePath(basepath);

        //if namespace is not available in local cache add it.
        if (nameSpace != null && !localConfigMap.containsKey(nameSpace)) {
            localConfigMap.put(nameSpace, new ConcurrentHashMap<>());
        }

        if (nameSpace != null && localConfigMap.containsKey(nameSpace) && localConfigMap.get(nameSpace).isEmpty()) {
            populateConfigMapForScopes(swagger, nameSpace);
        }

        String resourceConfig = verb + "_" + apiPrefix + pathTemplate;
        if (localConfigMap.get(nameSpace).containsKey(resourceConfig)) {
            return localConfigMap.get(nameSpace).get(resourceConfig).toString();
        }
        return null;
    }

    /*
    * This method populates resource to scope mappings into localConfigMap
    *
    * @param swagger swagger oc of the apis
    * @param String namespacee unigue identifier of the api
    *
    * */
    private void populateConfigMapForScopes(Swagger swagger, String namespace) {
        Map<String, String> configMap = ServiceReferenceHolder.getInstance().getRestAPIConfigurationMap(namespace);
        //update local cache with configs defined in configuration file(dep.yaml)
        if (!localConfigMap.containsKey(namespace)) {
            localConfigMap.put(namespace, new ConcurrentHashMap<>());
        }
        if (configMap != null) {
            localConfigMap.get(namespace).putAll(configMap);
        }
        //update local cache with the resource to scope mapping read from swagger
        if (swagger != null) {
            for (Map.Entry<String, Path> entry : swagger.getPaths().entrySet()) {
                Path resource = entry.getValue();
                Map<HttpMethod, Operation> operationsMap = resource.getOperationMap();
                for (Map.Entry<HttpMethod, Operation> httpverbEntry : operationsMap.entrySet()) {
                    if (httpverbEntry.getValue().getVendorExtensions().size() > 0 && httpverbEntry.getValue()
                            .getVendorExtensions().get(APIMgtConstants.SWAGGER_X_SCOPE) != null) {
                        String path = httpverbEntry.getKey() + "_" + entry.getKey();
                        if (!localConfigMap.get(namespace).containsKey(path)) {
                            localConfigMap.get(namespace).put(path, httpverbEntry.getValue().getVendorExtensions()
                                    .get(APIMgtConstants.SWAGGER_X_SCOPE).toString());
                        }
                    }

                }
            }
        }
    }

    @Override
    public List<APIResource> parseSwaggerAPIResources(StringBuilder resourceConfigsJSON)
            throws APIManagementException {
        List<APIResource> apiResources = new ArrayList<>();
        SwaggerParser swaggerParser = new SwaggerParser();
        Swagger swagger = swaggerParser.parse(resourceConfigsJSON.toString());
        Map<String, Path> resourceList = swagger.getPaths();
        Map<String, Scope> scopeMap = getScopes(resourceConfigsJSON.toString());
        for (Map.Entry<String, Path> resourceEntry : resourceList.entrySet()) {
            Path resource = resourceEntry.getValue();
            UriTemplate.UriTemplateBuilder uriTemplateBuilder = new UriTemplate.UriTemplateBuilder();
            uriTemplateBuilder.uriTemplate(resourceEntry.getKey());
            for (Map.Entry<HttpMethod, Operation> operationEntry : resource.getOperationMap().entrySet()) {
                Operation operation = operationEntry.getValue();
                Map<String, Object> vendorExtensions = operation.getVendorExtensions();
                APIResource.Builder apiResourceBuilder = new APIResource.Builder();
                List<String> producesList = operation.getProduces();
                if (producesList != null) {
                    String produceSeparatedString = "\"";
                    produceSeparatedString += String.join("\",\"", producesList) + "\"";
                    apiResourceBuilder.produces(produceSeparatedString);
                }
                List<String> consumesList = operation.getConsumes();
                if (consumesList != null) {
                    String consumesSeparatedString = "\"";
                    consumesSeparatedString += String.join("\",\"", consumesList) + "\"";
                    apiResourceBuilder.consumes(consumesSeparatedString);
                }
                if (operation.getOperationId() != null) {
                    uriTemplateBuilder.templateId(operation.getOperationId());
                } else {
                    uriTemplateBuilder.templateId(APIUtils.generateOperationIdFromPath(resourceEntry.getKey(),
                            operationEntry.getKey().name()));
                }
                uriTemplateBuilder.httpVerb(operationEntry.getKey().name());
                String scope = (String) vendorExtensions.get(APIMgtConstants.SWAGGER_X_SCOPE);
                if (StringUtils.isNotEmpty(scope)) {
                    apiResourceBuilder.scope(scopeMap.get(scope));
                }
                apiResourceBuilder.uriTemplate(uriTemplateBuilder.build());
                apiResources.add(apiResourceBuilder.build());
            }
        }
        resourceConfigsJSON.setLength(0);
        resourceConfigsJSON.append(Json.pretty(swagger));
        return apiResources;
    }

    @Override
    public Map<String, Scope> getScopes(String resourceConfigsJSON) throws APIManagementException {

        SwaggerParser swaggerParser = new SwaggerParser();
        Swagger swagger = swaggerParser.parse(resourceConfigsJSON);
        if (swagger.getVendorExtensions() != null) {
            String basePath = swagger.getBasePath();
            String nameSpace = getNamespaceFromBasePath(basePath);
            if (nameSpace == null) {
                return new HashMap<>();
            }
            String securityHeaderScopes = null;
            //read security header from deployment.yaml
            if (localConfigMap.containsKey(nameSpace)) {
                if (localConfigMap.get(nameSpace).containsKey(APIMgtConstants.SWAGGER_X_WSO2_SCOPES)) {
                    securityHeaderScopes = localConfigMap.get(nameSpace).get(APIMgtConstants.SWAGGER_X_WSO2_SCOPES)
                            .toString();
                }
            } else {
                // rest api resource to scope mapping configurations have not been loaded.hence, populating
                populateConfigMapForScopes(swagger, nameSpace);
            }
            if (securityHeaderScopes == null || StringUtils.isEmpty(securityHeaderScopes)) {
                //security header is not found in deployment.yaml.hence, reading from swagger
                securityHeaderScopes = swagger.getVendorExtensions().get(APIMgtConstants.SWAGGER_X_WSO2_SECURITY)
                        .toString();
                localConfigMap.get(nameSpace).put(APIMgtConstants.SWAGGER_X_WSO2_SCOPES, securityHeaderScopes);
            }
            try {
                JSONObject scopesJson = (JSONObject) new JSONParser().parse(securityHeaderScopes);
                return extractScopesFromJson(scopesJson);
            } catch (ParseException e) {
                String msg = "invalid json : " + securityHeaderScopes;
                log.error(msg, e);
                throw new APIManagementException(msg, ExceptionCodes.SWAGGER_PARSE_EXCEPTION);
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("vendor extensions are not found in provided swagger json. resourceConfigsJSON = "
                        + resourceConfigsJSON);
            }
        }
        return new HashMap<>();
    }

    /*
    *  This method extracts scopes from scoped json defined
    *
    *  @param  JSONObject scopes as a json object
    *  @return Map<String, Scope> map of scopes
    *
    * */
    private Map<String, Scope> extractScopesFromJson(JSONObject scopesJson) {
        Map<String, Scope> scopeMap = new HashMap<>();
        if (scopesJson != null) {
            Iterator<?> scopesIterator = ((JSONArray) ((JSONObject) scopesJson
                    .get(APIMgtConstants.SWAGGER_OBJECT_NAME_APIM)).get(APIMgtConstants.SWAGGER_X_WSO2_SCOPES))
                            .iterator();
            while (scopesIterator.hasNext()) {
                Scope scope = new Gson().fromJson(((JSONObject) scopesIterator.next()).toJSONString(), Scope.class);
                scopeMap.put(scope.getKey(), scope);
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Unable to extract scopes from provided json as it is null.");
            }
        }
        return scopeMap;
    }

    /*
    * Method to get namespace based on the specified basepath
    *
    * @param String basepath defined in sswagger definition
    * @return String namespace value
    *
    * */
    private String getNamespaceFromBasePath(String basePath) {
        if (basePath.contains(APIMgtConstants.APPType.PUBLISHER)) {
            return APIMgtConstants.NAMESPACE_PUBLISHER_API;
        } else if (basePath.contains(APIMgtConstants.APPType.STORE)) {
            return APIMgtConstants.NAMESPACE_STORE_API;
        } else if (basePath.contains(APIMgtConstants.APPType.ADMIN)) {
            return APIMgtConstants.NAMESPACE_ADMIN_API;
        }
        return null;
    }

    /**
     * generate the swagger from uri templates.
     *
     * @param api API Object
     * @return Generated swagger resources as string.
     */
    @Override
    public String generateSwaggerFromResources(API.APIBuilder api) {
        Swagger swagger = new Swagger();
        Info info = new Info();
        info.setTitle(api.getName());
        info.setDescription(api.getDescription());
        Contact contact = new Contact();
        if (api.getBusinessInformation() != null) {
            BusinessInformation businessInformation = api.getBusinessInformation();
            contact.setName(businessInformation.getBusinessOwner());
            contact.setEmail(businessInformation.getBusinessOwnerEmail());
        }
        info.setContact(contact);
        info.setVersion(api.getVersion());
        swagger.setInfo(info);
        Map<String, Path> stringPathMap = new HashMap();
        for (UriTemplate uriTemplate : api.getUriTemplates().values()) {
            String uriTemplateString = uriTemplate.getUriTemplate();
            List<Parameter> parameterList = getParameters(uriTemplateString);

            if (uriTemplate.getParameters() == null || uriTemplate.getParameters().isEmpty()) {
                if (!HttpMethod.GET.toString().equalsIgnoreCase(uriTemplate.getHttpVerb())
                        && !HttpMethod.DELETE.toString().equalsIgnoreCase(uriTemplate.getHttpVerb())
                        && !HttpMethod.OPTIONS.toString().equalsIgnoreCase(uriTemplate.getHttpVerb())
                        && !HttpMethod.HEAD.toString().equalsIgnoreCase(uriTemplate.getHttpVerb())) {
                    parameterList.add(getDefaultBodyParameter());
                }
            } else {
                for (URITemplateParam uriTemplateParam : uriTemplate.getParameters()) {
                    Parameter parameter = getParameterFromURITemplateParam(uriTemplateParam);
                    parameterList.add(parameter);
                }
            }

            Operation operation = new Operation();
            operation.setParameters(parameterList);
            operation.setOperationId(uriTemplate.getTemplateId());
            //having content types like */* can break swagger definition 
            if (!StringUtils.isEmpty(uriTemplate.getContentType()) && !uriTemplate.getContentType().contains("*")) {
                List<String> consumesList = new ArrayList<>();
                consumesList.add(uriTemplate.getContentType());
                operation.setConsumes(consumesList);
            }
            operation.addResponse("200", getDefaultResponse());
            if (stringPathMap.containsKey(uriTemplateString)) {
                Path path = stringPathMap.get(uriTemplateString);
                path.set(uriTemplate.getHttpVerb().toLowerCase(), operation);
            } else {
                Path path = new Path();
                path.set(uriTemplate.getHttpVerb().toLowerCase(), operation);
                stringPathMap.put(uriTemplateString, path);
            }
        }
        swagger.setPaths(stringPathMap);
        swagger.setPaths(stringPathMap);
        return Json.pretty(swagger);
    }

    @Override
    public String generateSwaggerFromResources(CompositeAPI.Builder api) {
        Swagger swagger = new Swagger();
        Info info = new Info();
        info.setTitle(api.getName());
        info.setDescription(api.getDescription());

        info.setVersion(api.getVersion());
        swagger.setInfo(info);
        Map<String, Path> stringPathMap = new HashMap();
        for (UriTemplate uriTemplate : api.getUriTemplates().values()) {
            String uriTemplateString = uriTemplate.getUriTemplate();
            List<Parameter> parameterList = getParameters(uriTemplateString);
            if (!HttpMethod.GET.toString().equalsIgnoreCase(uriTemplate.getHttpVerb())
                    && !HttpMethod.DELETE.toString().equalsIgnoreCase(uriTemplate.getHttpVerb())
                    && !HttpMethod.OPTIONS.toString().equalsIgnoreCase(uriTemplate.getHttpVerb())
                    && !HttpMethod.HEAD.toString().equalsIgnoreCase(uriTemplate.getHttpVerb())) {
                parameterList.add(getDefaultBodyParameter());
            }
            Operation operation = new Operation();
            operation.setParameters(parameterList);
            operation.setOperationId(uriTemplate.getTemplateId());
            operation.addResponse("200", getDefaultResponse());
            if (stringPathMap.containsKey(uriTemplateString)) {
                Path path = stringPathMap.get(uriTemplateString);
                path.set(uriTemplate.getHttpVerb().toLowerCase(), operation);
            } else {
                Path path = new Path();
                path.set(uriTemplate.getHttpVerb().toLowerCase(), operation);
                stringPathMap.put(uriTemplateString, path);
            }
        }
        swagger.setPaths(stringPathMap);
        swagger.setPaths(stringPathMap);
        return Json.pretty(swagger);
    }

    /**
     * return API Object
     *
     * @param provider      Provider of the API.
     * @param apiDefinition API definition as string
     * @return API object.
     * @throws APIManagementException If failed to generate API from swagger.
     */
    @Override
    public API.APIBuilder generateApiFromSwaggerResource(String provider, String apiDefinition)
            throws APIManagementException {
        SwaggerParser swaggerParser = new SwaggerParser();
        Swagger swagger = swaggerParser.parse(apiDefinition);

        if (swagger == null) {
            throw new APIManagementException("Swagger could not be generated from provided API definition");
        }

        Info apiInfo = swagger.getInfo();
        if (apiInfo == null) {
            throw new APIManagementException("Swagger doesn't contains the info");
        } else {
            String apiName = apiInfo.getTitle();
            String apiVersion = apiInfo.getVersion();
            String apiDescription = apiInfo.getDescription();
            Contact contact = apiInfo.getContact();
            BusinessInformation businessInformation = new BusinessInformation();
            if (contact != null) {
                businessInformation.setBusinessOwner(contact.getName());
                businessInformation.setBusinessOwnerEmail(contact.getEmail());
            }
            API.APIBuilder apiBuilder = new API.APIBuilder(provider, apiName, apiVersion);
            apiBuilder.businessInformation(businessInformation);
            apiBuilder.description(apiDescription);
            apiBuilder.context(swagger.getBasePath());
            List<APIResource> apiResourceList = parseSwaggerAPIResources(new StringBuilder(apiDefinition));
            Map<String, UriTemplate> uriTemplateMap = new HashMap();
            for (APIResource apiResource : apiResourceList) {
                uriTemplateMap.put(apiResource.getUriTemplate().getTemplateId(), apiResource.getUriTemplate());
            }
            apiBuilder.uriTemplates(uriTemplateMap);
            apiBuilder.id(UUID.randomUUID().toString());
            return apiBuilder;
        }
    }

    @Override
    public CompositeAPI.Builder generateCompositeApiFromSwaggerResource(String provider, String apiDefinition)
            throws APIManagementException {
        SwaggerParser swaggerParser = new SwaggerParser();
        Swagger swagger = swaggerParser.parse(apiDefinition);

        if (swagger == null) {
            throw new APIManagementException("Swagger could not be generated from provided API definition");
        }

        Info apiInfo = swagger.getInfo();
        if (apiInfo == null) {
            throw new APIManagementException("Provided Swagger definition doesn't contain API information");
        } else {
            String apiName = apiInfo.getTitle();
            String apiVersion = apiInfo.getVersion();
            String apiDescription = apiInfo.getDescription();
            CompositeAPI.Builder apiBuilder = new CompositeAPI.Builder().provider(provider).name(apiName)
                    .version(apiVersion).description(apiDescription).context(swagger.getBasePath());

            List<APIResource> apiResourceList = parseSwaggerAPIResources(new StringBuilder(apiDefinition));
            Map<String, UriTemplate> uriTemplateMap = new HashMap();
            for (APIResource apiResource : apiResourceList) {
                uriTemplateMap.put(apiResource.getUriTemplate().getTemplateId(), apiResource.getUriTemplate());
            }
            apiBuilder.uriTemplates(uriTemplateMap);
            apiBuilder.id(UUID.randomUUID().toString());
            return apiBuilder;
        }
    }

    public static List<Parameter> getParameters(String uriTemplate) {
        List<Parameter> parameters = new ArrayList<>();
        StringTokenizer stringTokenizer = new StringTokenizer(uriTemplate, "/");
        while (stringTokenizer.hasMoreElements()) {
            String part1 = stringTokenizer.nextToken();
            if (part1.contains("{")) {
                String pathParam = part1.replace("{", "").replace("}", "");
                PathParameter parameter = new PathParameter();
                parameter.setName(pathParam);
                parameter.setType("string");
                parameters.add(parameter);
            }
        }
        return parameters;
    }

    private Response getDefaultResponse() {
        Response response = new Response();
        response.setDescription("OK");
        return response;
    }

    private BodyParameter getDefaultBodyParameter() {
        BodyParameter bodyParameter = new BodyParameter();
        bodyParameter.setName("Payload");
        bodyParameter.setDescription("Request Body");
        bodyParameter.setRequired(false);
        Model model = new ModelImpl();
        Map<String, Property> properties = new HashMap<>();
        Property property = new StringProperty();
        properties.put("payload", property);
        model.setProperties(properties);
        bodyParameter.setSchema(model);
        return bodyParameter;
    }

    private Parameter getParameterFromURITemplateParam(URITemplateParam uriTemplateParam) {
        switch (uriTemplateParam.getParamType()) {
        case BODY:
            return getDefaultBodyParameter();
        case PATH:
            PathParameter pathParameter = new PathParameter();
            pathParameter.setName(uriTemplateParam.getName());
            pathParameter.setType(uriTemplateParam.getDataType());
            return pathParameter;
        case QUERY:
            QueryParameter queryParameter = new QueryParameter();
            queryParameter.setName(uriTemplateParam.getName());
            queryParameter.setType(uriTemplateParam.getDataType());
            return queryParameter;
        case FORM_DATA:
            FormParameter formParameter = new FormParameter();
            formParameter.setName(uriTemplateParam.getName());
            formParameter.setType(uriTemplateParam.getDataType());
            return formParameter;
        default:
            return null;
        }
    }
}