org.mule.modules.quickbooks.windows.api.DefaultQuickBooksWindowsClient.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.modules.quickbooks.windows.api.DefaultQuickBooksWindowsClient.java

Source

/**
 * Mule QuickBooks Connector
 *
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 *
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */

package org.mule.modules.quickbooks.windows.api;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.bind.JAXBException;

import net.sf.staccatocommons.collections.stream.Streams;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.mule.modules.quickbooks.api.AbstractQuickBooksClientOAuth;
import org.mule.modules.quickbooks.api.exception.ErrorInfo;
import org.mule.modules.quickbooks.api.exception.ExceptionInfo;
import org.mule.modules.quickbooks.api.exception.QuickBooksRuntimeException;
import org.mule.modules.quickbooks.api.model.*;
import org.mule.modules.quickbooks.api.oauth.OAuthCredentials;
import org.mule.modules.quickbooks.utils.MessageUtils;
import org.mule.modules.quickbooks.windows.WindowsEntityType;
import org.mule.modules.quickbooks.windows.objectfactory.QBWMessageUtils;
import org.mule.modules.quickbooks.windows.schema.*;
import org.mule.modules.quickbooks.windows.schema.PlatformResponse;
import org.mule.modules.quickbooks.windows.schema.UserResponse;
import org.mule.modules.quickbooks.windows.schema.UserInformation;
import org.mule.modules.utils.MuleSoftException;
import org.mule.modules.utils.pagination.PaginatedIterable;

public class DefaultQuickBooksWindowsClient extends AbstractQuickBooksClientOAuth
        implements QuickBooksWindowsClient {

    public DefaultQuickBooksWindowsClient(final String baseUri, final String consumerKey,
            final String consumerSecret, final String appKey) {
        Validate.notEmpty(baseUri);

        init(baseUri, consumerKey, consumerSecret, appKey);
    }

    @Override
    public Object create(final OAuthCredentials credentials, WindowsEntityType type, Object obj, String requestId,
            Boolean draft, Boolean fullResponse) {
        Validate.notNull(obj);

        String str = String.format("%s/%s/v2/%s", credentials.getBaseUri(), type.getResouceName(),
                credentials.getRealmId());

        HttpUriRequest httpRequest = new HttpPost(str);
        httpRequest.addHeader("Content-Type", "text/xml");

        AddRequest addRequest = new AddRequest();
        addRequest.setRequestId(requestId);
        addRequest.setCdmObject(getMessageUtilsInstance().createJaxbElement(obj));
        addRequest.setDraft(draft);
        addRequest.setFullResponse(fullResponse);

        prepareToPost(addRequest, httpRequest);

        try {
            Object respObj = makeARequestToQuickbooks(httpRequest, credentials, false);
            if (respObj instanceof ErrorResponse) {
                throw new QuickBooksRuntimeException(new ErrorInfo(respObj));
            }
            if (fullResponse != null && fullResponse) {
                return ((SuccessResponse) respObj).getCdmObject().getValue();
            }
            return ((SuccessResponse) respObj).getCdmObjectRef().getValue();
        } catch (QuickBooksRuntimeException e) {
            if (e.isAExpiredTokenFault()) {
                destroyAccessToken(credentials);
                return create(credentials, type, obj, requestId, draft, fullResponse);
            } else {
                throw e;
            }
        }
    }

    @Override
    public Object getObject(final OAuthCredentials credentials, WindowsEntityType type, IdType id) {
        Validate.notNull(type);
        Validate.notNull(id);

        String str = String.format("%s/%s/v2/%s/%s?idDomain=%s", credentials.getBaseUri(), type.getResouceName(),
                credentials.getRealmId(), id.getValue(), id.getIdDomain().value());

        HttpUriRequest httpRequest = new HttpGet(str);

        try {
            Object respObj = makeARequestToQuickbooks(httpRequest, credentials, false);
            if (respObj instanceof ErrorResponse) {
                throw new QuickBooksRuntimeException(new ErrorInfo(respObj));
            }
            return Streams.from(getListFromIntuitResponse(respObj, type)).anyOrNull();
        } catch (QuickBooksRuntimeException e) {
            if (e.isAExpiredTokenFault()) {
                destroyAccessToken(credentials);
                return getObject(credentials, type, id);
            } else {
                throw e;
            }
        }
    }

    private List getListFromIntuitResponse(Object response, WindowsEntityType type) {
        try {
            if (type.getType().getSimpleName().equals("Class")) {
                return (List) response.getClass().getMethod("getClazz").invoke(response);
            }

            return (List) response.getClass().getMethod("get" + type.getType().getSimpleName()).invoke(response);
        } catch (Exception e) {
            throw MuleSoftException.soften(e);
        }
    }

    @Override
    public Object update(final OAuthCredentials credentials, WindowsEntityType type, Object obj, String requestId,
            Boolean draft, Boolean fullResponse) {
        Validate.notNull(obj);

        if (((CdmBase) obj).getSyncToken() == null) {
            ((CdmBase) obj)
                    .setSyncToken(((CdmBase) getObject(credentials, type, ((CdmBase) obj).getId())).getSyncToken());
        }

        String str = String.format("%s/%s/v2/%s", credentials.getBaseUri(), type.getResouceName(),
                credentials.getRealmId());

        HttpUriRequest httpRequest = new HttpPost(str);
        httpRequest.addHeader("Content-Type", "text/xml");

        ModRequest modRequest = new ModRequest();
        modRequest.setRequestId(requestId);
        modRequest.setCdmObject(getMessageUtilsInstance().createJaxbElement(obj));
        modRequest.setDraft(draft);
        modRequest.setFullResponse(fullResponse);

        prepareToPost(modRequest, httpRequest);

        try {
            Object respObj = makeARequestToQuickbooks(httpRequest, credentials, false);
            if (respObj instanceof ErrorResponse) {
                throw new QuickBooksRuntimeException(new ErrorInfo(respObj));
            }
            if (fullResponse != null && fullResponse) {
                return ((SuccessResponse) respObj).getCdmObject().getValue();
            }
            return ((SuccessResponse) respObj).getCdmObjectRef().getValue();
        } catch (QuickBooksRuntimeException e) {
            if (e.isAExpiredTokenFault()) {
                destroyAccessToken(credentials);
                return update(credentials, type, obj, requestId, draft, fullResponse);
            } else {
                throw e;
            }
        }
    }

    @Override
    public void delete(final OAuthCredentials credentials, WindowsEntityType type, Object obj, String requestId) {
        Validate.notNull(type);
        Validate.notNull(obj);
        Validate.notNull(((CdmBase) obj).getId());
        Validate.isTrue(
                ((CdmBase) obj).getId().getValue() != null && !((CdmBase) obj).getId().getValue().equals(""));

        if (((CdmBase) obj).getSyncToken() == null || ((CdmBase) obj).getMetaData() == null) {
            obj = getObject(credentials, type, ((CdmBase) obj).getId());
        }

        String str = String.format("%s/%s/v2/%s", credentials.getBaseUri(), type.getResouceName(),
                credentials.getRealmId());

        HttpUriRequest httpRequest = new HttpPost(str);
        httpRequest.addHeader("Content-Type", "text/xml");

        DelRequest delRequest = new DelRequest();
        delRequest.setRequestId(requestId);
        delRequest.setCdmObject(getMessageUtilsInstance().createJaxbElement(obj));

        prepareToPost(delRequest, httpRequest);
        try {
            Object respObj = makeARequestToQuickbooks(httpRequest, credentials, false);
            if (respObj instanceof ErrorResponse) {
                throw new QuickBooksRuntimeException(new ErrorInfo(respObj));
            }
        } catch (QuickBooksRuntimeException e) {
            if (e.isAExpiredTokenFault()) {
                destroyAccessToken(credentials);
                delete(credentials, type, obj, requestId);
            } else {
                throw e;
            }
        }
    }

    /**
     * Returns the list of results pages from QB
     * 
     * @return List of pages with results
     */
    @Override
    public Iterable findObjectsGetPages(final OAuthCredentials credentials, final WindowsEntityType type,
            final Object query) {
        Validate.notNull(type);
        return new PaginatedIterable<Object, List<Object>>() {
            private Integer countPage = 1;

            @Override
            protected List<Object> firstPage() {
                return askAnEspecificPage(countPage);
            }

            @Override
            protected boolean hasNextPage(List<Object> arg0) {
                return arg0.isEmpty();
            }

            @Override
            protected List<Object> nextPage(List<Object> arg0) {
                countPage = countPage + 1;
                return askAnEspecificPage(countPage);
            }

            @Override
            protected Iterator<Object> pageIterator(List<Object> arg0) {
                return arg0.iterator();
            }

            private List<Object> askAnEspecificPage(Integer pageNumber) {
                String str = String.format("%s/%s/v2/%s", credentials.getBaseUri(), type.getResouceName(),
                        credentials.getRealmId());

                HttpUriRequest httpRequest = new HttpPost(str);
                httpRequest.addHeader("Content-Type", "text/xml");

                ((QueryBase) query).setStartPage(BigInteger.valueOf(pageNumber));
                ((QueryBase) query).setChunkSize(getResultsPerPage());

                prepareToPost(query, httpRequest);

                try {
                    Object respObj = makeARequestToQuickbooks(httpRequest, credentials, false);
                    if (respObj instanceof ErrorResponse) {
                        throw new QuickBooksRuntimeException(new ErrorInfo(respObj));
                    }
                    return getListFromIntuitResponse(respObj, type);
                } catch (QuickBooksRuntimeException e) {
                    if (e.isAExpiredTokenFault()) {
                        destroyAccessToken(credentials);
                        return askAnEspecificPage(pageNumber);
                    } else {
                        throw e;
                    }
                }
            }
        };
    }

    /**
     * Returns all the results from QB
     * 
     * @return List with all the results
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public Iterable findObjects(final OAuthCredentials credentials, final WindowsEntityType type, Integer startPage,
            Integer chunkSize, final Object query) {
        Validate.notNull(type);

        List<Object> listOfResults = new ArrayList<Object>();
        Boolean hasMoreResults = true;
        Boolean externalPagination = false;
        Integer pageNumber = 1;

        if (startPage != null && chunkSize != null) {
            setResultsPerPage(chunkSize);
            pageNumber = startPage;
            externalPagination = true;
        }

        HttpUriRequest httpRequest;
        Object responseObject;

        String str = String.format("%s/%s/v2/%s", credentials.getBaseUri(), type.getResouceName(),
                credentials.getRealmId());

        while (hasMoreResults) {
            httpRequest = new HttpPost(str);
            httpRequest.addHeader("Content-Type", "text/xml");

            ((QueryBase) query).setStartPage(BigInteger.valueOf(pageNumber));
            ((QueryBase) query).setChunkSize(getResultsPerPage());

            prepareToPost(query, httpRequest);

            try {
                responseObject = makeARequestToQuickbooks(httpRequest, credentials, false);
                if (responseObject instanceof ErrorResponse) {
                    throw new QuickBooksRuntimeException(new ErrorInfo(responseObject));
                }
            } catch (QuickBooksRuntimeException e) {
                if (e.isAExpiredTokenFault()) {
                    destroyAccessToken(credentials);
                    responseObject = makeARequestToQuickbooks(httpRequest, credentials, false);
                } else {
                    throw e;
                }
            }

            List intuitList = getListFromIntuitResponse(responseObject, type);

            if (intuitList != null) {
                listOfResults.addAll(intuitList);
                hasMoreResults = (intuitList.size() >= getResultsPerPage()) && !externalPagination;
                pageNumber++;
            } else {
                hasMoreResults = false;
            }
        }

        return listOfResults;
    }

    @Override
    public Object retrieveWithoutUsingQueryObjects(final OAuthCredentials credentials, Object syncStatusRequest,
            String objectName) {
        Validate.notNull(syncStatusRequest);

        String str = String.format("%s/%s/v2/%s", credentials.getBaseUri(), objectName, credentials.getRealmId());

        HttpUriRequest httpRequest = new HttpPost(str);
        httpRequest.addHeader("Content-Type", "text/xml");

        prepareToPost(syncStatusRequest, httpRequest);
        try {
            Object respObj = makeARequestToQuickbooks(httpRequest, credentials, false);
            if (respObj instanceof ErrorResponse) {
                throw new QuickBooksRuntimeException(new ErrorInfo(respObj));
            }
            return respObj;
        } catch (QuickBooksRuntimeException e) {
            if (e.isAExpiredTokenFault()) {
                destroyAccessToken(credentials);
                return retrieveWithoutUsingQueryObjects(credentials, syncStatusRequest, objectName);
            } else {
                throw e;
            }
        }
    }

    @Override
    public void revert(final OAuthCredentials credentials, WindowsEntityType type, Object obj, String requestId) {
        Validate.notNull(type);

        String str = String.format("%s/%s/v2/%s", credentials.getBaseUri(), type.getResouceName(),
                credentials.getRealmId());

        HttpUriRequest httpRequest = new HttpPost(str);
        httpRequest.addHeader("Content-Type", "text/xml");

        RevertRequest revertRequest = new RevertRequest();
        revertRequest.setRequestId(requestId);
        revertRequest.setCdmObject(getMessageUtilsInstance().createJaxbElement(obj));

        prepareToPost(revertRequest, httpRequest);

        try {
            Object respObj = makeARequestToQuickbooks(httpRequest, credentials, false);
            if (respObj instanceof ErrorResponse) {
                throw new QuickBooksRuntimeException(new ErrorInfo(respObj));
            }
        } catch (QuickBooksRuntimeException e) {
            if (e.isAExpiredTokenFault()) {
                destroyAccessToken(credentials);
                revert(credentials, type, obj, requestId);
            } else {
                throw e;
            }
        }
    }

    @Override
    public String generateARequestId() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    @Override
    protected ExceptionInfo getFaultInfo(String str) throws JAXBException {
        if (str.contains("oauth_problem=token_rejected")) {
            //This use the QBO FaultInfo because, in QBW it's not defined FaultInfo, 
            //but if the tokens expired of if they are wrong, they send us this object.
            ExceptionInfo exceptionInfo = new ExceptionInfo();
            exceptionInfo.setCause("SERVER");
            exceptionInfo.setErrorCode("401");
            exceptionInfo.setMessage("Unauthorized OAuth Token: token_rejected");
            return exceptionInfo;
        }
        return null;
    }

    @Override
    protected MessageUtils getMessageUtilsInstance() {
        return QBWMessageUtils.getInstance();
    }

    @Override
    public UserInformation getCurrentUserInformation(final OAuthCredentials credentials) {
        Object response = retrieveUserInformation(credentials);
        if (response instanceof UserResponse) {
            return ((UserResponse) response).getUser();
        } else if (response instanceof PlatformResponse) {
            PlatformResponse platformResponse = (PlatformResponse) response;
            throw new QuickBooksRuntimeException(String.format("Error Code: %s Error Message: %s",
                    platformResponse.getErrorCode(), platformResponse.getErrorMessage()));
        } else {
            throw new QuickBooksRuntimeException("It is not possible to parse the response from Intuit Platform");
        }
    }

    @Override
    public boolean disconnect(OAuthCredentials credentials) {
        org.mule.modules.quickbooks.windows.schema.PlatformResponse response = disconnectFromQB(credentials);
        if (response.getErrorCode() != 0)
            throw new QuickBooksRuntimeException(response.getErrorMessage());

        return true;
    }

    @Override
    public OAuthCredentials reconnect(OAuthCredentials credentials) {
        org.mule.modules.quickbooks.windows.schema.ReconnectResponse response = reconnectToQB(credentials);
        if (response.getErrorCode() != 0) {
            throw new QuickBooksRuntimeException(response.getErrorMessage());
        }

        credentials.setAccessToken(response.getOAuthToken());
        credentials.setAccessTokenSecret(response.getOAuthTokenSecret());
        return credentials;
    }

    /**
     * Parse the HTML information for BlueDotMenu
     * @param credentials OAuth credentials
     * @param regex Regex for extracting the information
     *              <p>The regex has to extract the information in this way:</p>
     *              <p>match[0]: appId,appName,contextArea</p>
     *              <p>match[1]: logoImageUrl</p>
     *              <p>The method will split the application information to generate the @link{AppMenuInformation} object</p>
     * @return List with connected apps information
     */
    @Override
    public BlueDotMenu getBlueDotInformation(OAuthCredentials credentials, String regex) {
        String blueDotInformation = (String) getBlueDotMenu(credentials);
        List<AppMenuInformation> menuInformationList = new ArrayList<AppMenuInformation>();
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(blueDotInformation);
        while (matcher.find()) {
            AppMenuInformation info = new AppMenuInformation();
            String[] parameters = StringUtils.split(matcher.group(1), ",");
            info.setAppId(StringUtils.trim(StringUtils.remove(parameters[0], "'")));
            info.setName(StringUtils.trim(StringUtils.remove(parameters[1], "'")));
            info.setContentArea(StringUtils.trim(StringUtils.remove(parameters[2], "'")));
            info.setImageUrl(StringUtils.trim(matcher.group(2)));

            menuInformationList.add(info);
        }

        return new BlueDotMenu(menuInformationList, blueDotInformation);
    }

    @Override
    public Object get(final OAuthCredentials credentials, WindowsEntityType type) {
        Validate.notNull(type);

        String str = String.format("%s/%s/v2/%s", credentials.getBaseUri(), type.getResouceName(),
                credentials.getRealmId());

        HttpUriRequest httpRequest = new HttpGet(str);

        try {
            Object respObj = makeARequestToQuickbooks(httpRequest, credentials, false);
            if (respObj instanceof ErrorResponse) {
                throw new QuickBooksRuntimeException(new ErrorInfo(respObj));
            }
            return Streams.from(getListFromIntuitResponse(respObj, type)).anyOrNull();
        } catch (QuickBooksRuntimeException e) {
            if (e.isAExpiredTokenFault()) {
                destroyAccessToken(credentials);
                return get(credentials, type);
            } else {
                throw e;
            }
        }
    }
}