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

com.github.davidmoten.microsoft.client.builder.MicrosoftClientBuilder Maven / Gradle / Ivy

There is a newer version: 0.2.2
Show newest version
package com.github.davidmoten.microsoft.client.builder;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.SocketConfig;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import com.github.davidmoten.guavamini.Preconditions;
import com.github.davidmoten.microsoft.authentication.AccessTokenProvider;
import com.github.davidmoten.microsoft.authentication.AuthenticationEndpoint;
import com.github.davidmoten.microsoft.authentication.Authenticator;
import com.github.davidmoten.microsoft.authentication.BearerAuthenticator;
import com.github.davidmoten.microsoft.authentication.ClientCredentialsAccessTokenProvider;
import com.github.davidmoten.odata.client.Context;
import com.github.davidmoten.odata.client.HttpService;
import com.github.davidmoten.odata.client.Path;
import com.github.davidmoten.odata.client.PathStyle;
import com.github.davidmoten.odata.client.Properties;
import com.github.davidmoten.odata.client.RequestHeader;
import com.github.davidmoten.odata.client.SchemaInfo;
import com.github.davidmoten.odata.client.Serializer;
import com.github.davidmoten.odata.client.internal.ApacheHttpClientHttpService;

public final class MicrosoftClientBuilder {

    private final Creator creator;
    private final String baseUrl;
    private String tenantName;
    private String resource;
    private List scopes = new ArrayList<>();
    private String clientId;
    private String clientSecret;
    private long refreshBeforeExpiryDurationMs = TimeUnit.MINUTES.toMillis(5);
    private long connectTimeoutMs;
    private long readTimeoutMs;
    private Optional proxyHost = Optional.empty();
    private Optional proxyPort = Optional.empty();
    private Optional proxyUsername = Optional.empty();
    private Optional proxyPassword = Optional.empty();
    private Optional> httpClientSupplier = Optional.empty();
    private Optional> httpClientBuilderExtras = Optional
            .empty();
    private String authenticationEndpoint = AuthenticationEndpoint.GLOBAL.url();
    private Function httpServiceTransformer = x -> x;
    private Optional accessTokenProvider = Optional.empty();
    private Optional authenticator = Optional.empty();
    private Optional> basicCredentials = Optional.empty();
    private final List schemas;
    private final PathStyle pathStyle;

    MicrosoftClientBuilder(String baseUrl, Creator creator, List schemas, PathStyle pathStyle) {
        Preconditions.checkNotNull(baseUrl);
        Preconditions.checkNotNull(creator);
        this.baseUrl = baseUrl;
        this.creator = creator;
        this.schemas = schemas;
        this.pathStyle = pathStyle;
    }
    
    public static BuilderBuilder baseUrl(String baseUrl) {
        Preconditions.checkNotNull(baseUrl);
        return new BuilderBuilder(baseUrl);
    }
    
    public static final class BuilderBuilder {

        private final String baseUrl;

        BuilderBuilder(String baseUrl) {
            this.baseUrl = baseUrl;
        }
        
        public  BuilderBuilder2 creator(Creator creator) {
            return new BuilderBuilder2(this, creator); 
        }
        
    }
    
    public static final class BuilderBuilder2 {

        private final BuilderBuilder b;
        private Creator creator;
        private final List schemas = new ArrayList<>();
        private PathStyle pathStyle = PathStyle.IDENTIFIERS_AS_SEGMENTS;

        BuilderBuilder2(BuilderBuilder b, Creator creator) {
            this.b = b;
            this.creator = creator;
        }
        
        public BuilderBuilder2 addSchema(SchemaInfo schema) {
            schemas.add(schema);
            return this;
        }
        
        /**
         * Sets the path style. Default is {@link PathStyle#IDENTIFIERS_AS_SEGMENTS}.
         * @param pathStyle path style to use
         * @return this
         */
        public BuilderBuilder2 pathStyle(PathStyle pathStyle) {
            this.pathStyle = pathStyle;
            return this;
        }
        
        public MicrosoftClientBuilder build() {
            return new MicrosoftClientBuilder(b.baseUrl,creator, schemas, pathStyle);
        }
        
    }
    
    public BuilderCustomAuthenticator authenticator(Authenticator authenticator) {
        return new BuilderCustomAuthenticator(this, authenticator);
    }

    public static final class BuilderCustomAuthenticator {

        private final Authenticator authenticator;
        private final MicrosoftClientBuilder b;

        BuilderCustomAuthenticator(MicrosoftClientBuilder b, Authenticator authenticator) {
            this.authenticator = authenticator;
            this.b = b;
        }

        public BuilderCustomAuthenticator connectTimeout(long duration, TimeUnit unit) {
            b.connectTimeoutMs = unit.toMillis(duration);
            return this;
        }

        public BuilderCustomAuthenticator readTimeout(long duration, TimeUnit unit) {
            b.readTimeoutMs = unit.toMillis(duration);
            return this;
        }

        public BuilderCustomAuthenticator proxyHost(String proxyHost) {
            b.proxyHost = Optional.of(proxyHost);
            return this;
        }

        public BuilderCustomAuthenticator proxyPort(int proxyPort) {
            b.proxyPort = Optional.of(proxyPort);
            return this;
        }

        public BuilderCustomAuthenticator proxyUsername(String username) {
            b.proxyUsername = Optional.of(username);
            return this;
        }

        public BuilderCustomAuthenticator proxyPassword(String password) {
            b.proxyPassword = Optional.of(password);
            return this;
        }

        /**
         * Do your own thing to create an Apache {@link HttpClient}. This method might
         * disappear if the underlying http service gets swapped out for another one.
         * You might want to use this method if your proxy interaction is complicated
         * for example.
         * 
         * @param supplier provider of HttpClient
         * @return this
         */
        public BuilderCustomAuthenticator httpClientProvider(
                Supplier supplier) {
            Preconditions.checkArgument(!b.httpClientBuilderExtras.isPresent());
            b.httpClientSupplier = Optional.of(supplier);
            return this;
        }

        /**
         * Do your own thing to further modify a configured {@link HttpClientBuilder}.
         * This method might disappear if the underlying http service gets swapped out
         * for another one. You might want to use this method if your proxy interaction
         * is complicated for example or if you want to use interceptors or other fancy
         * stuff.
         * 
         * @param extras modifier of builder
         * @return this
         */
        public BuilderCustomAuthenticator httpClientBuilderExtras(
                Function extras) {
            Preconditions.checkArgument(!b.httpClientSupplier.isPresent());
            b.httpClientBuilderExtras = Optional.of(extras);
            return this;
        }

        public T build() {
            return createService(b.baseUrl, authenticator, b.connectTimeoutMs, b.readTimeoutMs,
                    b.proxyHost, b.proxyPort, b.proxyUsername, b.proxyPassword,
                    b.httpClientSupplier, b.httpClientBuilderExtras, b.creator,
                    b.authenticationEndpoint, b.httpServiceTransformer,b.schemas, b.pathStyle);
        }

    }

    public Builder5 basicAuthentication(Supplier usernamePassword) {
        this.basicCredentials = Optional.of(usernamePassword);
        return new Builder5(this);
    }

    public Builder tenantName(String tenantName) {
        this.tenantName = tenantName;
        return new Builder(this);
    }

    public static final class Builder {
        private final MicrosoftClientBuilder b;

        Builder(MicrosoftClientBuilder b) {
            this.b = b;
        }

        public Builder2 resource(String resource) {
            Preconditions.checkNotNull(resource);
            b.resource = resource;
            return new Builder2(b);
        }

    }

    public static final class Builder2 {
        private final MicrosoftClientBuilder b;

        Builder2(MicrosoftClientBuilder b) {
            this.b = b;
        }

        public Builder3 scope(String... scopes) {
            Preconditions.checkNotNull(scopes);
            return scope(Arrays.asList(scopes));
        }
        
        public Builder3 scope(List scopes) {
            Preconditions.checkNotNull(scopes);
            b.scopes.addAll(scopes);
            return new Builder3(b);
        }
        
        public Builder4 clientId(String clientId) {
            return new Builder3(b).clientId(clientId);
        }

    }

    public static final class Builder3 {
        private final MicrosoftClientBuilder b;

        Builder3(MicrosoftClientBuilder b) {
            this.b = b;
        }

        public Builder4 clientId(String clientId) {
            b.clientId = clientId;
            return new Builder4(b);
        }

    }

    public static final class Builder4 {
        private final MicrosoftClientBuilder b;

        Builder4(MicrosoftClientBuilder b) {
            this.b = b;
        }

        public Builder5 clientSecret(String clientSecret) {
            b.clientSecret = clientSecret;
            return new Builder5(b);
        }

    }

    public static final class Builder5 {
        private final MicrosoftClientBuilder b;

        Builder5(MicrosoftClientBuilder b) {
            this.b = b;
        }

        public Builder5 refreshBeforeExpiry(long duration, TimeUnit unit) {
            b.refreshBeforeExpiryDurationMs = unit.toMillis(duration);
            return this;
        }

        public Builder5 connectTimeout(long duration, TimeUnit unit) {
            b.connectTimeoutMs = unit.toMillis(duration);
            return this;
        }

        public Builder5 readTimeout(long duration, TimeUnit unit) {
            b.readTimeoutMs = unit.toMillis(duration);
            return this;
        }

        public Builder5 proxyHost(String proxyHost) {
            b.proxyHost = Optional.of(proxyHost);
            return this;
        }

        public Builder5 proxyPort(int proxyPort) {
            b.proxyPort = Optional.of(proxyPort);
            return this;
        }

        public Builder5 httpServiceTransformer(
                Function transformer) {
            b.httpServiceTransformer = transformer;
            return this;
        }

        public Builder5 accessTokenProvider(AccessTokenProvider atp) {
            b.accessTokenProvider = Optional.of(atp);
            return this;
        }

        public Builder5 proxyUsername(String username) {
            b.proxyUsername = Optional.of(username);
            return this;
        }

        public Builder5 proxyPassword(String password) {
            b.proxyPassword = Optional.of(password);
            return this;
        }
        
        /**
         * Do your own thing to create an Apache {@link HttpClient}. This method might
         * disappear if the underlying http service gets swapped out for another one.
         * You might want to use this method if your proxy interaction is complicated
         * for example.
         * 
         * @param supplier provider of HttpClient
         * @return this
         */
        public Builder5 httpClientProvider(Supplier supplier) {
            Preconditions.checkArgument(!b.httpClientBuilderExtras.isPresent());
            b.httpClientSupplier = Optional.of(supplier);
            return this;
        }

        /**
         * Do your own thing to further modify a configured {@link HttpClientBuilder}.
         * This method might disappear if the underlying http service gets swapped out
         * for another one. You might want to use this method if your proxy interaction
         * is complicated for example or if you want to use interceptors or other fancy
         * stuff.
         * 
         * @param extras modifier of builder
         * @return this
         */
        public Builder5 httpClientBuilderExtras(
                Function extras) {
            Preconditions.checkArgument(!b.httpClientSupplier.isPresent());
            b.httpClientBuilderExtras = Optional.of(extras);
            return this;
        }

        /**
         * Sets the authentication endpoint url to use for access tokens etc. If not
         * specified defaults to {@link AuthenticationEndpoint#GLOBAL}.
         * 
         * @param authenticationEndpoint endpoint to use for authentication
         * @return this
         */
        public Builder5 authenticationEndpoint(AuthenticationEndpoint authenticationEndpoint) {
            return authenticationEndpoint(authenticationEndpoint.url());
        }

        /**
         * Sets the authentication endpoint url to use for access tokens etc. If not
         * specified defaults to {@link AuthenticationEndpoint#GLOBAL} url.
         * 
         * @param authenticationEndpoint endpoint to use for authentication
         * @return this
         */
        public Builder5 authenticationEndpoint(String authenticationEndpoint) {
            b.authenticationEndpoint = authenticationEndpoint;
            return this;
        }

        public Builder5 authenticator(Authenticator authenticator) {
            b.authenticator = Optional.of(authenticator);
            return this;
        }

        public T build() {
            if (!b.authenticator.isPresent() && b.basicCredentials.isPresent()) {
                Supplier bc = b.basicCredentials.get();
                authenticator((url, requestHeaders) -> {
                    // some streaming endpoints object to auth so don't add header
                    // if not on the base service
                    if (url.toExternalForm().startsWith(b.baseUrl)) {
                        // remove Authorization header if present
                        List list = requestHeaders //
                                .stream() //
                                .filter(rh -> !rh.name().equalsIgnoreCase("Authorization")) //
                                .collect(Collectors.toList());
                        // add basic auth request header
                        UsernamePassword c = bc.get();
                        list.add(basicAuth(c.username(), c.password()));
                        return list;
                    } else {
                        return requestHeaders;
                    }
                });
            }
            return createService(b.baseUrl, b.tenantName, b.resource, b.scopes, b.clientId,
                    b.clientSecret, b.refreshBeforeExpiryDurationMs, b.connectTimeoutMs,
                    b.readTimeoutMs, b.proxyHost, b.proxyPort, b.proxyUsername, b.proxyPassword,
                    b.httpClientSupplier, b.httpClientBuilderExtras, b.creator,
                    b.authenticationEndpoint, b.httpServiceTransformer, b.accessTokenProvider,
                    b.authenticator, b.schemas, b.pathStyle);
        }

    }

    private static RequestHeader basicAuth(String username, String password) {
        String s = username + ":" + password;
        String encoded = Base64.getEncoder().encodeToString(s.getBytes(StandardCharsets.UTF_8));
        return RequestHeader.create("Authorization", "Basic " + encoded);
    }

    public static final class UsernamePassword {

        private final String username;
        private final String password;

        UsernamePassword(String username, String password) {
            this.username = username;
            this.password = password;
        }

        public static UsernamePassword create(String username, String password) {
            return new UsernamePassword(username, password);
        }

        public String username() {
            return username;
        }

        public String password() {
            return password;
        }
    }

    private static  T createService(String baseUrl, String tenantName, String resource,
            List scopes, String clientId, String clientSecret, long refreshBeforeExpiryDurationMs,
            long connectTimeoutMs, long readTimeoutMs, //
            Optional proxyHost, Optional proxyPort, //
            Optional proxyUsername, Optional proxyPassword,
            Optional> supplier,
            Optional> httpClientBuilderExtras,
            Creator creator, //
            String authenticationEndpoint, //
            Function httpServiceTransformer,
            Optional accessTokenProviderOverride, //
            Optional authenticator, List schemas, PathStyle pathStyle) {
        final Authenticator auth;
        if (authenticator.isPresent()) {
            auth = authenticator.get();
        } else {
            AccessTokenProvider accessTokenProvider = accessTokenProviderOverride //
                    .orElseGet(() -> ClientCredentialsAccessTokenProvider //
                            .tenantName(tenantName) //
                            .resource(resource) //
                            .scope(scopes) //
                            .clientId(clientId) //
                            .clientSecret(clientSecret) //
                            .connectTimeoutMs(connectTimeoutMs, TimeUnit.MILLISECONDS) //
                            .readTimeoutMs(readTimeoutMs, TimeUnit.MILLISECONDS) //
                            .refreshBeforeExpiry(refreshBeforeExpiryDurationMs,
                                    TimeUnit.MILLISECONDS) //
                            .authenticationEndpoint(authenticationEndpoint) //
                            .proxyHost(proxyHost) //
                            .proxyPort(proxyPort) //
                            .proxyUsername(proxyUsername) //
                            .proxyPassword(proxyPassword) //
                            .build());
            auth = new BearerAuthenticator(accessTokenProvider, baseUrl);
        }
        return createService(baseUrl, auth, connectTimeoutMs, readTimeoutMs, proxyHost, proxyPort,
                proxyUsername, proxyPassword, supplier, httpClientBuilderExtras, creator,
                authenticationEndpoint, httpServiceTransformer, schemas, pathStyle);
    }

    private static Supplier createClientSupplier(long connectTimeoutMs,
            long readTimeoutMs, Optional proxyHost, Optional proxyPort,
            Optional proxyUsername, Optional proxyPassword,
            Optional> supplier,
            Optional> httpClientBuilderExtras) {
        final Supplier clientSupplier;
        if (supplier.isPresent()) {
            clientSupplier = supplier.get();
        } else {
            clientSupplier = () -> createHttpClient(connectTimeoutMs, readTimeoutMs, proxyHost,
                    proxyPort, proxyUsername, proxyPassword, httpClientBuilderExtras);
        }
        return clientSupplier;
    }

    @SuppressWarnings("resource")
    private static  T createService(String baseUrl, Authenticator authenticator,
            long connectTimeoutMs, long readTimeoutMs, //
            Optional proxyHost, Optional proxyPort, //
            Optional proxyUsername, Optional proxyPassword,
            Optional> supplier,
            Optional> httpClientBuilderExtras,
            Creator creator, String authenticationEndpoint, //
            Function httpServiceTransformer, //
            List schemas, PathStyle pathStyle) {
        final Supplier clientSupplier = createClientSupplier(connectTimeoutMs,
                readTimeoutMs, proxyHost, proxyPort, proxyUsername, proxyPassword, supplier,
                httpClientBuilderExtras);
        Path basePath = new Path(baseUrl, pathStyle);
        HttpService httpService = new ApacheHttpClientHttpService( //
                basePath, //
                clientSupplier, //
                authenticator::authenticate);
        httpService = httpServiceTransformer.apply(httpService);
        return creator.create(new Context(Serializer.INSTANCE, httpService, createProperties(), schemas));
    }

    public static Map createProperties() {
        Map p = new HashMap<>();
        p.put(Properties.MODIFY_STREAM_EDIT_LINK, "true");
        p.put(Properties.ATTEMPT_STREAM_WHEN_NO_METADATA, "true");
        p.put(Properties.ACTION_OR_FUNCTION_SEGMENT_SIMPLE_NAME, "true");
        return p;
    }

    private static CloseableHttpClient createHttpClient(long connectTimeoutMs, long readTimeoutMs,
            Optional proxyHost, Optional proxyPort, Optional proxyUsername,
            Optional proxyPassword,
            Optional> httpClientBuilderExtras) {
        RequestConfig config = RequestConfig.custom() //
                .setConnectTimeout((int) connectTimeoutMs) //
                .setSocketTimeout((int) readTimeoutMs) //
                .build();

        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        // Set soTimeout here to affect socketRead in the phase of ssl handshake. Note
        // that
        // the RequestConfig.setSocketTimeout will take effect only after the ssl
        // handshake completed.
        cm.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout((int) readTimeoutMs).build());

        HttpClientBuilder b = HttpClientBuilder //
                .create() //
                .useSystemProperties() //
                .setDefaultRequestConfig(config) //
                .setConnectionManager(cm);

        if (proxyHost.isPresent()) {
            HttpHost proxy = new HttpHost(proxyHost.get(), proxyPort.get());
            if (proxyUsername.isPresent()) {
                Credentials credentials = new UsernamePasswordCredentials(proxyUsername.get(),
                        proxyPassword.orElse(null));
                AuthScope authScope = new AuthScope(proxyHost.get(), proxyPort.get());
                CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                credentialsProvider.setCredentials(authScope, credentials);
                b = b.setDefaultCredentialsProvider(credentialsProvider);
            }
            b = b.setProxy(proxy);
        }
        if (httpClientBuilderExtras.isPresent()) {
            b = httpClientBuilderExtras.get().apply(b);
        }
        return b.build();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy