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

com.c4_soft.springaddons.rest.SpringAddonsRestProperties Maven / Gradle / Ivy

package com.c4_soft.springaddons.rest;

import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.service.annotation.HttpExchange;
import lombok.Data;

/**
 * @author Jérôme Wacongne <ch4mp@c4-soft.com>
 */
@Data
@AutoConfiguration
@ConfigurationProperties(prefix = "com.c4-soft.springaddons.rest")
public class SpringAddonsRestProperties {

  /**
   * Expose {@link RestClient} or {@link WebClient} instances as named beans
   */
  private Map client = new HashMap<>();

  // FIXME: enable when a way is found to generate and register service proxies as beans.
  // For instance, have the HttpExchangeProxyFactoryBean definitions registered with a
  // BeanDefinitionRegistryPostProcessor

  // /**
  // * Expose {@link HttpExchange @HttpExchange} proxies as named beans (generated using
  // * {@link HttpServiceProxyFactory})
  // */
  // private Map service = new HashMap<>();

  public String getClientBeanName(String clientId) {
    if (!client.containsKey(clientId)) {
      return null;
    }
    final var clientProperties = client.get(clientId);
    return clientProperties.getBeanName()
        .orElse(clientProperties.isExposeBuilder() ? toCamelCase(clientId) + "Builder"
            : toCamelCase(clientId));
  }

  private static String toCamelCase(String in) {
    if (in == null) {
      return null;
    }
    if (!StringUtils.hasText(in)) {
      return "";
    }
    String[] words = in.split("[\\W_]+");
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < words.length; i++) {
      String word = words[i];
      if (i == 0) {
        word = word.isEmpty() ? word : word.toLowerCase();
      } else {
        word = word.isEmpty() ? word
            : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase();
      }
      builder.append(word);
    }
    return builder.toString();
  }

  @Data
  public static class RestClientProperties {
    /**
     * Base URI used to build the REST client ({@link RestClient} or {@link WebClient})
     */
    private Optional baseUrl = Optional.empty();

    /**
     * Configure a {@link ClientHttpRequestInterceptor} or {@link ExchangeFilterFunction} to
     * authorize requests (add a Basic or Bearer header to each request)
     */
    private AuthorizationProperties authorization = new AuthorizationProperties();

    /**
     * Configure the internal {@link SimpleClientHttpRequestFactory} with timeouts and HTTP or SOCKS
     * proxy
     */
    private ClientHttpRequestFactoryProperties http = new ClientHttpRequestFactoryProperties();

    /**
     * Defines the type of the REST client. Default is {@link RestClient} in servlet applications
     * and {@link WebClient} in reactive ones.
     */
    private ClientType type = ClientType.DEFAULT;

    /**
     * If true, what is exposed as a bean is the pre-configured {@link RestClient.Builder} or
     * {@link WebClient.Builder}. This allows to add some more configuration. Don't forget to expose
     * the resulting {@link RestClient} or {@link WebClient} as a named bean if you intend to use it
     * as the REST client in an auto-configured {@link HttpExchange @HttpExchange} proxy.
     */
    private boolean exposeBuilder = false;

    /**
     * 

* Override the auto-configured bean name which defaults to the camelCase version of the * client-id, with the "Builder" suffix if expose-builder is true. *

*

* For instance, "com.c4-soft.springaddons.rest.client.machin-client" will create a bean named * machinClient or machinClientBuilder depending on * "com.c4-soft.springaddons.rest.client.machin-client.expose-builder" value. *

*/ private Optional beanName = Optional.empty(); /** * Some static headers to add to all requests sent by this client (for instance an API key). The * key is the header name. If the value contains several entries, the header is set several * times. */ private Map> headers = new HashMap<>(); public Optional getBaseUrl() { return baseUrl.map(t -> { try { return new URL(t); } catch (MalformedURLException e) { throw new RuntimeException(e); } }); } @Data public static class AuthorizationProperties { private OAuth2Properties oauth2 = new OAuth2Properties(); private BasicAuthProperties basic = new BasicAuthProperties(); boolean isConfigured() { return oauth2.isConfigured() || basic.isConfigured(); } boolean isConfValid() { return oauth2.isConfValid() && basic.isConfValid() && (!oauth2.isConfigured() || !basic.isConfigured()); } @Data public static class OAuth2Properties { /** *

* If provided, it is used to get an access token from the * {@link OAuth2AuthorizedClientManager}. *

*

* Must reference a valid entry under spring.security.oauth2.client.registration *

*

* Mutually exclusive with forward-bearer property. *

*/ private Optional oauth2RegistrationId = Optional.empty(); /** *

* If true, the access token is taken from the {@link Authentication} in the security * context. *

*

* Mutually exclusive with auth2-registration-id property. *

*/ private boolean forwardBearer = false; boolean isConfigured() { return forwardBearer || oauth2RegistrationId.isPresent(); } boolean isConfValid() { return !forwardBearer || oauth2RegistrationId.isEmpty(); } } @Data public static class BasicAuthProperties { private Optional username = Optional.empty(); private Optional password = Optional.empty(); private Optional charset = Optional.empty(); private Optional encodedCredentials = Optional.empty(); boolean isConfigured() { return encodedCredentials.isPresent() || username.isPresent(); } boolean isConfValid() { return encodedCredentials.isEmpty() || (username.isEmpty() && password.isEmpty()); } } } @Data public static class ClientHttpRequestFactoryProperties { /** *

* Configure Proxy-Authorization header for authentication on a HTTP or SOCKS proxy. This * header auto-configuration can be disable on each client. *

*

* HTTP_PROXY and NO_PROXY standard environment variable are used only if * "com.c4-soft.springaddons.rest.proxy.hostname" is left empty and * "com.c4-soft.springaddons.rest.proxy.enabled" is TRUE or null. In other words, if the * standard environment variables are correctly set, leaving "proxy" properties empty here is * probably the best option. *

*/ private ProxyProperties proxy = new ProxyProperties(); /** * Connection timeout in milliseconds. */ private Optional connectTimeoutMillis = Optional.empty(); /** * Read timeout in milliseconds. */ private Optional readTimeoutMillis = Optional.empty(); @Data public static class ProxyProperties { private boolean enabled = true; private String protocol = "http"; private int port = 8080; private String username; private String password; private int connectTimeoutMillis = 10000; private Optional host = Optional.empty(); private String nonProxyHostsPattern; } } public static enum ClientType { DEFAULT, REST_CLIENT, WEB_CLIENT; } } @Data public static class RestServiceProperties { /** *

* Name of a {@link RestClient} or {@link WebClient} bean. *

* Note that: *
    *
  • This bean does not have to be one of the auto-generated REST clients.
  • *
  • The value is a REST client bean name, not a "com.c4-soft.springaddons.rest.client" * key, which is the ID of for an auto-generated REST client (or builder) bean.
  • *
  • As a reminder, auto-generated REST client beans hare named with a camel-case version of * their ID. For instance "com.c4-soft.springaddons.rest.client.machin-client" properties would * create a bean named "machinClient"
  • *
*/ private String clientBeanName; /** * Fully qualified class name of the {@link HttpExchange} to implement */ private String httpExchangeClass; /** *

* Override the auto-configured bean name which defaults to the camelCase version of the * client-id. *

*/ private Optional beanName = Optional.empty(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy