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

org.moskito.control.plugins.mattermost.api.BaseRequest Maven / Gradle / Ivy

The newest version!
package org.moskito.control.plugins.mattermost.api;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.DefaultHttpClient;
import org.moskito.control.plugins.mattermost.api.annotations.FieldName;
import org.moskito.control.plugins.mattermost.api.annotations.IgnoreField;
import org.moskito.control.plugins.mattermost.api.exceptions.MattermostAPIException;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * Basic class for requests to Mattermost API.
 *
 * Generic parameter of this class (and class parameter in constructor)
 * determinate response object class corresponding to request.
 *
 * Class and it`s child uses Apache http components library and Jackson library.
 * To make http requests and encode or decode objects with request and response data to or from json.
 *
 * Request invoked by calling {@link BaseRequest#makeRequest(String)}
 * method with string url template parameter:
 *  First {@link BaseRequest#buildUrl(String)} method are called.
 *  This method invokes building map from request object fields. (see {@link BaseRequest#convertObjectToMap(Object)})
 *  Keys names formed from field names changing camelCase name style to underscore (as Mattermost API require)
 *  Some fields may be ignored or renamed using annotations ({@link IgnoreField}, {@link FieldName})
 *  Then url template filled from object map.
 *  Second abstract method {@link BaseRequest#getHttpRequestObject(String)} is called
 *  witch returns org.apache.http.client.methods.HttpRequestBase class object from Apache library.
 *  In post requests this method also parses object map to json and uses it to fill request body.
 *  This object is used to make a request.
 *  Then response object from generic parameter is returned
 *  by calling {@link BaseRequest#createResponseObject(HttpResponse)} method.
 *  Method populates response object from response json.
 *
 * Due class contains self-parsing mechanism and all necessary for making request
 * in most situations only fields (and proper setters and getters)
 * are declared in child final classes.
 *
 * @param  response class
 */
public abstract class BaseRequest  {

    /**
     * Class of response object, must be same as generic parameter
     * Required for calling constructor.
     */
    private Class clazz;
    /**
     * Main API wrapper class
     * Token for request taken from here
     */
    private MattermostApi api;
    /**
     * Parsed request fields.
     * Used for forming url and json request body
     * See class description for more details
     */
    private Map objectMap;

    /**
     * Wrapper method for getting request object map.
     * Creates map on first call.
     * Used for forming url and json request body
     * @return map with parsed request object fields
     */
    protected Map getObjectMap(){

        if(objectMap == null)
            objectMap = convertObjectToMap(this);

        return objectMap;

    }

    /**
     * Returns MattermostAPI object.
     * Used in child classes
     * @return MattermostAPI class instance
     */
    protected MattermostApi getApi() {
        return api;
    }

    /**
     * First parameter defines class of response object
     * must be same as in generic template.
     * Second MattermostAPI class instance required for authorized calls to API
     *
     * @param clazz class of response object
     * @param api MattermostAPI instance
     */
    BaseRequest(Class clazz, MattermostApi api)
    {
        this.clazz = clazz;
        this.api = api;
    }

    /**
     * Creates response object from http response.
     * Returns new object of class specified in generic parameter and constructor
     * populated from response json.
     * @param response http response of Mattermost API
     *
     * @return populated from response json response object
     * @throws InstantiationException should not be thrown, if there is no bugs in API wrapper module
     * @throws IllegalAccessException should not be thrown, if there is no bugs in API wrapper module
     * @throws IOException thrown by Apache http library or Jackson
     * @throws com.fasterxml.jackson.core.JsonProcessingException thrown by Jackson
     */
    /* test scope */ T createResponseObject(HttpResponse response)
            throws InstantiationException, IllegalAccessException, IOException {
        T responseObject = clazz.newInstance();
        responseObject.populateResponse(response);
        return responseObject;
    }

    /**
     * Used to convert camelCase fields names to underscore as Mattermost API required
     * @param camelCaseString string to convert
     * @return underscore style string
     */
    private String camelCaseToUnderscore(String camelCaseString){
        return camelCaseString.replaceAll("([A-Z]+)","\\_$1").toLowerCase();
    }

    /**
     * Parses object fields to map.
     * Uses field names as keys and field values as values.
     * Field names are converted from camelCase to underscore.
     * Field must be public or contain
     * public getter with field name (private String teamName; => public String getTeamName(){...})
     * Also field can be ignored or renamed using annotations (see class description for more details)
     * @param obj object to parse map from
     * @return map with parsed object fields
     */
    /* Test Scope */ Map convertObjectToMap(Object obj) {

        Map fieldsMap = new HashMap<>();

        Class clazz = obj.getClass();

        for (Field field : clazz.getDeclaredFields()){

            // Skipping field with ignore annotation
            if(field.getAnnotation(IgnoreField.class) == null){

                // Name, that be written to map
                String fieldName;
                // Real field name. Needed for finding getter
                String realFieldName = field.getName();
                // Field value
                Object fieldValue;

                // Getting field name from annotation, if it present
                if(field.getAnnotation(FieldName.class) != null){
                    fieldName = field.getAnnotation(FieldName.class).name();
                }
                // Getting field name by converting it camelCase name to underscore (means field name annotation not present)
                else
                    fieldName = camelCaseToUnderscore(realFieldName);

                // If field is public, value taken directly from it
                if((field.getModifiers() & Modifier.PUBLIC) != 0){
                    try {
                        fieldValue = field.get(obj);
                    } catch (IllegalAccessException ignored) {continue;} // Field access is checked
                }
                // Else trying to find and invoke getter
                else {

                    try {
                        Method getter = clazz.getMethod("get" + StringUtils.capitalize(realFieldName));
                        fieldValue = getter.invoke(obj);
                    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                        continue; // Filed to find or invoke getter. Slipping this field
                    }

                }

                // Checking is field value not null
                if(fieldValue != null && !fieldValue.equals(0L))
                    // Field name and value are successfully found.
                    // Putting it to map
                    fieldsMap.put(fieldName, fieldValue);

            }

        }

        return fieldsMap;

    }

    /**
     * Adds "$" sign to beginning of string.
     * Such format names are used in url templates
     * @param fieldName name of field to convert into template variable
     * @return template variable string
     */
    private String convertFieldNameToTemplate(String fieldName){
        return "$" + fieldName;
    }

    /**
     * Resolves url template filling it variables from object map.
     * Template variables starts with "$" sign and ends with request class field name,
     * from where value is taken.
     * Example:
     *  "teams/$team_id/channels/name/$channel_name" => "teams/5da4a5ad54544554ad/channels/name/moskito-alerts"
     * @param urlTemplate template of url
     * @return builded url ready to make request
     */
    /* Test Scope */ String buildUrl(String urlTemplate){

        Map requestFields = getObjectMap();
        String url = urlTemplate;

        for (Map.Entry field : requestFields.entrySet()){

            try {
                url = StringUtils.replace(
                        url,
                        convertFieldNameToTemplate(field.getKey()),
                        // Null check prevents null pointer exception in case object field not initialized
                        URLEncoder.encode(field.getValue() != null ? field.getValue().toString() : "", "UTF-8")
                );
            } catch (UnsupportedEncodingException ignored) {} // UTF-8 encoding should be supported

        }

        return url;

    }

    /**
     * Triggers making request to Mattermost API.
     * Builds url and body (in case of post requests) of request and
     * makes request to Mattermost API.
     *
     * @param url Mattermost API method url template
     * @return response object corresponding to request
     */
     public T makeRequest(String url) throws IOException, MattermostAPIException, ReflectiveOperationException {

        HttpClient client = new DefaultHttpClient();

        HttpRequestBase httpRequest = getHttpRequestObject(buildUrl(url));

        if(api.isAuthorized())
            httpRequest.setHeader("Authorization", "Bearer " + api.getToken());

        HttpResponse httpResponse = client.execute(httpRequest);

        if(httpResponse.getStatusLine().getStatusCode() != 200)
            // Means there is an errors in API request data (Non API wrapper bug)
            throw MattermostAPIException.parseException(httpResponse);

        return createResponseObject(httpResponse);

    }

    protected abstract HttpRequestBase getHttpRequestObject(String url);

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy