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

discord4j.rest.RestClientBuilder Maven / Gradle / Ivy

/*
 * This file is part of Discord4J.
 *
 * Discord4J is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Discord4J is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Discord4J.  If not, see .
 */
package discord4j.rest;

import com.fasterxml.jackson.databind.ObjectMapper;
import discord4j.common.JacksonResources;
import discord4j.common.ReactorResources;
import discord4j.rest.http.ExchangeStrategies;
import discord4j.rest.request.*;
import discord4j.rest.response.ResponseFunction;
import discord4j.rest.route.Route;
import reactor.core.publisher.EmitterProcessor;
import reactor.core.publisher.FluxSink;
import reactor.netty.http.client.HttpClient;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * Builder suited for creating a {@link RestClient}. To acquire an instance, see {@link #createRest(String)}.
 */
public class RestClientBuilder {

    protected final Function clientFactory;
    protected final Function optionsModifier;

    protected String token;
    protected ReactorResources reactorResources;
    protected JacksonResources jacksonResources;
    protected ExchangeStrategies exchangeStrategies;
    protected List responseTransformers = new ArrayList<>();
    protected GlobalRateLimiter globalRateLimiter;
    protected RequestQueueFactory requestQueueFactory;

    /**
     * Initialize a new builder with the given token.
     *
     * @param token the bot token used to authenticate to Discord
     */
    public static RestClientBuilder createRest(String token) {
        Function clientFactory = config -> new RestClient(config.getRouter());
        return new RestClientBuilder<>(token, clientFactory, Function.identity());
    }

    protected RestClientBuilder(String token,
                                Function clientFactory,
                                Function optionsModifier) {
        this.token = Objects.requireNonNull(token, "token");
        this.clientFactory = Objects.requireNonNull(clientFactory, "clientFactory");
        this.optionsModifier = Objects.requireNonNull(optionsModifier, "optionsModifier");
    }

    protected RestClientBuilder(RestClientBuilder source,
                                Function clientFactory,
                                Function optionsModifier) {
        this.clientFactory = clientFactory;
        this.optionsModifier = optionsModifier;

        this.token = source.token;
        this.reactorResources = source.reactorResources;
        this.jacksonResources = source.jacksonResources;
        this.exchangeStrategies = source.exchangeStrategies;
        this.responseTransformers = source.responseTransformers;
        this.globalRateLimiter = source.globalRateLimiter;
        this.requestQueueFactory = source.requestQueueFactory;
    }

    /**
     * Add a configuration for {@link Router} implementation-specific cases, changing the type of the current
     * {@link RouterOptions} object passed to the {@link Router} factory in build methods.
     *
     * @param optionsModifier {@link Function} to transform the {@link RouterOptions} type to provide custom
     * {@link Router} implementations a proper configuration object.
     * @param  new type for the options
     * @return a new {@link RestClientBuilder} that will now work with the new options type.
     */
    public  RestClientBuilder setExtraOptions(Function optionsModifier) {
        return new RestClientBuilder<>(this, this.clientFactory, this.optionsModifier.andThen(optionsModifier));
    }

    /**
     * Change the token stored in this builder.
     *
     * @param token the new bot token
     * @return this builder
     */
    public RestClientBuilder setToken(final String token) {
        this.token = Objects.requireNonNull(token, "token");
        return this;
    }

    /**
     * Set a new {@link ReactorResources} dedicated to set up a connection pool, an event pool, as well as the
     * supporting {@link HttpClient} used for making rest requests and maintaining gateway connections.
     *
     * @param reactorResources the new resource provider used for rest and gateway operations
     * @return this builder
     */
    public RestClientBuilder setReactorResources(ReactorResources reactorResources) {
        this.reactorResources = reactorResources;
        return this;
    }

    /**
     * Set a new {@link JacksonResources} to this builder, dedicated to provide an {@link ObjectMapper} for
     * serialization and deserialization of data.
     *
     * @param jacksonResources the new resource provider for serialization and deserialization
     * @return this builder
     */
    public RestClientBuilder setJacksonResources(JacksonResources jacksonResources) {
        this.jacksonResources = jacksonResources;
        return this;
    }

    /**
     * Set the strategies to use when reading or writing HTTP request and response body entities. Defaults to using
     * {@link #setJacksonResources(JacksonResources)} to build a {@link ExchangeStrategies#jackson(ObjectMapper)} that
     * is capable of encoding and decoding JSON using Jackson.
     *
     * @param exchangeStrategies the HTTP exchange strategies to use
     * @return this builder
     */
    public RestClientBuilder setExchangeStrategies(ExchangeStrategies exchangeStrategies) {
        this.exchangeStrategies = Objects.requireNonNull(exchangeStrategies, "exchangeStrategies");
        return this;
    }

    /**
     * Sets a new API response behavior to the supporting {@link Router}, allowing cross-cutting behavior across
     * all requests made by it.
     * 

* The given {@link ResponseFunction} will be applied after every response. Calling this function multiple * times will result in additive behavior, so care must be taken regarding the order in * which multiple calls occur. Transformations will be added to the response pipeline in that order. *

* Built-in factories are supplied for commonly used behavior: *

    *
  • {@link ResponseFunction#emptyIfNotFound()} transforms any HTTP 404 error into an empty sequence.
  • *
  • {@link ResponseFunction#emptyIfNotFound(RouteMatcher)} transforms HTTP 404 errors from the given * {@link Route}s into an empty sequence.
  • *
  • {@link ResponseFunction#emptyOnErrorStatus(RouteMatcher, Integer...)} provides the same behavior as * above but for any given status codes.
  • *
  • {@link ResponseFunction#retryOnceOnErrorStatus(Integer...)} retries once for the given status codes.
  • *
  • {@link ResponseFunction#retryOnceOnErrorStatus(RouteMatcher, Integer...)} provides the same behavior * as above but for any matching {@link Route}.
  • *
* * @param responseFunction the {@link ResponseFunction} to transform the responses from matching requests. * @return this builder */ public RestClientBuilder onClientResponse(ResponseFunction responseFunction) { responseTransformers.add(responseFunction); return this; } /** * Define the {@link GlobalRateLimiter} to be applied while configuring the {@link Router} for a client. * {@link GlobalRateLimiter} purpose is to coordinate API requests to properly delay them under global rate * limiting scenarios. A supporting {@link Router} factory (supplied at {@link #build(Function)}) is responsible * for applying the given limiter. * * @param globalRateLimiter the limiter instance to be used while configuring a {@link Router} * @return this builder * @see GlobalRateLimiter */ public RestClientBuilder setGlobalRateLimiter(GlobalRateLimiter globalRateLimiter) { this.globalRateLimiter = globalRateLimiter; return this; } /** * Sets the {@link RequestQueueFactory} that will provide {@link RequestQueue} instances for the router. * *

* If not set, it will use a {@link RequestQueueFactory} providing request queues backed by an * {@link EmitterProcessor} with buffering overflow strategy. *

* * @param requestQueueFactory the factory that will provide {@link RequestQueue} instances for the router * @return this builder * @see RequestQueueFactory#backedByProcessor(Supplier, FluxSink.OverflowStrategy) */ public RestClientBuilder setRequestQueueFactory(RequestQueueFactory requestQueueFactory) { this.requestQueueFactory = requestQueueFactory; return this; } /** * Create a client capable of connecting to Discord REST API using a {@link DefaultRouter} that is capable of * working in monolithic environments. * * @return a configured {@link RestClient} based on this builder parameters */ public C build() { return build(DefaultRouter::new); } /** * Create a client capable of connecting to Discord REST API using a custom {@link Router} factory. The resulting * {@link RestClient} will use the produced {@link Router} for every request. * * @param routerFactory the factory of {@link Router} implementation * @return a configured {@link RestClient} based on this builder parameters */ public C build(Function routerFactory) { ReactorResources reactor = initReactorResources(); JacksonResources jackson = initJacksonResources(); O options = buildOptions(reactor, jackson); Router router = routerFactory.apply(options); Config config = new Config(token, reactor, jackson, initExchangeStrategies(jackson), Collections.unmodifiableList(responseTransformers), globalRateLimiter, router); return clientFactory.apply(config); } private O buildOptions(ReactorResources reactor, JacksonResources jackson) { RouterOptions options = new RouterOptions(token, reactor, initExchangeStrategies(jackson), responseTransformers, initGlobalRateLimiter(reactor), initRequestQueueFactory()); return this.optionsModifier.apply(options); } private ReactorResources initReactorResources() { if (reactorResources != null) { return reactorResources; } return new ReactorResources(); } private JacksonResources initJacksonResources() { if (jacksonResources != null) { return jacksonResources; } return new JacksonResources(); } private ExchangeStrategies initExchangeStrategies(JacksonResources jacksonResources) { if (exchangeStrategies != null) { return exchangeStrategies; } return ExchangeStrategies.jackson(jacksonResources.getObjectMapper()); } private GlobalRateLimiter initGlobalRateLimiter(ReactorResources reactorResources) { if (globalRateLimiter != null) { return globalRateLimiter; } return BucketGlobalRateLimiter.create(50, Duration.ofSeconds(1), reactorResources.getTimerTaskScheduler()); } private RequestQueueFactory initRequestQueueFactory() { if (requestQueueFactory != null) { return requestQueueFactory; } return RequestQueueFactory.buffering(); } protected static class Config { private final String token; private final ReactorResources reactorResources; private final JacksonResources jacksonResources; private final ExchangeStrategies exchangeStrategies; private final List responseTransformers; private final GlobalRateLimiter globalRateLimiter; private final Router router; public Config(String token, ReactorResources reactorResources, JacksonResources jacksonResources, ExchangeStrategies exchangeStrategies, List responseTransformers, GlobalRateLimiter globalRateLimiter, Router router) { this.token = token; this.reactorResources = reactorResources; this.jacksonResources = jacksonResources; this.exchangeStrategies = exchangeStrategies; this.responseTransformers = responseTransformers; this.globalRateLimiter = globalRateLimiter; this.router = router; } public String getToken() { return token; } public ReactorResources getReactorResources() { return reactorResources; } public JacksonResources getJacksonResources() { return jacksonResources; } public ExchangeStrategies getExchangeStrategies() { return exchangeStrategies; } public List getResponseTransformers() { return responseTransformers; } public GlobalRateLimiter getGlobalRateLimiter() { return globalRateLimiter; } public Router getRouter() { return router; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy