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

com.github.ljtfreitas.julian.http.spring.webflux.WebClientHTTPRequest Maven / Gradle / Ivy

/*
 * Copyright (C) 2021 Tiago de Freitas Lima
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.github.ljtfreitas.julian.http.spring.webflux;

import com.github.ljtfreitas.julian.JavaType;
import com.github.ljtfreitas.julian.Promise;
import com.github.ljtfreitas.julian.http.FailureHTTPResponse;
import com.github.ljtfreitas.julian.http.HTTPException;
import com.github.ljtfreitas.julian.http.HTTPFailureResponseException;
import com.github.ljtfreitas.julian.http.HTTPHeader;
import com.github.ljtfreitas.julian.http.HTTPHeaders;
import com.github.ljtfreitas.julian.http.HTTPRequestIO;
import com.github.ljtfreitas.julian.http.HTTPResponse;
import com.github.ljtfreitas.julian.http.HTTPResponseException;
import com.github.ljtfreitas.julian.http.HTTPStatus;
import com.github.ljtfreitas.julian.http.HTTPStatusCode;
import com.github.ljtfreitas.julian.http.HTTPUnknownFailureResponseException;
import com.github.ljtfreitas.julian.http.client.HTTPClientException;
import com.github.ljtfreitas.julian.http.codec.HTTPMessageException;
import com.github.ljtfreitas.julian.reactor.MonoPromise;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.UnsupportedMediaTypeException;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientException;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URI;

class WebClientHTTPRequest implements HTTPRequestIO {

    private final URI path;
    private final HttpMethod method;
    private final HttpHeaders headers;
    private final BodyInserter body;
    private final JavaType responseType;
    private final WebClient webClient;

    public WebClientHTTPRequest(URI path, HttpMethod method, HttpHeaders headers, BodyInserter body, JavaType responseType, WebClient webClient) {
        this.path = path;
        this.method = method;
        this.headers = headers;
        this.body = body;
        this.responseType = responseType;
        this.webClient = webClient;
    }

    @Override
    public Promise> execute() {
        Mono> mono = webClient.method(method)
                .uri(path)
                .headers(h -> headers.forEach(h::addAll))
                .body(body)
                .exchangeToMono(this::read);

        return new MonoPromise<>(mono.onErrorMap(this::exceptionally));
    }

    Mono> read(ClientResponse response) {
        HTTPStatus status = new HTTPStatus(response.rawStatusCode(), response.statusCode().getReasonPhrase());

        HTTPHeaders headers = response.headers().asHttpHeaders().entrySet().stream()
                .reduce(HTTPHeaders.empty(), (h, e) -> h.join(new HTTPHeader(e.getKey(), e.getValue())), (a, b) -> b);

        if (response.statusCode().isError()) {
            return response.createException().map(e -> failure(status, headers, e));

        } else {
            return success(status, headers, response);
        }
    }

    private HTTPResponse failure(HTTPStatus status, HTTPHeaders headers, WebClientResponseException e) {
        HTTPResponseException exception = HTTPStatusCode.select(status.code())
                .map(httpStatusCode -> HTTPFailureResponseException.create(httpStatusCode, headers, e.getResponseBodyAsByteArray()))
                .orElseGet(() -> new HTTPUnknownFailureResponseException(status, headers, e.getResponseBodyAsByteArray()));

        return new FailureHTTPResponse<>(exception);
    }

    private HTTPException exceptionally(Throwable e) {
        if (e instanceof HTTPException) {
            return (HTTPException) e;

        } else if (e instanceof IOException) {
            return new HTTPClientException("I/O error: [" + method + " " + path + "]", e);

        } else if (e instanceof UnsupportedMediaTypeException) {
            return new HTTPMessageException("WebClient error: [" + method + " " + path + "]", e);

        } else if (e instanceof WebClientRequestException) {
            return new HTTPClientException("WebClient error: [" + method + " " + path + "]", e);

        } else if (e instanceof WebClientException) {
            return new HTTPClientException("WebClient error: [" + method + " " + path + "]", e);

        } else {
            return new HTTPException("Error on HTTP request: [" + method + " " + path + "]", e);
        }
    }

    private Mono> success(HTTPStatus status, HTTPHeaders headers, ClientResponse response) {
        ParameterizedTypeReference expectedType = new ParameterizedTypeReference<>() {
            @Override
            public Type getType() {
                return responseType.get();
            }
        };

        return response.bodyToMono(expectedType)
                .map(value -> HTTPResponse.success(status, headers, value))
                .switchIfEmpty(Mono.just(HTTPResponse.empty(status, headers)));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy