All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.phoenixnap.oss.ramlapisync.generation.rules.spring.SpringRestClientMethodBodyRule Maven / Gradle / Ivy

Go to download

Components including the parsing of RAML documents and Spring MVC Annotations to create RAML models

The newest version!
/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed 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 com.phoenixnap.oss.ramlapisync.generation.rules.spring;

import static com.phoenixnap.oss.ramlapisync.generation.CodeModelHelper.findFirstClassBySimpleName;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import com.google.common.base.CaseFormat;
import com.phoenixnap.oss.ramlapisync.data.ApiActionMetadata;
import com.phoenixnap.oss.ramlapisync.data.ApiBodyMetadata;
import com.phoenixnap.oss.ramlapisync.data.ApiParameterMetadata;
import com.phoenixnap.oss.ramlapisync.generation.CodeModelHelper;
import com.phoenixnap.oss.ramlapisync.generation.rules.Rule;
import com.phoenixnap.oss.ramlapisync.naming.NamingHelper;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;

/**
 * Generates a method body that calls the rest template to execute a REST call.
 *
 * INPUT:
 * #%RAML 0.8
 * title: myapi
 * mediaType: application/json
 * baseUri: /
 * /base:
 *   /{id}
 *     get:
 *
 * OUTPUT:
 * HttpEntity httpEntity = new HttpEntity();
 * return this.restTemplate.exchange(/, GET, httpEntity,responseBody);
 *
 * The name of the field can be configured. Default is "restTemplate".
 *
 * @author Kurt Paris
 * @author Kris Galea
 * @since 0.5.0
 */
public class SpringRestClientMethodBodyRule implements Rule {

    private String restTemplateFieldName = "restTemplate";

    private String baseUrlFieldName = "baseUrl";

    public SpringRestClientMethodBodyRule(String restTemplateFieldName, String baseUrlFieldName) {
        if(StringUtils.hasText(restTemplateFieldName)) {
            this.restTemplateFieldName = restTemplateFieldName;
        }
        if (!StringUtils.isEmpty(baseUrlFieldName)){
            this.baseUrlFieldName = baseUrlFieldName;
        }
    }

    @Override
    public JMethod apply(ApiActionMetadata endpointMetadata, CodeModelHelper.JExtMethod generatableType) {
        JBlock body = generatableType.get().body();
        JCodeModel owner = generatableType.owner();
        //build HttpHeaders
        JClass httpHeadersClass = owner.ref(HttpHeaders.class);
        JExpression headersInit = JExpr._new(httpHeadersClass);
        JVar httpHeaders = null;
        if (endpointMetadata.getInjectHttpHeadersParameter()) {
            for (JVar var : generatableType.get().params()) {
                if (var.name().equals("httpHeaders")) {
                    httpHeaders = var;
                    break;
                }
            }
        } else {
            httpHeaders = body.decl(httpHeadersClass, "httpHeaders", headersInit);
        }


        //Declare Arraylist to contain the acceptable Media Types
        body.directStatement("//  Add Accepts Headers and Body Content-Type");
        JClass mediaTypeClass = owner.ref(MediaType.class);
        JClass refArrayListClass = owner.ref(ArrayList.class).narrow(mediaTypeClass);
        JVar acceptsListVar = body.decl(refArrayListClass, "acceptsList", JExpr._new(refArrayListClass));

        //If we have a request body, lets set the content type of our request
        if (endpointMetadata.getRequestBody() != null) {
        	body.invoke(httpHeaders, "setContentType").arg(mediaTypeClass.staticInvoke("valueOf").arg(endpointMetadata.getRequestBodyMime()));
        }

        //If we have response bodies defined, we need to add them to our accepts headers list
        //TODO possibly restrict
        String documentDefaultType = endpointMetadata.getParent().getDocument().getMediaType();
        //If a global mediatype is defined add it
        if (StringUtils.hasText(documentDefaultType)){
        	body.invoke(acceptsListVar, "add").arg(mediaTypeClass.staticInvoke("valueOf").arg(documentDefaultType));
        } else { //default to application/json just in case
        	body.invoke(acceptsListVar, "add").arg(mediaTypeClass.staticInvoke("valueOf").arg("application/json"));
        }

        //Iterate over Response Bodies and add each distinct mime type to accepts headers
        if (endpointMetadata.getResponseBody() != null && !endpointMetadata.getResponseBody().isEmpty()) {
        	for (String responseMime : endpointMetadata.getResponseBody().keySet()) {
        		if (!responseMime.equals(documentDefaultType) && !responseMime.equals("application/json")) {
        			body.invoke(acceptsListVar, "add").arg(mediaTypeClass.staticInvoke("valueOf").arg(responseMime));
        		}
        	}
        }

        //Set accepts list as our accepts headers for the call
        body.invoke(httpHeaders, "setAccept").arg(acceptsListVar);


        //Get the parameters from the model and put them in a map for easy lookup
        List params = generatableType.get().params();
        Map methodParamMap = new LinkedHashMap<>();
        for (JVar param : params) {
        	methodParamMap.put(param.name(), param);
        }

        // Add headers
        for (ApiParameterMetadata parameter : endpointMetadata.getRequestHeaders()) {
            JVar param = methodParamMap.get(NamingHelper.getParameterName(parameter.getName()));
            String javaParamName = NamingHelper.getParameterName(parameter.getName());
            body._if(methodParamMap.get(javaParamName).ne(JExpr._null()))._then().block()
                    .invoke(httpHeaders, "add").arg(parameter.getName()).arg(JExpr.invoke(param, "toString"));
        }

        //Build the Http Entity object
        JClass httpEntityClass = owner.ref(HttpEntity.class);
        JInvocation init = JExpr._new(httpEntityClass);

        if (endpointMetadata.getRequestBody() != null) {
	       init.arg(methodParamMap.get(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, endpointMetadata.getRequestBody().getName())));
        }
        init.arg(httpHeaders);

        //Build the URL variable
        JExpression urlRef = JExpr.ref(baseUrlFieldName);
        JType urlClass = owner._ref(String.class);
        JExpression targetUrl = urlRef.invoke("concat").arg(endpointMetadata.getResource().getUri());
        JVar url = body.decl(urlClass, "url", targetUrl);
        JVar uriBuilderVar = null;
        JVar uriComponentVar = null;



        //Initialise the UriComponentsBuilder
    	JClass builderClass = owner.ref(UriComponentsBuilder.class);
    	JExpression builderInit = builderClass.staticInvoke("fromHttpUrl").arg(url);

		// Add these to the code model
		uriBuilderVar = body.decl(builderClass, "builder", builderInit);

		// If we have any Query Parameters, we will use the URIBuilder to encode
		// them in the URL
		if (!CollectionUtils.isEmpty(endpointMetadata.getRequestParameters())) {
			// iterate over the parameters and add calls to .queryParam
			for (ApiParameterMetadata parameter : endpointMetadata.getRequestParameters()) {
				// we need to check if param is not null in order to prevent
				// client for sending that param (that will be read as empty
				// string instead of null)
				body._if(methodParamMap.get(NamingHelper.getParameterName(parameter.getName())).ne(JExpr._null()))._then().block()
						.invoke(uriBuilderVar, "queryParam").arg(parameter.getName())
						.arg(methodParamMap.get(NamingHelper.getParameterName(parameter.getName())));
			}
		}

        JClass componentClass = owner.ref(UriComponents.class);
    	JExpression component = uriBuilderVar.invoke("build");
    	uriComponentVar = body.decl(componentClass, "uriComponents", component);

        //build request entity holder
        JVar httpEntityVar = body.decl(httpEntityClass, "httpEntity", init);

        //construct the HTTP Method enum
        JClass httpMethod = null;
        try {
            httpMethod = (JClass)owner._ref(HttpMethod.class);
        }
        catch (ClassCastException e) {

        }

        //get all uri params from metadata set and add them to the param map in code
        if (!CollectionUtils.isEmpty(endpointMetadata.getPathVariables())) {
            //Create Map with Uri Path Variables
            JClass uriParamMap = owner.ref(Map.class).narrow(String.class, Object.class);
            JExpression uriParamMapInit = JExpr._new(owner.ref(HashMap.class));
            JVar uriParamMapVar = body.decl(uriParamMap, "uriParamMap", uriParamMapInit);

        	endpointMetadata.getPathVariables().forEach(p -> body.invoke(uriParamMapVar, "put").arg(p.getName()).arg(methodParamMap.get(p.getName())));
        	JInvocation expandInvocation = uriComponentVar.invoke("expand").arg(uriParamMapVar);

        	body.assign(uriComponentVar, expandInvocation);
        }

        //Determining response entity type
        JClass returnType = null;
        if (!endpointMetadata.getResponseBody().isEmpty()) {
            ApiBodyMetadata apiBodyMetadata = endpointMetadata.getResponseBody().values().iterator().next();
            JClass genericType = findFirstClassBySimpleName(apiBodyMetadata.getCodeModel(), apiBodyMetadata.getName());
            if (apiBodyMetadata.isArray()) {
                JClass arrayType = owner.ref(List.class);
                returnType = arrayType.narrow(genericType);
            } else {
                returnType = genericType;
            }
        } else {
            returnType = owner.ref(Object.class);
        }

        JExpression returnExpression = JExpr.dotclass(returnType);//assume not parameterized by default
        //check if return is parameterized
        if (!CollectionUtils.isEmpty(returnType.getTypeParameters())) {
            //if yes - build the parameterized type reference and change returnExpression
            //Due to issue 61, it is generated as
            //class _P extends org.springframework.core.ParameterizedTypeReference>
            //ParameterizedTypeReference> typeRef = new _P();
            //Create Map with Uri Path Variables
            JClass paramTypeRefClass = owner.ref(ParameterizedTypeReference.class);
            paramTypeRefClass = paramTypeRefClass.narrow(returnType);

            body.directStatement("class _P extends " + paramTypeRefClass.fullName() + "{};");

            JExpression paramTypeRefInit = JExpr._new(owner.directClass("_P"));
            returnExpression = body.decl(paramTypeRefClass, "typeReference", paramTypeRefInit);
        }

        //build rest template exchange invocation
        JInvocation jInvocation = JExpr._this().ref(restTemplateFieldName).invoke("exchange");

        jInvocation.arg(uriComponentVar.invoke("encode").invoke("toUri"));
        jInvocation.arg(httpMethod.staticRef(endpointMetadata.getActionType().name()));
        jInvocation.arg(httpEntityVar);
        jInvocation.arg(returnExpression);

        body._return(jInvocation);

        return generatableType.get();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy