fr.ouestfrance.querydsl.postgrest.PostgrestWebClient Maven / Gradle / Ivy
package fr.ouestfrance.querydsl.postgrest;
import fr.ouestfrance.querydsl.postgrest.model.BulkResponse;
import fr.ouestfrance.querydsl.postgrest.model.CountItem;
import fr.ouestfrance.querydsl.postgrest.model.HeaderRange;
import fr.ouestfrance.querydsl.postgrest.model.RangeResponse;
import fr.ouestfrance.querydsl.postgrest.model.exceptions.PostgrestRequestException;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static fr.ouestfrance.querydsl.postgrest.ParametrizedTypeUtils.listRef;
import static fr.ouestfrance.querydsl.postgrest.ResponseUtils.toBulkResponse;
/**
* Rest interface for querying postgrest
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class PostgrestWebClient implements PostgrestClient {
/**
* Content type
*/
private static final String CONTENT_TYPE = "Content-Type";
/**
* webClient
*/
private final WebClient webClient;
/**
* Postgrest webclient adapter
*
* @param webClient webClient
* @return PostgrestWebClient implementation
*/
public static PostgrestWebClient of(WebClient webClient) {
return new PostgrestWebClient(webClient);
}
@Override
public RangeResponse search(String resource, Map> params,
Map> headers, Class clazz) {
ResponseEntity> response = webClient.get().uri(uriBuilder -> {
uriBuilder.path(resource);
uriBuilder.queryParams(toMultiMap(params));
return uriBuilder.build();
}).headers(httpHeaders ->
safeAdd(headers, httpHeaders)
)
.retrieve()
.toEntity(listRef(clazz))
.block();
// Retrieve result headers
return Optional.ofNullable(response)
.map(HttpEntity::getBody)
.map(x -> {
HeaderRange range = ResponseUtils.getCount(response.getHeaders())
.orElse(null);
return new RangeResponse<>(x, range);
}).orElse(new RangeResponse<>(List.of(), null));
}
@Override
public List count(String resource, Map> params) {
ResponseEntity> response = webClient.get().uri(uriBuilder -> {
uriBuilder.path(resource);
uriBuilder.queryParams(toMultiMap(params));
return uriBuilder.build();
})
.retrieve()
.toEntity(listRef(CountItem.class))
.block();
return Optional.ofNullable(response).map(HttpEntity::getBody).orElse(List.of());
}
@Override
public V rpc(String rpcName, Map> params, Object body, Type clazz) {
WebClient.RequestBodySpec request = webClient.post().uri(uriBuilder -> {
uriBuilder.path(rpcName);
uriBuilder.queryParams(toMultiMap(params));
return uriBuilder.build();
});
Optional.ofNullable(body).ifPresent(request::bodyValue);
Object result = request
.retrieve()
.bodyToMono(ParameterizedTypeReference.forType(clazz))
// On not found raise postgrestRequestException with body
.onErrorMap(WebClientResponseException.NotFound.class, e -> new PostgrestRequestException(rpcName, e.getMessage(), e, e.getResponseBodyAsString()))
.block();
if (result != null) {
return (V) result;
}
return null;
}
@Override
public BulkResponse post(String resource, Map> params, Object value, Map> headers, Class clazz) {
ResponseEntity> response = webClient.post().uri(uriBuilder -> {
uriBuilder.path(resource);
uriBuilder.queryParams(toMultiMap(params));
return uriBuilder.build();
}).headers(httpHeaders -> safeAdd(headers, httpHeaders))
.bodyValue(value)
.retrieve()
.toEntity(listRef(clazz))
.block();
return toBulkResponse(response);
}
@Override
public BulkResponse patch(String resource, Map> params, Object value, Map> headers, Class clazz) {
ResponseEntity> response = webClient.patch().uri(uriBuilder -> {
uriBuilder.path(resource);
uriBuilder.queryParams(toMultiMap(params));
return uriBuilder.build();
})
.bodyValue(value)
.headers(httpHeaders -> safeAdd(headers, httpHeaders))
.retrieve()
.toEntity(listRef(clazz)).block();
return toBulkResponse(response);
}
@Override
public BulkResponse delete(String resource, Map> params, Map> headers, Class clazz) {
ResponseEntity> response = webClient.delete().uri(uriBuilder -> {
uriBuilder.path(resource);
uriBuilder.queryParams(toMultiMap(params));
return uriBuilder.build();
})
.headers(httpHeaders -> safeAdd(headers, httpHeaders))
.retrieve()
.toEntity(listRef(clazz)).block();
return toBulkResponse(response);
}
/**
* Convert map to MultiValueMap
*
* @param params map
* @return MultiValueMap
*/
private static MultiValueMap toMultiMap(Map> params) {
return new LinkedMultiValueMap<>(params);
}
/**
* Safe add headers to httpHeaders
*
* @param headers headers
* @param httpHeaders httpHeaders
*/
private static void safeAdd(Map> headers, HttpHeaders httpHeaders) {
Optional.ofNullable(headers)
.map(PostgrestWebClient::toMultiMap).ifPresent(httpHeaders::addAll);
// Add contentType with default on call if webclient default is not set
httpHeaders.put(CONTENT_TYPE, List.of(MediaType.APPLICATION_JSON_VALUE));
}
}