org.mule.modules.zuora.ZuoraModule.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.modules.zuora.ZuoraModule.java

Source

/**
 * Mule Zuora Cloud 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.
 */

/**
 * This file was automatically generated by the Mule Development Kit
 */
package org.mule.modules.zuora;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;

import javax.xml.datatype.XMLGregorianCalendar;

import com.zuora.api.*;
import org.apache.commons.io.IOUtils;
import org.mule.api.ConnectionException;
import org.mule.api.ConnectionExceptionCode;
import org.mule.api.MuleContext;
import org.mule.api.MuleException;
import org.mule.api.annotations.*;
import org.mule.api.annotations.display.Password;
import org.mule.api.annotations.display.Placement;
import org.mule.api.annotations.licensing.RequiresEnterpriseLicense;
import org.mule.api.annotations.param.ConnectionKey;
import org.mule.api.annotations.param.Default;
import org.mule.api.annotations.param.MetaDataKeyParam;
import org.mule.api.annotations.param.Optional;
import org.mule.api.context.MuleContextAware;
import org.mule.common.metadata.*;
import org.mule.common.metadata.builder.DefaultMetaDataBuilder;
import org.mule.common.metadata.builder.PojoMetaDataBuilder;
import org.mule.common.metadata.datatype.DataType;
import org.mule.common.query.DefaultOperatorVisitor;
import org.mule.common.query.DsqlQueryVisitor;
import org.mule.common.query.DsqlQuery;
import org.mule.modules.zuora.zobject.ZObjectType;
import org.mule.modules.zuora.zuora.api.CxfZuoraClient;
import org.mule.modules.zuora.zuora.api.RestZuoraClient;
import org.mule.modules.zuora.zuora.api.RestZuoraClientImpl;
import org.mule.modules.zuora.zuora.api.SessionTimedOutException;
import org.mule.modules.zuora.zuora.api.ZuoraClient;
import org.mule.modules.zuora.zuora.api.ZuoraException;
import org.mule.streaming.PagingConfiguration;
import org.mule.streaming.ProviderAwarePagingDelegate;

import com.zuora.api.object.ZObject;
import com.zuora.api.object.ZuoraBeanMap;
import org.springframework.beans.BeanUtils;

/**
 * Zuora is the leader in online recurring billing and payment solutions for SaaS and subscription businesses.
 * <p/>
 * This connector provides full access to the Z-Commerce platform API.
 *
 * @author MuleSoft, Inc.
 */
@Connector(name = "zuora", friendlyName = "Zuora", minMuleVersion = "3.5")
@RequiresEnterpriseLicense
public class ZuoraModule implements MuleContextAware {

    private static final String API_URL = "/apps/services/a/48.0";
    private static final String REST_API_URL = "/apps/api/";
    /**
     * The client to use. Mainly for mocking purposes
     */
    @Configurable
    @Optional
    private ZuoraClient<Exception> client;
    // Not configurable, just used to get batch exports. //NOTE: If more processors need this, we might build a Facade to hide it along with the SOAP client
    private RestZuoraClient restClient;

    /**
     * Target URI to connect to
     */
    @Configurable
    @Default("https://apisandbox.zuora.com")
    @Optional
    @Placement(group = "Connection")
    private String endpoint;

    private String username;
    private String password;

    private MuleContext muleContext;

    @MetaDataKeyRetriever
    public List<MetaDataKey> getMetadataKeys() {
        List<MetaDataKey> keys = new ArrayList<MetaDataKey>();
        Class<?> c = ZObjectType.class;

        Field[] flds = c.getDeclaredFields();
        for (Field f : flds) {
            if (f.isEnumConstant()) {
                Object value = null;
                try {
                    value = f.get(c);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Error while retrieving keys.", e);
                }
                keys.add(createKey(((ZObjectType) value).getZObjectClass()));
            }
        }

        return keys;
    }

    private MetaDataKey createKey(Class<?> cls) {
        return new DefaultMetaDataKey(cls.getSimpleName(), cls.getSimpleName());
    }

    @MetaDataRetriever
    public MetaData getMetadata(MetaDataKey key) throws Exception {

        Class<?> cls = getClassForType(key.getId());

        PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(cls);

        Map<String, MetaDataModel> fieldMap = new HashMap<String, MetaDataModel>();
        for (PropertyDescriptor pd : pds) {
            if (pd.getReadMethod() != null && pd.getWriteMethod() != null) {
                fieldMap.put(pd.getName(), getFieldMetadata(pd.getPropertyType()));
            }
        }

        DefaultDefinedMapMetaDataModel mapModel = new DefaultDefinedMapMetaDataModel(fieldMap, key.getId());

        return new DefaultMetaData(mapModel);
    }

    private MetaDataModel getFieldMetadata(Class<?> propertyType) {
        DataType dataType;
        if (String.class.equals(propertyType)) {
            dataType = DataType.STRING;
        } else if (Number.class.isAssignableFrom(propertyType)) {
            dataType = DataType.NUMBER;
        } else if (XMLGregorianCalendar.class.equals(propertyType)) {
            dataType = DataType.DATE_TIME;
        } else {
            return new DefaultPojoMetaDataModel(propertyType);
        }
        return new DefaultSimpleMetaDataModel(dataType);
    }

    private Class<?> getClassForType(String type) throws ClassNotFoundException {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();

        Class<?> cls = cl.loadClass("com.zuora.api.object." + type);
        return cls;
    }

    /**
     * Connects to Zuora
     *
     * @param username Username to identify the user
     * @param password Password to authenticate the username
     */
    @Connect
    public synchronized void connect(@ConnectionKey String username, @Password String password)
            throws ConnectionException {
        try {
            client = new CxfZuoraClient(username, password, this.endpoint + API_URL);
            restClient = new RestZuoraClientImpl(this.endpoint + REST_API_URL);
            this.username = username;
            this.password = password;
            //Dummy call to make sure credentials are ok.
            getUserInfo();
        } catch (UnexpectedErrorFault e) {
            throw new ConnectionException(ConnectionExceptionCode.UNKNOWN, e.getFaultInfo().getFaultCode().value(),
                    e.getFaultInfo().getFaultMessage());
        } catch (LoginFault e) {
            if (e.getFaultInfo().getFaultCode() == ErrorCode.INVALID_LOGIN) {
                throw new ConnectionException(ConnectionExceptionCode.INCORRECT_CREDENTIALS,
                        e.getFaultInfo().getFaultCode().value(), e.getFaultInfo().getFaultMessage());
            } else {
                throw new ConnectionException(ConnectionExceptionCode.UNKNOWN,
                        e.getFaultInfo().getFaultCode().value(), e.getFaultInfo().getFaultMessage());
            }
        } catch (Exception e) {
            throw new ConnectionException(ConnectionExceptionCode.UNKNOWN,
                    ConnectionExceptionCode.UNKNOWN.toString(), e.getMessage());
        }
    }

    @ValidateConnection
    public boolean isConnected() {
        return client != null;
    }

    /**
     * Destroys the session
     */
    @Disconnect
    public synchronized void disconnect() {
        client = null;
    }

    @ConnectionIdentifier
    public String getSessionId() {
        return client.getSessionId();
    }

    /**
     * Batch creation of ZObjects associated to Subscriptions
     * <p/>
     * {@sample.xml ../../../doc/mule-module-zuora.xml.sample zuora:subscribe}
     *
     * @param subscriptions the list of subscriptions to perform
     * @return a subscription results list, one for each subscription
     * 
     * @throws Exception If subscription fails
     */
    @Processor
    @InvalidateConnectionOn(exception = SessionTimedOutException.class)
    public List<SubscribeResult> subscribe(
            @Default("#[payload]") List<com.zuora.api.SubscribeRequest> subscriptions) throws Exception {
        return client.subscribe(subscriptions);
    }

    /**
     * Batch creation of ZObjects
     * <p/>
     * {@sample.xml ../../../doc/mule-module-zuora.xml.sample zuora:create}
     *
     * @param zobjects the zobjects to create
     * @param type     the type of zobject passed
     * @return a list of {@link SaveResult}, one for each ZObject
     * 
     * @throws Exception If creation fails
     */
    @Processor
    @InvalidateConnectionOn(exception = SessionTimedOutException.class)
    public List<SaveResult> create(@MetaDataKeyParam String type,
            @Optional @Default("#[payload]") List<Map<String, Object>> zobjects) throws Exception {
        return client.create(toPojos(type, zobjects));
    }

    private List<ZObject> toPojos(String type, List<Map<String, Object>> zobjects) {
        ArrayList<ZObject> pojos = new ArrayList<ZObject>();
        for (Map<String, Object> o : zobjects) {
            ZObject zobject = toPojo(type, o);

            pojos.add(zobject);
        }
        return pojos;
    }

    @SuppressWarnings("unchecked")
    private ZObject toPojo(String type, Map<String, Object> o) {
        ZObject zobject;
        if (o instanceof ZuoraBeanMap) {
            zobject = (ZObject) ((ZuoraBeanMap) o).getBean();
        } else {
            try {
                Class<?> cls = getClassForType(type);
                zobject = (ZObject) cls.newInstance();
                ZuoraBeanMap zuoraBeanMap = new ZuoraBeanMap(zobject);
                zuoraBeanMap.putAll(o);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return zobject;
    }

    /**
      * Batch creation of invoices for accounts
      * <p/>
      * {@sample.xml ../../../doc/mule-module-zuora.xml.sample zuora:generate}
      *
      * @param zobjects the zobjects to generate, as a list of string-object maps .
      *                 Zuora attribute names, unlike java beans, are CamelCase.
      * @param type     the type of zobject passed
      * @return a list of {@link SaveResult}, one for each ZObject
      * 
      * @throws Exception if generation fails
      */
    @Processor
    @InvalidateConnectionOn(exception = SessionTimedOutException.class)
    public List<SaveResult> generate(@MetaDataKeyParam String type,
            @Optional @Default("#[payload]") List<Map<String, Object>> zobjects) throws Exception {
        return client.generate(toPojos(type, zobjects));
    }

    /**
     * Batch update of ZObjects
     * <p/>
     * {@sample.xml ../../../doc/mule-module-zuora.xml.sample zuora:update}
     *
     * @param zobjects the zobjects to update, as a list of string-object maps .
     *                 Zuora attribute names, unlike java beans, are CamelCase.
     * @param type     the type of zobject passed
     * @return a list of {@link SaveResult}, one for each ZObject
     * 
     * @throws Exception If update fails
     */
    @Processor
    @InvalidateConnectionOn(exception = SessionTimedOutException.class)
    public List<SaveResult> update(@MetaDataKeyParam String type,
            @Optional @Default("#[payload]") List<Map<String, Object>> zobjects) throws Exception {
        return client.update(toPojos(type, zobjects));
    }

    /**
     * Batch delete of ZObjects
     * <p/>
     * {@sample.xml ../../../doc/mule-module-zuora.xml.sample zuora:delete}
     *
     * @param type the type of ZObjects to delete
     * @param ids  the list of ids to delete
     * @return a list of {@link DeleteResult}, one for each id
     * 
     * @throws Exception if deletion fails
     */
    @Processor
    @InvalidateConnectionOn(exception = SessionTimedOutException.class)
    public List<DeleteResult> delete(@MetaDataKeyParam String type, List<String> ids) throws Exception {
        return client.delete(type, ids);
    }

    /**
     * Lazily retrieves ZObject that match a given query,
     * written in Zuora native query language
     * <p/>
     * If you expect more than 2000 results to be returned, remember that you have
     * to enable queryMore for your zuora account (as documented:
     * http://knowledgecenter.zuora.com/D_Using_the_Zuora_API/C_API_Reference/E_API_Calls/queryMore())
     * <p/>
     * {@sample.xml ../../../doc/mule-module-zuora.xml.sample zuora:find}
     *
     * @param query the query, using the SQL-Like Zuora Query Language
     * @param pagingConfiguration .
     * @return a {@link ZObject} iterable. {@link ZObject} returned by this operation
     *         may be instances of either static ZObject - like Account or Amendment -,  if the object is a non-customizable Zuora entity,
     *         or {@link ZObject},  if the object is a customizable Zuora entity
     * @throws Exception if find fails
     */
    @SuppressWarnings("unchecked")
    @Paged
    @Processor
    @InvalidateConnectionOn(exception = SessionTimedOutException.class)
    public ProviderAwarePagingDelegate<Map<String, Object>, ZuoraModule> find(
            @org.mule.api.annotations.Query final String query, final PagingConfiguration pagingConfiguration) {
        return new ProviderAwarePagingDelegate<Map<String, Object>, ZuoraModule>() {

            private int pageNumber = 0;
            private boolean isDone;
            private String queryLocator;
            private int count = -1;

            @Override
            public List<Map<String, Object>> getPage(ZuoraModule provider) {
                try {
                    QueryResult result = null;
                    if (this.pageNumber == 0) {
                        result = provider.getClient().query(query);
                        pageNumber++;
                        count = result.getSize();
                        isDone = result.getDone();
                        queryLocator = result.getQueryLocator();
                    } else if (!isDone) {
                        result = provider.getClient().queryMore(queryLocator);
                        pageNumber++;
                        isDone = result.getDone();
                        queryLocator = result.getQueryLocator();
                    } else {
                        return Collections.emptyList();
                    }

                    List<Map<String, Object>> maps = new ArrayList<Map<String, Object>>();
                    for (ZObject o : result.getRecords()) {
                        if (o != null) {
                            maps.add(new ZuoraBeanMap(o));
                        }
                    }

                    return maps;

                } catch (Exception e) {
                    e.printStackTrace();
                }
                return Collections.emptyList();
            }

            @Override
            public void close() throws MuleException {

            }

            @Override
            public int getTotalResults(ZuoraModule provider) throws Exception {
                return 0;
            }

        };
    }

    @QueryTranslator
    public String toNativeQuery(DsqlQuery query) {
        ZuoraQueryVisitor visitor = new ZuoraQueryVisitor();
        query.accept(visitor);
        return visitor.dsqlQuery();
    }

    /**
     * Answers user information
     * <p/>
     * {@sample.xml ../../../doc/mule-module-zuora.xml.sample zuora:get-user-info}
     *
     * @return a {@link User}
     * @throws Exception if request fails
     */
    @Processor
    @InvalidateConnectionOn(exception = SessionTimedOutException.class)
    public User getUserInfo() throws Exception {
        return client.getUserInfo();
    }

    /**
     * Amends subscriptions
     * <p/>
     * {@sample.xml ../../../doc/mule-module-zuora.xml.sample zuora:amend}
     *
     * @param amendaments the list of amendments to perform
     * @return a list of {@link AmendResult}, one for each amendment
     * @throws Exception if amend fails
     */
    @Processor
    @InvalidateConnectionOn(exception = SessionTimedOutException.class)
    public List<AmendResult> amend(@Default("#[payload]") List<com.zuora.api.AmendRequest> amendaments)
            throws Exception {
        return client.amend(amendaments);
    }

    /**
     * Retrieve an exported file from Zuora and return an InputStream to it
     * 
     * {@sample.xml ../../../doc/mule-module-zuora.xml.sample zuora:get-export-file-stream}
     *
     * @param exportId id of the Zuora exported file to retrieve
     * @return an InputStream to read from the exported file
     * @throws IOException if can't access the exported file
     * @throws ZuoraException if the exported file doesn't exist
     */
    @Processor
    public InputStream getExportFileStream(final String exportId) throws IOException {
        return getRestClient().getExportedFileStream(this.username, this.password, exportId);
    }

    /**
     * Retrieve an exported file from Zuora, and return its content as a String
     * 
     * {@sample.xml ../../../doc/mule-module-zuora.xml.sample zuora:get-export-file-content}
     *
     * @param exportId id of the Zuora exported file to retrieve
     * @return the file's content as a String
     * @throws IOException if can't access the exported file
     * @throws ZuoraException if the exported file doesn't exist
     */
    @Processor
    public String getExportFileContent(final String exportId) throws IOException {
        final InputStream exportedFileStream = getExportFileStream(exportId);
        final String content = IOUtils.toString(exportedFileStream, "UTF-8");
        exportedFileStream.close();
        return content;
    }

    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
    }

    public String getEndpoint() {
        return endpoint;
    }

    public void setClient(ZuoraClient<Exception> client) {
        this.client = client;
    }

    public ZuoraClient<Exception> getClient() {
        return client;
    }

    public void setRestClient(RestZuoraClient client) {
        this.restClient = client;
    }

    private RestZuoraClient getRestClient() {
        return this.restClient;
    }

    @Override
    public void setMuleContext(final MuleContext context) {
        this.muleContext = context;
    }

    MuleContext getMuleContext() {
        return this.muleContext;
    }

    public static class ZuoraQueryVisitor extends DsqlQueryVisitor {

        @Override
        public org.mule.common.query.expression.OperatorVisitor operatorVisitor() {
            return new ZuoraOperatorVisitor();
        }

    }

    public static class ZuoraOperatorVisitor extends DefaultOperatorVisitor {

        @Override
        public java.lang.String notEqualsOperator() {
            return " != ";
        }

        @Override
        public String lessOperator() {
            return " &lt; ";
        }

        @Override
        public java.lang.String lessOrEqualsOperator() {
            return " &lt;= ";
        }

    }
}