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

reactivefeign.rx3.Rx3ReactiveFeign Maven / Gradle / Ivy

package reactivefeign.rx3;

import feign.Contract;
import feign.MethodMetadata;
import io.reactivex.rxjava3.core.*;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.web.reactive.function.client.WebClient;
import reactivefeign.ReactiveOptions;
import reactivefeign.client.ReactiveHttpClient;
import reactivefeign.client.ReactiveHttpClientFactory;
import reactivefeign.client.ReactiveHttpRequestInterceptor;
import reactivefeign.methodhandler.MethodHandlerFactory;
import reactivefeign.publisher.FluxPublisherHttpClient;
import reactivefeign.publisher.MonoPublisherHttpClient;
import reactivefeign.publisher.PublisherClientFactory;
import reactivefeign.publisher.PublisherHttpClient;
import reactivefeign.retry.ReactiveRetryPolicy;
import reactivefeign.rx3.client.statushandler.Rx3ReactiveStatusHandler;
import reactivefeign.rx3.client.statushandler.Rx3StatusHandler;
import reactivefeign.rx3.methodhandler.Rx3MethodHandlerFactory;
import reactivefeign.webclient.WebClientFeignCustomizer;
import reactivefeign.webclient.WebReactiveFeign;
import reactivefeign.webclient.client.WebReactiveHttpClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import static feign.Util.resolveLastTypeParameter;
import static java.util.Optional.ofNullable;
import static reactivefeign.utils.FeignUtils.getBodyActualType;
import static reactivefeign.utils.FeignUtils.returnPublisherType;
import static reactivefeign.webclient.client.WebReactiveHttpClient.webReactiveHttpResponse;

/**
 * @author Sergii Karpenko
 */
public final class Rx3ReactiveFeign {

    private Rx3ReactiveFeign(){}

    public static  Builder builder() {
        return new Builder<>(WebClient.builder());
    }

    public static  Builder builder(WebClient.Builder webClientBuilder) {
        return new Builder<>(webClientBuilder);
    }

    public static  Builder builder(WebClient.Builder webClientBuilder, WebClientFeignCustomizer webClientCustomizer) {
        return new Builder<>(webClientBuilder, webClientCustomizer);
    }

    public static class Builder extends WebReactiveFeign.Builder {

        private BackpressureStrategy backpressureStrategy;

        protected Builder(WebClient.Builder webClientBuilder) {
            super(webClientBuilder);
        }

        protected Builder(WebClient.Builder webClientBuilder, WebClientFeignCustomizer webClientCustomizer) {
            super(webClientBuilder, webClientCustomizer);
        }

        /**
         * Used to convert {@link Observable}  into {@link Flux}
         * @param backpressureStrategy
         */
        public void setBackpressureStrategy(BackpressureStrategy backpressureStrategy) {
            this.backpressureStrategy = backpressureStrategy;
        }

        @Override
        public MethodHandlerFactory buildReactiveMethodHandlerFactory(PublisherClientFactory publisherClientFactory) {
            return new Rx3MethodHandlerFactory(publisherClientFactory, backpressureStrategy);
        }

        @Override
        public Builder contract(final Contract contract) {
            this.contract = new Rx3Contract(contract);
            return this;
        }

        @Override
        public Builder addRequestInterceptor(ReactiveHttpRequestInterceptor requestInterceptor) {
            super.addRequestInterceptor(requestInterceptor);
            return this;
        }

        @Override
        public Builder decode404() {
            super.decode404();
            return this;
        }

        public Builder statusHandler(Rx3StatusHandler statusHandler) {
            super.statusHandler(new Rx3ReactiveStatusHandler(statusHandler));
            return this;
        }

        @Override
        public Builder retryWhen(ReactiveRetryPolicy retryPolicy){
            super.retryWhen(retryPolicy);
            return this;
        }

        @Override
        public Builder options(final ReactiveOptions options) {
            super.options(options);
            return this;
        }

        protected PublisherHttpClient toPublisher(ReactiveHttpClient reactiveHttpClient, MethodMetadata methodMetadata){
            Type returnType = returnPublisherType(methodMetadata);
            if(returnType == Single.class || returnType == Maybe.class){
                return new MonoPublisherHttpClient(reactiveHttpClient);
            } else if(returnType == Flowable.class || returnType == Observable.class){
                return new FluxPublisherHttpClient(reactiveHttpClient);
            } else {
                throw new IllegalArgumentException("Unknown returnType: " + returnType);
            }
        }

        @Override
        protected ReactiveHttpClientFactory clientFactory(){
            this.webClientBuilder.clientConnector(clientConnector());

            if(webClientCustomizer != null){
                webClientCustomizer.accept(webClientBuilder);
            }

            return methodMetadata -> webClient(methodMetadata, webClientBuilder.build());
        }

        public WebReactiveHttpClient webClient(MethodMetadata methodMetadata, WebClient webClient) {

            final Type returnType = methodMetadata.returnType();
            Type returnPublisherType = ((ParameterizedType) returnType).getRawType();
            ParameterizedTypeReference returnActualType = ParameterizedTypeReference.forType(
                    resolveLastTypeParameter(returnType, (Class) returnPublisherType));
            ParameterizedTypeReference bodyActualType = ofNullable(
                    getBodyActualType(methodMetadata.bodyType()))
                    .map(type -> ParameterizedTypeReference.forType(type))
                    .orElse(null);

            return new WebReactiveHttpClient(webClient, bodyActualType,
                    webReactiveHttpResponse(rx2ToReactor(returnPublisherType), returnActualType),
                    errorMapper());
        }

        private static Class rx2ToReactor(Type type){
            if(type == Flowable.class){
                return Flux.class;
            } else if(type == Observable.class){
                return Flux.class;
            } else if(type == Single.class){
                return Mono.class;
            } else if(type == Maybe.class){
                return Mono.class;
            } else {
                throw new IllegalArgumentException("Unexpected type="+type);
            }
        }
    }

}