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

com.xero.api.ApiClient Maven / Gradle / Ivy

package com.xero.api;

import com.xero.api.client.*;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule;
import org.threeten.bp.*;
import com.google.api.client.googleapis.util.Utils;
import com.google.api.client.http.AbstractHttpContent;
import com.google.api.client.http.ByteArrayContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpContent;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpMethods;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.Json;
import java.io.IOException;
import java.io.OutputStream;
import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkException;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.UrlJwkProvider;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.interfaces.RSAPublicKey;

/** 
 * ApiClient for managing global http client and object mapper
*/
public class ApiClient {
    private final String basePath;
    private final HttpRequestFactory httpRequestFactory;
    private final ObjectMapper objectMapper;
    private HttpTransport httpTransport = new NetHttpTransport();
    private int connectionTimeout = 180000;
    private int readTimeout = 180000;

    private static final String defaultBasePath = "https://api.xero.com/api.xro/2.0";

    // A reasonable default object mapper. Client can pass in a chosen ObjectMapper anyway, this is just for reasonable defaults.
    private static ObjectMapper createDefaultObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper()
            .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .setDateFormat(new RFC3339DateFormat())
            .setSerializationInclusion(Include.NON_EMPTY);
        objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
        
        ThreeTenModule module = new ThreeTenModule();
        objectMapper.registerModule(module);
        return objectMapper;
    }

    /** method for initiazing object instance with default properties */
    public ApiClient() {
        this(null, null, null, null,null);
    }

    /** ApiClient method for initiazing object instance with custom properties 
    * @param basePath String that defines the base path for each API request
    * @param transport HttpTransport required for creating the httpRequestFactory
    * @param initializer HttpRequestInitializer for additional configuration during creation of httpRequestFactory
    * @param objectMapper ObjectMapper object used to serialize and deserialize using model classes.
    * @param reqFactory HttpRequestFactory is the thread-safe light-weight HTTP request factory layer on top of the HTTP transport  
    */
    public ApiClient(
        String basePath,
        HttpTransport transport,
        HttpRequestInitializer initializer,
        ObjectMapper objectMapper,
        HttpRequestFactory reqFactory
    ) {
        this.basePath = basePath == null ? defaultBasePath : (
            basePath.endsWith("/") ? basePath.substring(0, basePath.length() - 1) : basePath
        );
        if (transport != null) {
            this.httpTransport = transport;
        }
        this.httpRequestFactory = (reqFactory != null ? reqFactory : (transport == null ? Utils.getDefaultTransport() : transport).createRequestFactory(initializer) );
        this.objectMapper = (objectMapper == null ? createDefaultObjectMapper() : objectMapper);
    }

    /** method for retrieving the httpRequestFactory property
    * @return HttpRequestFactory
    */
    public HttpRequestFactory getHttpRequestFactory() {
        return httpRequestFactory;
    }

    /** method for retrieving the connectionTimeout property 
    * @return int
    */
    public int getConnectionTimeout() {
        return connectionTimeout;
    }

    /** method for setting the connectionTimeout property 
    * @param connectionTimeout int defines in milliseconds the time allowed making the initial connection to the server.
    */
    public void setConnectionTimeout(int connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    /** method for retrieving the readTimeout property
    * @return int
    */
    public int getReadTimeout() {
        return readTimeout;
    }

    /** method for setting the readTimeout property 
    * @param readTimeout int defines in milliseconds the time allowed to complete reading data from the server.
    */
    public void setReadTimeout(int readTimeout) {
        this.readTimeout = readTimeout;
    }

    /** method for retrieving the httpTransport property 
    * @return HttpTransport
    */
    public HttpTransport getHttpTransport() {
        return httpTransport;
    }

    /** method for setting the httpTransport property 
    * @param transport HttpTransport required for creating the httpRequestFactory
    */
    public void setHttpTransport(HttpTransport transport) {
        this.httpTransport = transport;
    }

    /** method for retrieving the basePath property 
    * @return String
    */
    public String getBasePath() {
        return basePath;
    }

    /** method for retrieving the objectMapper property 
    * @return ObjectMapper
    */
    public ObjectMapper getObjectMapper() {
        return objectMapper;
    }

    /** method for managing objects to be serialized */
    public class JacksonJsonHttpContent extends AbstractHttpContent {
        /* A POJO that can be serialized with a com.fasterxml Jackson ObjectMapper */
        private final Object data;

        /**  method for taking native object and serializing into JSON data for use by http client 
        * @param data Object 
        */
        public JacksonJsonHttpContent(Object data) {
            super(Json.MEDIA_TYPE);
            this.data = data;
        }

        @Override
        public void writeTo(OutputStream out) throws IOException {
            objectMapper.writeValue(out, data);
        }
    }

    /**  Builder pattern to get API instances for this client. 
    * @return AccountingApi
    */
    public AccountingApi accountingApi() {
        return new AccountingApi(this);
    }

    /** method for verifying an access token using RSA256 Algorithm
    * @param accessToken String the JWT token used to access the API
    * @return DecodedJWT
    * @exception MalformedURLException Thrown to indicate that a malformed URL has occurred. 
    * @exception JwkException thrown to indicate a problem while verifying a JWT
    */
    public DecodedJWT verify(String accessToken) throws MalformedURLException, JwkException {
		
    	DecodedJWT unverifiedJWT = JWT.decode(accessToken);        
    	JwkProvider provider = new UrlJwkProvider(new URL("https://identity.xero.com/.well-known/openid-configuration/jwks"));
		Jwk jwk = provider.get(unverifiedJWT.getKeyId());
	
		Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(),null);
		 
		JWTVerifier verifier = JWT.require(algorithm)
				 .withIssuer("https://identity.xero.com")
                 .acceptLeeway(1000)
				 .build();
		DecodedJWT verifiedJWT = verifier.verify(accessToken);
		 
    	return verifiedJWT;
    }

    /** method for revoking a refresh token
     * @param refreshToken String the refresh token used to obtain a new access token via the API
     * @param clientId String the client id used to authtenticate with the API
     * @param clientSecret String the client secret used to authtenticate with the API
     * @return revokeResponse HttpResponse a successful revocation request will return a 200 response with an empty body.
     */
    public HttpResponse revoke(String clientId, String clientSecret, String refreshToken) throws IOException {

		// BASIC AUTHENTICATION HEADER
		HttpHeaders headers = new HttpHeaders();
	    headers.setBasicAuthentication(clientId, clientSecret);
		
	    // POST BODY WITH REFRESH TOKEN
		String urlParameters  = "token=" + refreshToken;
		byte[] postData = urlParameters.getBytes( StandardCharsets.UTF_8 );
		HttpContent content = new ByteArrayContent("application/x-www-form-urlencoded", postData);
	
		// REVOKE URL DEFINED
		GenericUrl url = new GenericUrl("https://identity.xero.com/connect/revocation");
        		
		HttpTransport transport = this.getHttpTransport();
	    HttpRequestFactory requestFactory = transport.createRequestFactory();
	    HttpResponse revokeResponse = requestFactory
		    .buildRequest(HttpMethods.POST, url, content)
		    .setHeaders(headers)
		    .setConnectTimeout(this.getConnectionTimeout())
		    .setReadTimeout(this.getReadTimeout())
		    .execute();
		
		return revokeResponse;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy