org.zalando.riptide.compatibility.AsyncHttpOperations Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of riptide-compatibility Show documentation
Show all versions of riptide-compatibility Show documentation
Client side response routing
package org.zalando.riptide.compatibility;
import com.google.common.reflect.TypeToken;
import lombok.AllArgsConstructor;
import org.apiguardian.api.API;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus.Series;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.concurrent.CompletableToListenableFutureAdapter;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.client.AsyncRequestCallback;
import org.springframework.web.client.AsyncRestOperations;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestOperations;
import org.zalando.riptide.Http;
import org.zalando.riptide.RequestArguments.Entity;
import org.zalando.riptide.Route;
import org.zalando.riptide.RoutingTree;
import org.zalando.riptide.capture.Capture;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.net.URI;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import static lombok.AccessLevel.PRIVATE;
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
import static org.springframework.core.ParameterizedTypeReference.forType;
import static org.springframework.http.HttpMethod.DELETE;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.HEAD;
import static org.springframework.http.HttpMethod.OPTIONS;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.HttpMethod.PUT;
import static org.springframework.http.HttpStatus.Series.SUCCESSFUL;
import static org.zalando.riptide.Bindings.anySeries;
import static org.zalando.riptide.Bindings.on;
import static org.zalando.riptide.Navigators.series;
import static org.zalando.riptide.PassRoute.pass;
import static org.zalando.riptide.Route.call;
import static org.zalando.riptide.RoutingTree.dispatch;
import static org.zalando.riptide.Types.responseEntityOf;
import static org.zalando.riptide.compatibility.ExtractRoute.extractTo;
import static org.zalando.riptide.compatibility.UriVariables.extract;
import static org.zalando.riptide.problem.ProblemRoute.problemHandling;
@Nonnull
@API(status = EXPERIMENTAL)
@AllArgsConstructor(access = PRIVATE)
@SuppressWarnings({
"deprecation", // usage of AsyncRestOperations
"UnstableApiUsage", // usage of TypeToken
})
public final class AsyncHttpOperations implements AsyncRestOperations {
private final Http http;
private final RoutingTree defaultRoutingTree;
public AsyncHttpOperations(final Http http) {
this(http, dispatch(series(),
anySeries().call(problemHandling())));
}
public AsyncHttpOperations withDefaultRoutingTree(final RoutingTree defaultRoutingTree) {
return new AsyncHttpOperations(http, defaultRoutingTree);
}
@Override
public RestOperations getRestOperations() {
return new HttpOperations(http);
}
@Override
public ListenableFuture> getForEntity(final String url, final Class responseType,
final Object... uriVariables) {
return exchange(url, GET, null, responseType, uriVariables);
}
@Override
public ListenableFuture> getForEntity(final String url, final Class responseType,
final Map uriVariables) {
return exchange(url, GET, null, responseType, uriVariables);
}
@Override
public ListenableFuture> getForEntity(final URI url, final Class responseType) {
return exchange(url, GET, null, responseType);
}
@Override
public ListenableFuture headForHeaders(final String url, final Object... uriVariables) {
return execute(url, HEAD, pass(), ClientHttpResponse::getHeaders, uriVariables);
}
@Nonnull
@Override
public ListenableFuture headForHeaders(final String url,
final Map uriVariables) {
return execute(url, HEAD, pass(), ClientHttpResponse::getHeaders, extract(url, uriVariables));
}
@Override
public ListenableFuture headForHeaders(final URI url) {
return execute(url, HEAD, pass(), ClientHttpResponse::getHeaders);
}
@Override
public ListenableFuture postForLocation(final String url, @Nullable final HttpEntity> entity,
final Object... uriVariables) {
return execute(url, POST, entity, pass(), response -> response.getHeaders().getLocation(), uriVariables);
}
@Nonnull
@Override
public ListenableFuture postForLocation(final String url, @Nullable final HttpEntity> entity,
final Map uriVariables) {
return execute(url, POST, entity, pass(), response -> response.getHeaders().getLocation(),
extract(url, uriVariables));
}
@Override
public ListenableFuture postForLocation(final URI url, @Nullable final HttpEntity> entity) {
return execute(url, POST, entity, pass(), response -> response.getHeaders().getLocation());
}
@Override
public ListenableFuture> postForEntity(final String url, @Nullable final HttpEntity> entity,
final Class responseType, final Object... uriVariables) {
final Capture> capture = Capture.empty();
return execute(url, POST, entity, call(responseEntityOf(responseType), capture), capture, uriVariables);
}
@Nonnull
@Override
public ListenableFuture> postForEntity(final String url,
@Nullable final HttpEntity> entity, final Class responseType, final Map uriVariables) {
final Capture> capture = Capture.empty();
return execute(url, POST, entity, call(responseEntityOf(responseType), capture), capture, extract(url, uriVariables));
}
@Override
public ListenableFuture> postForEntity(final URI url, @Nullable final HttpEntity> entity,
final Class responseType) {
return exchange(url, POST, entity, responseType);
}
@Override
public ListenableFuture> put(final String url, @Nullable final HttpEntity> entity,
final Object... uriVariables) {
return exchange(url, PUT, entity, Void.class, uriVariables);
}
@Override
public ListenableFuture> put(final String url, @Nullable final HttpEntity> entity,
final Map uriVariables) {
return exchange(url, PUT, entity, Void.class, uriVariables);
}
@Override
public ListenableFuture> put(final URI url, @Nullable final HttpEntity> entity) {
return exchange(url, PUT, entity, Void.class);
}
@Override
public ListenableFuture> delete(final String url, final Object... uriVariables) {
return execute(url, DELETE, pass(), response -> null, uriVariables);
}
@Override
public ListenableFuture> delete(final String url, final Map uriVariables) {
return execute(url, DELETE, pass(), response -> null, extract(url, uriVariables));
}
@Override
public ListenableFuture> delete(final URI url) {
return execute(url, DELETE, pass(), response -> null);
}
@Override
public ListenableFuture> optionsForAllow(final String url, final Object... uriVariables) {
return execute(url, OPTIONS, pass(), response -> response.getHeaders().getAllow(), uriVariables);
}
@Override
public ListenableFuture> optionsForAllow(final String url, final Map uriVariables) {
return execute(url, OPTIONS, pass(), response -> response.getHeaders().getAllow(), extract(url, uriVariables));
}
@Override
public ListenableFuture> optionsForAllow(final URI url) {
return execute(url, OPTIONS, pass(), response -> response.getHeaders().getAllow());
}
@Override
public ListenableFuture> exchange(final String url, final HttpMethod method,
@Nullable final HttpEntity> entity, final Class responseType, final Object... uriVariables) {
return exchange(url, method, entity, forType(responseType), uriVariables);
}
@Override
public ListenableFuture> exchange(final String url, final HttpMethod method,
@Nullable final HttpEntity> entity, final Class responseType, final Map uriVariables) {
return exchange(url, method, entity, forType(responseType), uriVariables);
}
@Override
public ListenableFuture> exchange(final URI url, final HttpMethod method,
@Nullable final HttpEntity> entity, final Class responseType) {
return exchange(url, method, entity, forType(responseType));
}
@Override
public ListenableFuture> exchange(final String url, final HttpMethod method,
@Nullable final HttpEntity> entity, final ParameterizedTypeReference responseType,
final Object... uriVariables) {
@SuppressWarnings("unchecked")
final TypeToken type = (TypeToken) TypeToken.of(responseType.getType());
return exchange(url, method, entity, type, uriVariables);
}
private ListenableFuture> exchange(final String url, final HttpMethod method,
@Nullable final HttpEntity> entity, final TypeToken type, final Object... uriVariables) {
final Capture> capture = Capture.empty();
return execute(url, method, entity, call(responseEntityOf(type), capture), capture, uriVariables);
}
@Override
public ListenableFuture> exchange(final String url, final HttpMethod method,
@Nullable final HttpEntity> entity, final ParameterizedTypeReference responseType,
final Map uriVariables) {
return exchange(url, method, entity, responseType, extract(url, uriVariables));
}
@Override
public ListenableFuture> exchange(final URI url, final HttpMethod method,
@Nullable final HttpEntity> entity, final ParameterizedTypeReference responseType) {
@SuppressWarnings("unchecked")
final TypeToken type = (TypeToken) TypeToken.of(responseType.getType());
return exchange(url, method, entity, type);
}
private ListenableFuture> exchange(final URI url, final HttpMethod method,
@Nullable final HttpEntity> entity, final TypeToken type) {
final Capture> capture = Capture.empty();
return execute(url, method, entity, call(responseEntityOf(type), capture), capture);
}
@Override
public ListenableFuture execute(final String url, final HttpMethod method,
@Nullable final AsyncRequestCallback callback, @Nullable final ResponseExtractor extractor,
final Object... uriVariables) {
final Capture capture = Capture.empty();
return execute(url, method, toEntity(callback), route(extractTo(extractor, capture)), capture, uriVariables);
}
@Nonnull
@Override
public ListenableFuture execute(final String url, final HttpMethod method,
@Nullable final AsyncRequestCallback callback, @Nullable final ResponseExtractor extractor,
final Map uriVariables) {
return execute(url, method, callback, extractor, extract(url, uriVariables));
}
@Nonnull
@Override
public ListenableFuture execute(final URI url, final HttpMethod method,
@Nullable final AsyncRequestCallback callback, @Nullable final ResponseExtractor extractor) {
final Capture capture = Capture.empty();
return execute(url, method, toEntity(callback), route(extractTo(extractor, capture)), capture);
}
private ListenableFuture execute(final String url, final HttpMethod method,
final Route route, final Function function, final Object[] uriVariables) {
return execute(url, method, (HttpEntity) null, route, function, uriVariables);
}
private ListenableFuture execute(final String url, final HttpMethod method,
@Nullable final HttpEntity> entity, final Route route, final Function function,
final Object[] uriVariables) {
final CompletableFuture future = http.execute(method, url, uriVariables)
.headers(getHeaders(entity))
.body(getBody(entity))
.call(route(route))
.thenApply(function);
return new CompletableToListenableFutureAdapter<>(future);
}
private ListenableFuture execute(final String url, final HttpMethod method,
@Nullable final Entity entity, final Route route, final Function function,
final Object[] uriVariables) {
final CompletableFuture future = http.execute(method, url, uriVariables)
.body(entity)
.call(route(route))
.thenApply(function);
return new CompletableToListenableFutureAdapter<>(future);
}
private ListenableFuture execute(final URI url, final HttpMethod method,
final Route route, final Function function) {
return execute(url, method, (HttpEntity) null, route, function);
}
private ListenableFuture execute(final URI url, final HttpMethod method,
@Nullable final HttpEntity> entity, final Route route, final Function function) {
final CompletableFuture future = http.execute(method, url)
.headers(getHeaders(entity))
.body(getBody(entity))
.call(route(route))
.thenApply(function);
return new CompletableToListenableFutureAdapter<>(future);
}
private ListenableFuture execute(final URI url, final HttpMethod method,
@Nullable final Entity entity, final Route route, final Function function) {
final CompletableFuture future = http.execute(method, url)
.body(entity)
.call(route(route))
.thenApply(function);
return new CompletableToListenableFutureAdapter<>(future);
}
private HttpHeaders getHeaders(@Nullable final HttpEntity> entity) {
return entity == null ? HttpHeaders.EMPTY : entity.getHeaders();
}
private Object getBody(@Nullable final HttpEntity> entity) {
return entity == null ? null : entity.getBody();
}
private Route route(final Route route) {
return defaultRoutingTree
.merge(on(SUCCESSFUL).call(route));
}
@Nullable
private Entity toEntity(@Nullable final AsyncRequestCallback callback) {
if (callback == null) {
return null;
}
return message ->
callback.doWithRequest(new HttpOutputMessageAsyncClientHttpRequestAdapter(message));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy