Java.libraries.native.ApiClient.mustache Maven / Gradle / Ivy
{{>licenseInfo}}
package {{invokerPackage}};
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
{{#openApiNullable}}
import org.openapitools.jackson.nullable.JsonNullableModule;
{{/openApiNullable}}
import java.io.InputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpConnectTimeoutException;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Configuration and utility class for API clients.
*
* This class can be constructed and modified, then used to instantiate the
* various API classes. The API classes use the settings in this class to
* configure themselves, but otherwise do not store a link to this class.
*
* This class is mutable and not synchronized, so it is not thread-safe.
* The API classes generated from this are immutable and thread-safe.
*
* The setter methods of this class return the current object to facilitate
* a fluent style of configuration.
*/
{{>generatedAnnotation}}
public class ApiClient {
private HttpClient.Builder builder;
private ObjectMapper mapper;
private String scheme;
private String host;
private int port;
private String basePath;
private Consumer interceptor;
private Consumer> responseInterceptor;
private Consumer> asyncResponseInterceptor;
private Duration readTimeout;
private Duration connectTimeout;
public static String valueToString(Object value) {
if (value == null) {
return "";
}
if (value instanceof OffsetDateTime) {
return ((OffsetDateTime) value).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}
return value.toString();
}
/**
* URL encode a string in the UTF-8 encoding.
*
* @param s String to encode.
* @return URL-encoded representation of the input string.
*/
public static String urlEncode(String s) {
return URLEncoder.encode(s, UTF_8).replaceAll("\\+", "%20");
}
/**
* Convert a URL query name/value parameter to a list of encoded {@link Pair}
* objects.
*
* The value can be null, in which case an empty list is returned.
*
* @param name The query name parameter.
* @param value The query value, which may not be a collection but may be
* null.
* @return A singleton list of the {@link Pair} objects representing the input
* parameters, which is encoded for use in a URL. If the value is null, an
* empty list is returned.
*/
public static List parameterToPairs(String name, Object value) {
if (name == null || name.isEmpty() || value == null) {
return Collections.emptyList();
}
return Collections.singletonList(new Pair(urlEncode(name), urlEncode(valueToString(value))));
}
/**
* Convert a URL query name/collection parameter to a list of encoded
* {@link Pair} objects.
*
* @param collectionFormat The swagger collectionFormat string (csv, tsv, etc).
* @param name The query name parameter.
* @param values A collection of values for the given query name, which may be
* null.
* @return A list of {@link Pair} objects representing the input parameters,
* which is encoded for use in a URL. If the values collection is null, an
* empty list is returned.
*/
public static List parameterToPairs(
String collectionFormat, String name, Collection> values) {
if (name == null || name.isEmpty() || values == null || values.isEmpty()) {
return Collections.emptyList();
}
// get the collection format (default: csv)
String format = collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat;
// create the params based on the collection format
if ("multi".equals(format)) {
return values.stream()
.map(value -> new Pair(urlEncode(name), urlEncode(valueToString(value))))
.collect(Collectors.toList());
}
String delimiter;
switch(format) {
case "csv":
delimiter = urlEncode(",");
break;
case "ssv":
delimiter = urlEncode(" ");
break;
case "tsv":
delimiter = urlEncode("\t");
break;
case "pipes":
delimiter = urlEncode("|");
break;
default:
throw new IllegalArgumentException("Illegal collection format: " + collectionFormat);
}
StringJoiner joiner = new StringJoiner(delimiter);
for (Object value : values) {
joiner.add(urlEncode(valueToString(value)));
}
return Collections.singletonList(new Pair(urlEncode(name), joiner.toString()));
}
/**
* Create an instance of ApiClient.
*/
public ApiClient() {
this.builder = createDefaultHttpClientBuilder();
this.mapper = createDefaultObjectMapper();
updateBaseUri(getDefaultBaseUri());
interceptor = null;
readTimeout = null;
connectTimeout = null;
responseInterceptor = null;
asyncResponseInterceptor = null;
}
/**
* Create an instance of ApiClient.
*
* @param builder Http client builder.
* @param mapper Object mapper.
* @param baseUri Base URI
*/
public ApiClient(HttpClient.Builder builder, ObjectMapper mapper, String baseUri) {
this.builder = builder;
this.mapper = mapper;
updateBaseUri(baseUri != null ? baseUri : getDefaultBaseUri());
interceptor = null;
readTimeout = null;
connectTimeout = null;
responseInterceptor = null;
asyncResponseInterceptor = null;
}
protected ObjectMapper createDefaultObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
mapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
mapper.registerModule(new JavaTimeModule());
{{#openApiNullable}}
mapper.registerModule(new JsonNullableModule());
{{/openApiNullable}}
return mapper;
}
protected String getDefaultBaseUri() {
return "{{{basePath}}}";
}
protected HttpClient.Builder createDefaultHttpClientBuilder() {
return HttpClient.newBuilder();
}
public void updateBaseUri(String baseUri) {
URI uri = URI.create(baseUri);
scheme = uri.getScheme();
host = uri.getHost();
port = uri.getPort();
basePath = uri.getRawPath();
}
/**
* Set a custom {@link HttpClient.Builder} object to use when creating the
* {@link HttpClient} that is used by the API client.
*
* @param builder Custom client builder.
* @return This object.
*/
public ApiClient setHttpClientBuilder(HttpClient.Builder builder) {
this.builder = builder;
return this;
}
/**
* Get an {@link HttpClient} based on the current {@link HttpClient.Builder}.
*
* The returned object is immutable and thread-safe.
*
* @return The HTTP client.
*/
public HttpClient getHttpClient() {
return builder.build();
}
/**
* Set a custom {@link ObjectMapper} to serialize and deserialize the request
* and response bodies.
*
* @param mapper Custom object mapper.
* @return This object.
*/
public ApiClient setObjectMapper(ObjectMapper mapper) {
this.mapper = mapper;
return this;
}
/**
* Get a copy of the current {@link ObjectMapper}.
*
* @return A copy of the current object mapper.
*/
public ObjectMapper getObjectMapper() {
return mapper.copy();
}
/**
* Set a custom host name for the target service.
*
* @param host The host name of the target service.
* @return This object.
*/
public ApiClient setHost(String host) {
this.host = host;
return this;
}
/**
* Set a custom port number for the target service.
*
* @param port The port of the target service. Set this to -1 to reset the
* value to the default for the scheme.
* @return This object.
*/
public ApiClient setPort(int port) {
this.port = port;
return this;
}
/**
* Set a custom base path for the target service, for example '/v2'.
*
* @param basePath The base path against which the rest of the path is
* resolved.
* @return This object.
*/
public ApiClient setBasePath(String basePath) {
this.basePath = basePath;
return this;
}
/**
* Get the base URI to resolve the endpoint paths against.
*
* @return The complete base URI that the rest of the API parameters are
* resolved against.
*/
public String getBaseUri() {
return scheme + "://" + host + (port == -1 ? "" : ":" + port) + basePath;
}
/**
* Set a custom scheme for the target service, for example 'https'.
*
* @param scheme The scheme of the target service
* @return This object.
*/
public ApiClient setScheme(String scheme){
this.scheme = scheme;
return this;
}
/**
* Set a custom request interceptor.
*
* A request interceptor is a mechanism for altering each request before it
* is sent. After the request has been fully configured but not yet built, the
* request builder is passed into this function for further modification,
* after which it is sent out.
*
* This is useful for altering the requests in a custom manner, such as
* adding headers. It could also be used for logging and monitoring.
*
* @param interceptor A function invoked before creating each request. A value
* of null resets the interceptor to a no-op.
* @return This object.
*/
public ApiClient setRequestInterceptor(Consumer interceptor) {
this.interceptor = interceptor;
return this;
}
/**
* Get the custom interceptor.
*
* @return The custom interceptor that was set, or null if there isn't any.
*/
public Consumer getRequestInterceptor() {
return interceptor;
}
/**
* Set a custom response interceptor.
*
* This is useful for logging, monitoring or extraction of header variables
*
* @param interceptor A function invoked before creating each request. A value
* of null resets the interceptor to a no-op.
* @return This object.
*/
public ApiClient setResponseInterceptor(Consumer> interceptor) {
this.responseInterceptor = interceptor;
return this;
}
/**
* Get the custom response interceptor.
*
* @return The custom interceptor that was set, or null if there isn't any.
*/
public Consumer> getResponseInterceptor() {
return responseInterceptor;
}
/**
* Set a custom async response interceptor. Use this interceptor when asyncNative is set to 'true'.
*
* This is useful for logging, monitoring or extraction of header variables
*
* @param interceptor A function invoked before creating each request. A value
* of null resets the interceptor to a no-op.
* @return This object.
*/
public ApiClient setAsyncResponseInterceptor(Consumer> interceptor) {
this.asyncResponseInterceptor = interceptor;
return this;
}
/**
* Get the custom async response interceptor. Use this interceptor when asyncNative is set to 'true'.
*
* @return The custom interceptor that was set, or null if there isn't any.
*/
public Consumer> getAsyncResponseInterceptor() {
return asyncResponseInterceptor;
}
/**
* Set the read timeout for the http client.
*
* This is the value used by default for each request, though it can be
* overridden on a per-request basis with a request interceptor.
*
* @param readTimeout The read timeout used by default by the http client.
* Setting this value to null resets the timeout to an
* effectively infinite value.
* @return This object.
*/
public ApiClient setReadTimeout(Duration readTimeout) {
this.readTimeout = readTimeout;
return this;
}
/**
* Get the read timeout that was set.
*
* @return The read timeout, or null if no timeout was set. Null represents
* an infinite wait time.
*/
public Duration getReadTimeout() {
return readTimeout;
}
/**
* Sets the connect timeout (in milliseconds) for the http client.
*
* In the case where a new connection needs to be established, if
* the connection cannot be established within the given {@code
* duration}, then {@link HttpClient#send(HttpRequest,BodyHandler)
* HttpClient::send} throws an {@link HttpConnectTimeoutException}, or
* {@link HttpClient#sendAsync(HttpRequest,BodyHandler)
* HttpClient::sendAsync} completes exceptionally with an
* {@code HttpConnectTimeoutException}. If a new connection does not
* need to be established, for example if a connection can be reused
* from a previous request, then this timeout duration has no effect.
*
* @param connectTimeout connection timeout in milliseconds
*
* @return This object.
*/
public ApiClient setConnectTimeout(Duration connectTimeout) {
this.connectTimeout = connectTimeout;
this.builder.connectTimeout(connectTimeout);
return this;
}
/**
* Get connection timeout (in milliseconds).
*
* @return Timeout in milliseconds
*/
public Duration getConnectTimeout() {
return connectTimeout;
}
}