Java.libraries.vertx.ApiClient.mustache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of stackgen Show documentation
Show all versions of stackgen Show documentation
Starter StackGen CORE Service Generator
package {{invokerPackage}};
import {{invokerPackage}}.auth.Authentication;
import {{invokerPackage}}.auth.HttpBasicAuth;
import {{invokerPackage}}.auth.ApiKeyAuth;
import {{invokerPackage}}.auth.OAuth;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
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;
import io.vertx.core.*;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.file.AsyncFile;
import io.vertx.core.file.FileSystem;
import io.vertx.core.file.OpenOptions;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.HttpRequest;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import java.text.DateFormat;
import java.util.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.stream.Collectors.toMap;
{{>generatedAnnotation}}
public class ApiClient {
private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?");
private static final OpenOptions FILE_DOWNLOAD_OPTIONS = new OpenOptions().setCreate(true).setTruncateExisting(true);
private final Vertx vertx;
private final JsonObject config;
private final String identifier;
private MultiMap defaultHeaders = MultiMap.caseInsensitiveMultiMap();
private Map authentications;
private String basePath = "{{{basePath}}}";
private DateFormat dateFormat;
private ObjectMapper objectMapper;
private String downloadsDir = "";
public ApiClient(Vertx vertx, JsonObject config) {
Objects.requireNonNull(vertx, "Vertx must not be null");
Objects.requireNonNull(config, "Config must not be null");
this.vertx = vertx;
// Use RFC3339 format for date and datetime.
// See http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14
this.dateFormat = new RFC3339DateFormat();
// Use UTC as the default time zone.
this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
// Build object mapper
this.objectMapper = new ObjectMapper();
this.objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
this.objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
this.objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
this.objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
this.objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
this.objectMapper.registerModule(new JavaTimeModule());
this.objectMapper.setDateFormat(dateFormat);
// Setup authentications (key: authentication name, value: authentication).
this.authentications = new HashMap<>();{{#authMethods}}{{#isBasic}}
authentications.put("{{name}}", new HttpBasicAuth());{{/isBasic}}{{#isApiKey}}
authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}}
authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}}
// Prevent the authentications from being modified.
this.authentications = Collections.unmodifiableMap(authentications);
// Configurations
this.basePath = config.getString("basePath", this.basePath);
this.downloadsDir = config.getString("downloadsDir", this.downloadsDir);
this.config = config;
this.identifier = UUID.randomUUID().toString();
}
public Vertx getVertx() {
return vertx;
}
public ObjectMapper getObjectMapper() {
return objectMapper;
}
public ApiClient setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
return this;
}
public synchronized WebClient getWebClient() {
String webClientIdentifier = "web-client-" + identifier;
WebClient webClient = Vertx.currentContext().get(webClientIdentifier);
if (webClient == null) {
webClient = buildWebClient(vertx, config);
Vertx.currentContext().put(webClientIdentifier, webClient);
}
return webClient;
}
public String getBasePath() {
return basePath;
}
public ApiClient setBasePath(String basePath) {
this.basePath = basePath;
return this;
}
public String getDownloadsDir() {
return downloadsDir;
}
public ApiClient setDownloadsDir(String downloadsDir) {
this.downloadsDir = downloadsDir;
return this;
}
public MultiMap getDefaultHeaders() {
return defaultHeaders;
}
public ApiClient addDefaultHeader(String key, String value) {
defaultHeaders.add(key, value);
return this;
}
/**
* Get authentications (key: authentication name, value: authentication).
*
* @return Map of authentication object
*/
public Map getAuthentications() {
return authentications;
}
/**
* Get authentication for the given name.
*
* @param authName The authentication name
* @return The authentication, null if not found
*/
public Authentication getAuthentication(String authName) {
return authentications.get(authName);
}
/**
* Helper method to set username for the first HTTP basic authentication.
*
* @param username Username
*/
public ApiClient setUsername(String username) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBasicAuth) {
((HttpBasicAuth) auth).setUsername(username);
return this;
}
}
throw new RuntimeException("No HTTP basic authentication configured!");
}
/**
* Helper method to set password for the first HTTP basic authentication.
*
* @param password Password
*/
public ApiClient setPassword(String password) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBasicAuth) {
((HttpBasicAuth) auth).setPassword(password);
return this;
}
}
throw new RuntimeException("No HTTP basic authentication configured!");
}
/**
* Helper method to set API key value for the first API key authentication.
*
* @param apiKey API key
*/
public ApiClient setApiKey(String apiKey) {
for (Authentication auth : authentications.values()) {
if (auth instanceof ApiKeyAuth) {
((ApiKeyAuth) auth).setApiKey(apiKey);
return this;
}
}
throw new RuntimeException("No API key authentication configured!");
}
/**
* Helper method to set API key prefix for the first API key authentication.
*
* @param apiKeyPrefix API key prefix
*/
public ApiClient setApiKeyPrefix(String apiKeyPrefix) {
for (Authentication auth : authentications.values()) {
if (auth instanceof ApiKeyAuth) {
((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix);
return this;
}
}
throw new RuntimeException("No API key authentication configured!");
}
/**
* Helper method to set access token for the first OAuth2 authentication.
*
* @param accessToken Access token
*/
public ApiClient setAccessToken(String accessToken) {
for (Authentication auth : authentications.values()) {
if (auth instanceof OAuth) {
((OAuth) auth).setAccessToken(accessToken);
return this;
}
}
throw new RuntimeException("No OAuth2 authentication configured!");
}
/**
* Format the given Date object into string.
*
* @param date Date
* @return Date in string format
*/
public String formatDate(Date date) {
return dateFormat.format(date);
}
/**
* Format the given parameter object into string.
*
* @param param Object
* @return Object in string format
*/
public String parameterToString(Object param) {
if (param == null) {
return "";
} else if (param instanceof Date) {
return formatDate((Date) param);
} else if (param instanceof Collection) {
StringBuilder b = new StringBuilder();
for (Object o : (Collection) param) {
if (b.length() > 0) {
b.append(',');
}
b.append(String.valueOf(o));
}
return b.toString();
} else {
return String.valueOf(param);
}
}
/*
* Format to {@code Pair} objects.
* @param collectionFormat Collection format
* @param name Name
* @param value Value
* @return List of pairs
*/
public List parameterToPairs(String collectionFormat, String name, Object value) {
List params = new ArrayList();
// preconditions
if (name == null || name.isEmpty() || value == null) return params;
Collection valueCollection;
if (value instanceof Collection) {
valueCollection = (Collection) value;
} else {
params.add(new Pair(name, parameterToString(value)));
return params;
}
if (valueCollection.isEmpty()) {
return params;
}
// 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)) {
for (Object item : valueCollection) {
params.add(new Pair(name, parameterToString(item)));
}
return params;
}
String delimiter = ",";
if ("csv".equals(format)) {
delimiter = ",";
} else if ("ssv".equals(format)) {
delimiter = " ";
} else if ("tsv".equals(format)) {
delimiter = "\t";
} else if ("pipes".equals(format)) {
delimiter = "|";
}
StringBuilder sb = new StringBuilder();
for (Object item : valueCollection) {
sb.append(delimiter);
sb.append(parameterToString(item));
}
params.add(new Pair(name, sb.substring(1)));
return params;
}
/**
* Check if the given MIME is a JSON MIME.
* JSON MIME examples:
* application/json
* application/json; charset=UTF8
* APPLICATION/JSON
* application/vnd.company+json
*
* @param mime MIME
* @return True if the MIME type is JSON
*/
private boolean isJsonMime(String mime) {
String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$";
return mime != null && (mime.matches(jsonMime) || mime.equalsIgnoreCase("application/json-patch+json"));
}
/**
* Select the Accept header's value from the given accepts array:
* if JSON exists in the given array, use it;
* otherwise use all of them (joining into a string)
*
* @param accepts The accepts array to select from
* @return The Accept header to use. If the given array is empty,
* null will be returned (not to set the Accept header explicitly).
*/
protected String selectHeaderAccept(String[] accepts) {
if (accepts.length == 0) {
return null;
}
for (String accept : accepts) {
if (isJsonMime(accept)) {
return accept;
}
}
return StringUtil.join(accepts, ",");
}
/**
* Select the Content-Type header's value from the given array:
* if JSON exists in the given array, use it;
* otherwise use the first one of the array.
*
* @param contentTypes The Content-Type array to select from
* @return The Content-Type header to use. If the given array is empty,
* JSON will be used.
*/
protected String selectHeaderContentType(String[] contentTypes) {
if (contentTypes.length == 0) {
return "application/json";
}
for (String contentType : contentTypes) {
if (isJsonMime(contentType)) {
return contentType;
}
}
return contentTypes[0];
}
public void sendBody(HttpRequest request,
Handler>> responseHandler,
Object body) {
if (body instanceof byte[]) {
Buffer buffer = Buffer.buffer((byte[]) body);
request.sendBuffer(buffer, responseHandler);
} else if (body instanceof AsyncFile) {
AsyncFile file = (AsyncFile) body;
request.sendStream(file, responseHandler);
} else {
request.sendJson(body, responseHandler);
}
}
/**
* Invoke API by sending HTTP request with the given options.
*
* @param Type
* @param path The sub-path of the HTTP URL
* @param method The request method, one of "GET", "POST", "PUT", "HEAD" and "DELETE"
* @param queryParams The query parameters
* @param body The request body object
* @param headerParams The header parameters
* @param formParams The form parameters
* @param accepts The request's Accept headers
* @param contentTypes The request's Content-Type headers
* @param authNames The authentications to apply
* @param returnType The return type into which to deserialize the response
* @param resultHandler The asynchronous response handler
*/
public void invokeAPI(String path, String method, List queryParams, Object body, MultiMap headerParams,
Map formParams, String[] accepts, String[] contentTypes, String[] authNames,
TypeReference returnType, Handler> resultHandler) {
updateParamsForAuth(authNames, queryParams, headerParams);
if (accepts != null) {
headerParams.add(HttpHeaders.ACCEPT, selectHeaderAccept(accepts));
}
if (contentTypes != null) {
headerParams.add(HttpHeaders.CONTENT_TYPE, selectHeaderContentType(contentTypes));
}
HttpMethod httpMethod = HttpMethod.valueOf(method);
HttpRequest request = getWebClient().requestAbs(httpMethod, basePath + path);
if (httpMethod == HttpMethod.PATCH) {
request.putHeader("X-HTTP-Method-Override", "PATCH");
}
queryParams.forEach(entry -> {
if (entry.getValue() != null) {
request.addQueryParam(entry.getName(), entry.getValue());
}
});
headerParams.forEach(entry -> {
if (entry.getValue() != null) {
request.putHeader(entry.getKey(), entry.getValue());
}
});
defaultHeaders.forEach(entry -> {
if (entry.getValue() != null) {
request.putHeader(entry.getKey(), entry.getValue());
}
});
Handler>> responseHandler = buildResponseHandler(returnType, resultHandler);
if (body != null) {
sendBody(request, responseHandler, body);
} else if (formParams != null && !formParams.isEmpty()) {
Map formMap = formParams.entrySet().stream().collect(toMap(Map.Entry::getKey, entry -> parameterToString(entry.getValue())));
MultiMap form = MultiMap.caseInsensitiveMultiMap().addAll(formMap);
request.sendForm(form, responseHandler);
} else {
request.send(responseHandler);
}
}
/**
* Sanitize filename by removing path.
* e.g. ../../sun.gif becomes sun.gif
*
* @param filename The filename to be sanitized
* @return The sanitized filename
*/
protected String sanitizeFilename(String filename) {
return filename.replaceAll(".*[/\\\\]", "");
}
/**
* Create a filename from the given headers.
* When the headers have no "Content-Disposition" information, a random UUID name is generated.
*
* @param headers The HTTP response headers
* @return The filename
*/
protected String generateFilename(MultiMap headers) {
String filename = UUID.randomUUID().toString();
String contentDisposition = headers.get("Content-Disposition");
if (contentDisposition != null && !contentDisposition.isEmpty()) {
Matcher matcher = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
if (matcher.find()) {
filename = sanitizeFilename(matcher.group(1));
}
}
return filename;
}
/**
* File Download handling.
*
* @param response The HTTP response
* @param handler The response handler
*/
protected void handleFileDownload(HttpResponse response, Handler> handler) {
FileSystem fs = getVertx().fileSystem();
String filename = generateFilename(response.headers());
Consumer fileHandler = directory -> {
fs.open(directory + filename, FILE_DOWNLOAD_OPTIONS, asyncFileResult -> {
if (asyncFileResult.succeeded()) {
AsyncFile asyncFile = asyncFileResult.result();
asyncFile.write(response.bodyAsBuffer());
//noinspection unchecked
handler.handle(Future.succeededFuture((T) asyncFile));
} else {
handler.handle(ApiException.fail(asyncFileResult.cause()));
}
});
};
String dir = getDownloadsDir();
if (dir != null && !dir.isEmpty()) {
fs.mkdirs(dir, mkdirResult -> {
String sanitizedFolder = dir.endsWith("/") ? dir : dir + "/";
fileHandler.accept(sanitizedFolder);
});
} else {
fileHandler.accept("");
}
}
/**
* Build a response handler for the HttpResponse.
*
* @param returnType The return type
* @param handler The response handler
* @return The HTTP response handler
*/
protected Handler>> buildResponseHandler(TypeReference returnType,
Handler> handler) {
return response -> {
AsyncResult result;
if (response.succeeded()) {
HttpResponse httpResponse = response.result();
if (httpResponse.statusCode() / 100 == 2) {
if (httpResponse.statusCode() == 204 || returnType == null) {
result = Future.succeededFuture(null);
} else {
T resultContent;
if ("byte[]".equals(returnType.getType().toString())) {
resultContent = (T) httpResponse.body().getBytes();
} else if (AsyncFile.class.equals(returnType.getType())) {
handleFileDownload(httpResponse, handler);
return;
} else {
resultContent = Json.decodeValue(httpResponse.body(), returnType);
}
result = Future.succeededFuture(resultContent);
}
} else {
result = ApiException.fail(httpResponse.statusMessage(), httpResponse.statusCode(), httpResponse.headers(), httpResponse.bodyAsString());
}
} else if (response.cause() instanceof ApiException) {
result = Future.failedFuture(response.cause());
} else {
result = ApiException.fail(500, response.cause() != null ? response.cause().getMessage() : null);
}
handler.handle(result);
};
}
/**
* Build the WebClient used to make HTTP requests.
*
* @param vertx Vertx
* @return WebClient
*/
protected WebClient buildWebClient(Vertx vertx, JsonObject config) {
if (!config.containsKey("userAgent")) {
config.put("userAgent", "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{artifactVersion}}}/java{{/httpUserAgent}}");
}
return WebClient.create(vertx, new WebClientOptions(config));
}
/**
* Update query and header parameters based on authentication settings.
*
* @param authNames The authentications to apply
*/
protected void updateParamsForAuth(String[] authNames, List queryParams, MultiMap headerParams) {
for (String authName : authNames) {
Authentication auth = authentications.get(authName);
if (auth == null) throw new RuntimeException("Authentication undefined: " + authName);
auth.applyToParams(queryParams, headerParams);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy