diff --git a/bin/java-petstore-okhttp-gson.json b/bin/java-petstore-okhttp-gson.json new file mode 100644 index 00000000000..b894d172630 --- /dev/null +++ b/bin/java-petstore-okhttp-gson.json @@ -0,0 +1,4 @@ +{ + "library": "okhttp-gson", + "artifactId": "swagger-petstore-okhttp-gson" +} diff --git a/bin/java-petstore-okhttp-gson.sh b/bin/java-petstore-okhttp-gson.sh new file mode 100755 index 00000000000..17a48ddee53 --- /dev/null +++ b/bin/java-petstore-okhttp-gson.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +while [ -h "$SCRIPT" ] ; do + ls=`ls -ld "$SCRIPT"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + SCRIPT="$link" + else + SCRIPT=`dirname "$SCRIPT"`/"$link" + fi +done + +if [ ! -d "${APP_DIR}" ]; then + APP_DIR=`dirname "$SCRIPT"`/.. + APP_DIR=`cd "${APP_DIR}"; pwd` +fi + +executable="./modules/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn clean package +fi + +# if you've executed sbt assembly previously it will use that instead. +export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.json -l java -c bin/java-petstore-okhttp-gson.json -o samples/client/petstore/java/okhttp-gson" + +java $JAVA_OPTS -jar $executable $ags diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java index aad744dd1ce..eab4dffa8e3 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java @@ -83,6 +83,7 @@ public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig { supportedLibraries.put("", "HTTP client: Jersey client 1.18. JSON processing: Jackson 2.4.2"); supportedLibraries.put("jersey2", "HTTP client: Jersey client 2.6"); + supportedLibraries.put("okhttp-gson", "HTTP client: OkHttp 2.4.0. JSON processing: Gson 2.3.1"); cliOptions.add(buildLibraryCliOption(supportedLibraries)); } @@ -159,7 +160,15 @@ public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig { supportingFiles.add(new SupportingFile("JSON.mustache", invokerFolder, "JSON.java")); supportingFiles.add(new SupportingFile("Pair.mustache", invokerFolder, "Pair.java")); supportingFiles.add(new SupportingFile("StringUtil.mustache", invokerFolder, "StringUtil.java")); - supportingFiles.add(new SupportingFile("TypeRef.mustache", invokerFolder, "TypeRef.java")); + + // library-specific files + if ("okhttp-gson".equals(getLibrary())) { + // the "okhttp-gson" library template requires "ApiCallback.mustache" + // and does not require "TypeRef.mustache" + supportingFiles.add(new SupportingFile("ApiCallback.mustache", invokerFolder, "ApiCallback.java")); + } else { + supportingFiles.add(new SupportingFile("TypeRef.mustache", invokerFolder, "TypeRef.java")); + } final String authFolder = (sourceFolder + File.separator + invokerPackage + ".auth").replace(".", File.separator); supportingFiles.add(new SupportingFile("auth/Authentication.mustache", authFolder, "Authentication.java")); diff --git a/modules/swagger-codegen/src/main/resources/Java/StringUtil.mustache b/modules/swagger-codegen/src/main/resources/Java/StringUtil.mustache index 073966b0c21..c9583f1bc63 100644 --- a/modules/swagger-codegen/src/main/resources/Java/StringUtil.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/StringUtil.mustache @@ -39,4 +39,13 @@ public class StringUtil { } return out.toString(); } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + public static String toIndentedString(Object o) { + if (o == null) return "null"; + return o.toString().replace("\n", "\n "); + } } diff --git a/modules/swagger-codegen/src/main/resources/Java/apiException.mustache b/modules/swagger-codegen/src/main/resources/Java/apiException.mustache index bb3b53b0aeb..b9a62a6b231 100644 --- a/modules/swagger-codegen/src/main/resources/Java/apiException.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/apiException.mustache @@ -6,20 +6,45 @@ import java.util.List; {{>generatedAnnotation}} public class ApiException extends Exception { private int code = 0; - private String message = null; private Map> responseHeaders = null; private String responseBody = null; public ApiException() {} - public ApiException(int code, String message) { + public ApiException(Throwable throwable) { + super(throwable); + } + + public ApiException(String message) { + super(message); + } + + public ApiException(String message, Throwable throwable, int code, Map> responseHeaders, String responseBody) { + super(message, throwable); + this.code = code; + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + public ApiException(String message, int code, Map> responseHeaders, String responseBody) { + this(message, (Throwable) null, code, responseHeaders, responseBody); + } + + public ApiException(String message, Throwable throwable, int code, Map> responseHeaders) { + this(message, throwable, code, responseHeaders, null); + } + + public ApiException(int code, Map> responseHeaders, String responseBody) { + this((String) null, (Throwable) null, code, responseHeaders, responseBody); + } + + public ApiException(int code, String message) { + super(message); this.code = code; - this.message = message; } public ApiException(int code, String message, Map> responseHeaders, String responseBody) { - this.code = code; - this.message = message; + this(code, message); this.responseHeaders = responseHeaders; this.responseBody = responseBody; } @@ -28,10 +53,6 @@ public class ApiException extends Exception { return code; } - public String getMessage() { - return message; - } - /** * Get the HTTP response headers. */ diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiCallback.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiCallback.mustache new file mode 100644 index 00000000000..4af16c9faa9 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiCallback.mustache @@ -0,0 +1,22 @@ +package {{invokerPackage}}; + +import java.io.IOException; + +/** + * Callback for asynchronous API call. + * + * @param The return type + */ +public interface ApiCallback { + /** + * This is called when the API call fails. + */ + void onFailure(ApiException e); + + /** + * This is called when the API call succeeded. + * + * @param result The result deserialized from response + */ + void onSuccess(T result); +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache new file mode 100644 index 00000000000..069aa273b14 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache @@ -0,0 +1,762 @@ +package {{invokerPackage}}; + +import com.squareup.okhttp.Call; +import com.squareup.okhttp.Callback; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; +import com.squareup.okhttp.RequestBody; +import com.squareup.okhttp.FormEncodingBuilder; +import com.squareup.okhttp.MultipartBuilder; +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.Headers; + +import java.lang.reflect.Type; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Date; +import java.util.TimeZone; +import java.util.regex.Pattern; + +import java.net.URLEncoder; +import java.net.URLConnection; + +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.text.ParseException; + +import {{invokerPackage}}.auth.Authentication; +import {{invokerPackage}}.auth.HttpBasicAuth; +import {{invokerPackage}}.auth.ApiKeyAuth; +import {{invokerPackage}}.auth.OAuth; + +public class ApiClient { + private String basePath = "{{basePath}}"; + private boolean lenientOnJson = false; + private boolean debugging = false; + private Map defaultHeaderMap = new HashMap(); + + private Map authentications; + + private String dateFormat; + private DateFormat dateFormatter; + private int dateLength; + + private String datetimeFormat; + private DateFormat datetimeFormatter; + + private OkHttpClient httpClient; + private JSON json; + + public ApiClient() { + httpClient = new OkHttpClient(); + + json = new JSON(this); + + // Use ISO 8601 format for date and datetime. + // See https://en.wikipedia.org/wiki/ISO_8601 + setDateFormat("yyyy-MM-dd"); + setDatetimeFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + + // Set default User-Agent. + setUserAgent("Java-Swagger"); + + // Setup authentications (key: authentication name, value: authentication). + 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. + authentications = Collections.unmodifiableMap(authentications); + } + + public String getBasePath() { + return basePath; + } + + public ApiClient setBasePath(String basePath) { + this.basePath = basePath; + return this; + } + + public OkHttpClient getHttpClient() { + return httpClient; + } + + public ApiClient setHttpClient(OkHttpClient httpClient) { + this.httpClient = httpClient; + return this; + } + + public JSON getJSON() { + return json; + } + + public ApiClient setJSON(JSON json) { + this.json = json; + return this; + } + + public String getDateFormat() { + return dateFormat; + } + + public ApiClient setDateFormat(String dateFormat) { + this.dateFormat = dateFormat; + + this.dateFormatter = new SimpleDateFormat(dateFormat); + // Use UTC as the default time zone. + this.dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC")); + + this.dateLength = this.dateFormatter.format(new Date()).length(); + + return this; + } + + public String getDatetimeFormat() { + return datetimeFormat; + } + + public ApiClient setDatetimeFormat(String datetimeFormat) { + this.datetimeFormat = datetimeFormat; + + this.datetimeFormatter = new SimpleDateFormat(datetimeFormat); + // Note: The datetime formatter uses the system's default time zone. + + return this; + } + + /** + * Parse the given date string into Date object. + * The default dateFormat supports these ISO 8601 date formats: + * 2015-08-16 + * 2015-8-16 + */ + public Date parseDate(String str) { + if (str == null) + return null; + try { + return dateFormatter.parse(str); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * Parse the given date-time string into Date object. + * The default datetimeFormat supports these ISO 8601 datetime formats: + * 2015-08-16T08:20:05Z + * 2015-8-16T8:20:05Z + * 2015-08-16T08:20:05+00:00 + * 2015-08-16T08:20:05+0000 + * 2015-08-16T08:20:05.376Z + * 2015-08-16T08:20:05.376+00:00 + * 2015-08-16T08:20:05.376+00 + * Note: The 3-digit milli-seconds is optional. Time zone is required and can be in one of + * these formats: + * Z (same with +0000) + * +08:00 (same with +0800) + * -02 (same with -0200) + * -0200 + * @see https://en.wikipedia.org/wiki/ISO_8601 + */ + public Date parseDatetime(String str) { + if (str == null) + return null; + + if ("yyyy-MM-dd'T'HH:mm:ss.SSSZ".equals(datetimeFormat)) { + /* + * When the default datetime format is used, process the given string + * to support various formats defined by ISO 8601. + */ + // normalize time zone + // trailing "Z": 2015-08-16T08:20:05Z => 2015-08-16T08:20:05+0000 + str = str.replaceAll("[zZ]\\z", "+0000"); + // remove colon: 2015-08-16T08:20:05+00:00 => 2015-08-16T08:20:05+0000 + str = str.replaceAll("([+-]\\d{2}):(\\d{2})\\z", "$1$2"); + // expand time zone: 2015-08-16T08:20:05+00 => 2015-08-16T08:20:05+0000 + str = str.replaceAll("([+-]\\d{2})\\z", "$100"); + // add milliseconds when missing + // 2015-08-16T08:20:05+0000 => 2015-08-16T08:20:05.000+0000 + str = str.replaceAll("(:\\d{1,2})([+-]\\d{4})\\z", "$1.000$2"); + } + + try { + return datetimeFormatter.parse(str); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + public Date parseDateOrDatetime(String str) { + if (str == null) + return null; + else if (str.length() <= dateLength) + return parseDate(str); + else + return parseDatetime(str); + } + + /** + * Format the given Date object into string. + */ + public String formatDate(Date date) { + return dateFormatter.format(date); + } + + /** + * Format the given Date object into string. + */ + public String formatDatetime(Date date) { + return datetimeFormatter.format(date); + } + + /** + * Get authentications (key: authentication name, value: authentication). + */ + 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. + */ + public void setUsername(String username) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setUsername(username); + return; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set password for the first HTTP basic authentication. + */ + public void setPassword(String password) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setPassword(password); + return; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set API key value for the first API key authentication. + */ + public void setApiKey(String apiKey) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKey(apiKey); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set API key prefix for the first API key authentication. + */ + public void setApiKeyPrefix(String apiKeyPrefix) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Set the User-Agent header's value (by adding to the default header map). + */ + public ApiClient setUserAgent(String userAgent) { + addDefaultHeader("User-Agent", userAgent); + return this; + } + + /** + * Add a default header. + * + * @param key The header's key + * @param value The header's value + */ + public ApiClient addDefaultHeader(String key, String value) { + defaultHeaderMap.put(key, value); + return this; + } + + /** + * @see https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/stream/JsonReader.html#setLenient(boolean) + */ + public boolean isLenientOnJson() { + return lenientOnJson; + } + + public ApiClient setLenientOnJson(boolean lenient) { + this.lenientOnJson = lenient; + return this; + } + + /** + * Check that whether debugging is enabled for this API client. + */ + public boolean isDebugging() { + return debugging; + } + + /** + * Enable/disable debugging for this API client. + * + * @param debugging To enable (true) or disable (false) debugging + */ + public ApiClient setDebugging(boolean debugging) { + this.debugging = debugging; + return this; + } + + /** + * Format the given parameter object into string. + */ + public String parameterToString(Object param) { + if (param == null) { + return ""; + } else if (param instanceof Date) { + return formatDatetime((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. + */ + 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 = null; + 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 + collectionFormat = (collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat); // default: csv + + // create the params based on the collection format + if (collectionFormat.equals("multi")) { + for (Object item : valueCollection) { + params.add(new Pair(name, parameterToString(item))); + } + + return params; + } + + String delimiter = ","; + + if (collectionFormat.equals("csv")) { + delimiter = ","; + } else if (collectionFormat.equals("ssv")) { + delimiter = " "; + } else if (collectionFormat.equals("tsv")) { + delimiter = "\t"; + } else if (collectionFormat.equals("pipes")) { + 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; + } + + /** + * 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). + */ + public String selectHeaderAccept(String[] accepts) { + if (accepts.length == 0) return null; + if (StringUtil.containsIgnoreCase(accepts, "application/json")) return "application/json"; + 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. + */ + public String selectHeaderContentType(String[] contentTypes) { + if (contentTypes.length == 0) return "application/json"; + if (StringUtil.containsIgnoreCase(contentTypes, "application/json")) return "application/json"; + return contentTypes[0]; + } + + /** + * Escape the given string to be used as URL query value. + */ + public String escapeString(String str) { + try { + return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20"); + } catch (UnsupportedEncodingException e) { + return str; + } + } + + /** + * Deserialize response body to Java object, according the Content-Type + * response header. + * + * @param response HTTP response + * @param returnType The type of the Java object + * @return The deserialized Java object + */ + public T deserialize(Response response, Type returnType) throws ApiException { + if (response == null || returnType == null) + return null; + + String respBody; + try { + if (response.body() != null) + respBody = response.body().string(); + else + respBody = null; + } catch (IOException e) { + throw new ApiException(e); + } + + if (respBody == null || "".equals(respBody)) + return null; + + String contentType = response.headers().get("Content-Type"); + if (contentType == null) { + // ensuring a default content type + contentType = "application/json"; + } + if (contentType.startsWith("application/json")) { + return json.deserialize(respBody, returnType); + } else { + throw new ApiException( + "Content type \"" + contentType + "\" is not supported", + response.code(), + response.headers().toMultimap(), + respBody); + } + } + + /** + * Serialize the given Java object into request body string, according to the + * request Content-Type. + * + * @param obj The Java object + * @param contentType The request Content-Type + * @return The serialized string + */ + public String serialize(Object obj, String contentType) throws ApiException { + if (contentType.startsWith("application/json")) { + if (obj != null) + return json.serialize(obj); + else + return null; + } else { + throw new ApiException("Content type \"" + contentType + "\" is not supported"); + } + } + + /** + * @see #execute(Call, Type) + */ + public T execute(Call call) throws ApiException { + return execute(call, null); + } + + /** + * Execute HTTP call and deserialize the HTTP response body into the given return type. + * + * @param returnType The return type used to deserialize HTTP response body + * @param The return type corresponding to (same with) returnType + * @return The Java object deserialized from response body. Returns null if returnType is null. + */ + public T execute(Call call, Type returnType) throws ApiException { + try { + Response response = call.execute(); + return handleResponse(response, returnType); + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * #see executeAsync(Call, Type, ApiCallback) + */ + public void executeAsync(Call call, ApiCallback callback) throws ApiException { + executeAsync(call, null, callback); + } + + /** + * Execute HTTP call asynchronously. + * + * @see #execute(Call, Type) + * @param The callback to be executed when the API call finishes + */ + public void executeAsync(Call call, final Type returnType, final ApiCallback callback) { + call.enqueue(new Callback() { + @Override + public void onFailure(Request request, IOException e) { + callback.onFailure(new ApiException(e)); + } + + @Override + public void onResponse(Response response) throws IOException { + T result; + try { + result = (T) handleResponse(response, returnType); + } catch (ApiException e) { + callback.onFailure(e); + return; + } + callback.onSuccess(result); + } + }); + } + + public T handleResponse(Response response, Type returnType) throws ApiException { + if (response.isSuccessful()) { + if (returnType == null || response.code() == 204) { + // returning null if the returnType is not defined, + // or the status code is 204 (No Content) + return null; + } else { + return deserialize(response, returnType); + } + } else { + String respBody = null; + if (response.body() != null) { + try { + respBody = response.body().string(); + } catch (IOException e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + throw new ApiException(response.message(), response.code(), response.headers().toMultimap(), respBody); + } + } + + /** + * Build HTTP call with the given options. + * + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @return The HTTP call + */ + public Call buildCall(String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String[] authNames) throws ApiException { + updateParamsForAuth(authNames, queryParams, headerParams); + + final String url = buildUrl(path, queryParams); + final Request.Builder reqBuilder = new Request.Builder().url(url); + processHeaderParams(headerParams, reqBuilder); + + String contentType = (String) headerParams.get("Content-Type"); + // ensuring a default content type + if (contentType == null) contentType = "application/json"; + + RequestBody reqBody; + if ("GET".equals(method) || "HEAD".equals(method)) { + reqBody = null; + } else if ("application/x-www-form-urlencoded".equals(contentType)) { + reqBody = buildRequestBodyFormEncoding(formParams); + } else if ("multipart/form-data".equals(contentType)) { + reqBody = buildRequestBodyMultipart(formParams); + } else if (body == null) { + if ("DELETE".equals(method)) { + // allow calling DELETE without sending a request body + reqBody = null; + } else { + // use an empty request body (for POST, PUT and PATCH) + reqBody = RequestBody.create(MediaType.parse(contentType), ""); + } + } else { + reqBody = RequestBody.create(MediaType.parse(contentType), serialize(body, contentType)); + } + + Request request; + if ("GET".equals(method)) { + request = reqBuilder.get().build(); + } else if ("HEAD".equals(method)) { + request = reqBuilder.head().build(); + } else if ("POST".equals(method)) { + request = reqBuilder.post(reqBody).build(); + } else if ("PUT".equals(method)) { + request = reqBuilder.put(reqBody).build(); + } else if ("PATCH".equals(method)) { + request = reqBuilder.patch(reqBody).build(); + } else if ("DELETE".equals(method)) { + if (reqBody == null) { + // calling DELETE without sending a request body + request = reqBuilder.delete().build(); + } else { + request = reqBuilder.delete(reqBody).build(); + } + } else { + throw new ApiException("unknown method type: " + method); + } + + return httpClient.newCall(request); + } + + /** + * Build full URL by concatenating base path, the given sub path and query parameters. + * + * @param path The sub path + * @param queryParams The query parameters + * @return The full URL + */ + public String buildUrl(String path, List queryParams) { + StringBuilder query = new StringBuilder(); + if (queryParams != null) { + for (Pair param : queryParams) { + if (param.getValue() != null) { + if (query.toString().length() == 0) + query.append("?"); + else + query.append("&"); + String value = parameterToString(param.getValue()); + query.append(escapeString(param.getName())).append("=").append(escapeString(value)); + } + } + } + return basePath + path + query.toString(); + } + + /** + * Set header parameters to the request builder, including default headers. + */ + public void processHeaderParams(Map headerParams, Request.Builder reqBuilder) { + for (Entry param : headerParams.entrySet()) { + reqBuilder.header(param.getKey(), parameterToString(param.getValue())); + } + for (Entry header : defaultHeaderMap.entrySet()) { + if (!headerParams.containsKey(header.getKey())) { + reqBuilder.header(header.getKey(), parameterToString(header.getValue())); + } + } + } + + /** + * Update query and header parameters based on authentication settings. + * + * @param authNames The authentications to apply + */ + public void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams) { + for (String authName : authNames) { + Authentication auth = authentications.get(authName); + if (auth == null) throw new RuntimeException("Authentication undefined: " + authName); + auth.applyToParams(queryParams, headerParams); + } + } + + /** + * Build a form-encoding request body with the given form parameters. + */ + public RequestBody buildRequestBodyFormEncoding(Map formParams) { + FormEncodingBuilder formBuilder = new FormEncodingBuilder(); + for (Entry param : formParams.entrySet()) { + formBuilder.add(param.getKey(), parameterToString(param.getValue())); + } + return formBuilder.build(); + } + + /** + * Build a multipart (file uploading) request body with the given form parameters, + * which could contain text fields and file fields. + */ + public RequestBody buildRequestBodyMultipart(Map formParams) { + MultipartBuilder mpBuilder = new MultipartBuilder().type(MultipartBuilder.FORM); + for (Entry param : formParams.entrySet()) { + if (param.getValue() instanceof File) { + File file = (File) param.getValue(); + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\"; filename=\"" + file.getName() + "\""); + MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file)); + mpBuilder.addPart(partHeaders, RequestBody.create(mediaType, file)); + } else { + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\""); + mpBuilder.addPart(partHeaders, RequestBody.create(null, parameterToString(param.getValue()))); + } + } + return mpBuilder.build(); + } + + /** + * Guess Content-Type header from the given file (defaults to "application/octet-stream"). + * + * @param file The given file + * @return The Content-Type guessed + */ + public String guessContentTypeFromFile(File file) { + String contentType = URLConnection.guessContentTypeFromName(file.getName()); + if (contentType == null) { + return "application/octet-stream"; + } else { + return contentType; + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/JSON.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/JSON.mustache new file mode 100644 index 00000000000..10a43d97de0 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/JSON.mustache @@ -0,0 +1,102 @@ +package {{invokerPackage}}; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.stream.JsonReader; + +import java.io.StringReader; +import java.lang.reflect.Type; +import java.util.Date; + +public class JSON { + private ApiClient apiClient; + private Gson gson; + + public JSON(ApiClient apiClient) { + this.apiClient = apiClient; + gson = new GsonBuilder() + .serializeNulls() + .registerTypeAdapter(Date.class, new DateAdapter(apiClient)) + .create(); + } + + public Gson getGson() { + return gson; + } + + public void setGson(Gson gson) { + this.gson = gson; + } + + /** + * Serialize the given Java object into JSON string. + */ + public String serialize(Object obj) { + return gson.toJson(obj); + } + + /** + * Deserialize the given JSON string to Java object. + * + * @param body The JSON string + * @param returnType The type to deserialize inot + * @return The deserialized Java object + */ + public T deserialize(String body, Type returnType) { + try { + if (apiClient.isLenientOnJson()) { + JsonReader jsonReader = new JsonReader(new StringReader(body)); + // see https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/stream/JsonReader.html#setLenient(boolean) + jsonReader.setLenient(true); + return gson.fromJson(jsonReader, returnType); + } else { + return gson.fromJson(body, returnType); + } + } catch (JsonParseException e) { + // Fallback processing when failed to parse JSON form response body: + // return the response body string directly for the String return type; + // parse response body into date or datetime for the Date return type. + if (returnType.equals(String.class)) + return (T) body; + else if (returnType.equals(Date.class)) + return (T) apiClient.parseDateOrDatetime(body); + else throw(e); + } + } +} + +class DateAdapter implements JsonSerializer, JsonDeserializer { + private final ApiClient apiClient; + + public DateAdapter(ApiClient apiClient) { + super(); + this.apiClient = apiClient; + } + + @Override + public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { + if (src == null) { + return JsonNull.INSTANCE; + } else { + return new JsonPrimitive(apiClient.formatDatetime(src)); + } + } + + @Override + public Date deserialize(JsonElement json, Type date, JsonDeserializationContext context) throws JsonParseException { + String str = json.getAsJsonPrimitive().getAsString(); + try { + return apiClient.parseDateOrDatetime(str); + } catch (RuntimeException e) { + throw new JsonParseException(e); + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/api.mustache new file mode 100644 index 00000000000..b272687c7fd --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/api.mustache @@ -0,0 +1,116 @@ +package {{package}}; + +import {{invokerPackage}}.ApiCallback; +import {{invokerPackage}}.ApiClient; +import {{invokerPackage}}.ApiException; +import {{invokerPackage}}.Configuration; +import {{invokerPackage}}.Pair; + +import {{modelPackage}}.*; + +import com.google.gson.reflect.TypeToken; + +import com.squareup.okhttp.Call; + +import java.lang.reflect.Type; + +import java.util.*; + +{{#imports}}import {{import}}; +{{/imports}} + +import java.io.File; +import java.util.Map; +import java.util.HashMap; + +{{#operations}} +public class {{classname}} { + private ApiClient {{localVariablePrefix}}apiClient; + + public {{classname}}() { + this(Configuration.getDefaultApiClient()); + } + + public {{classname}}(ApiClient apiClient) { + this.{{localVariablePrefix}}apiClient = apiClient; + } + + public ApiClient getApiClient() { + return {{localVariablePrefix}}apiClient; + } + + public void setApiClient(ApiClient apiClient) { + this.{{localVariablePrefix}}apiClient = apiClient; + } + + {{#operation}} + /* Build call for {{nickname}} */ + private Call {{nickname}}Call({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { + Object {{localVariablePrefix}}postBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; + {{#allParams}}{{#required}} + // verify the required parameter '{{paramName}}' is set + if ({{paramName}} == null) { + throw new ApiException("Missing the required parameter '{{paramName}}' when calling {{nickname}}(Async)"); + } + {{/required}}{{/allParams}} + + // create path and map variables + String {{localVariablePrefix}}path = "{{path}}".replaceAll("\\{format\\}","json"){{#pathParams}} + .replaceAll("\\{" + "{{baseName}}" + "\\}", {{localVariablePrefix}}apiClient.escapeString({{{paramName}}}.toString())){{/pathParams}}; + + List {{localVariablePrefix}}queryParams = new ArrayList();{{#queryParams}} + if ({{paramName}} != null) + {{localVariablePrefix}}queryParams.addAll({{localVariablePrefix}}apiClient.parameterToPairs("{{#collectionFormat}}{{{collectionFormat}}}{{/collectionFormat}}", "{{baseName}}", {{paramName}}));{{/queryParams}} + + Map {{localVariablePrefix}}headerParams = new HashMap();{{#headerParams}} + if ({{paramName}} != null) + {{localVariablePrefix}}headerParams.put("{{baseName}}", {{localVariablePrefix}}apiClient.parameterToString({{paramName}}));{{/headerParams}} + + Map {{localVariablePrefix}}formParams = new HashMap();{{#formParams}} + if ({{paramName}} != null) + {{localVariablePrefix}}formParams.put("{{baseName}}", {{paramName}});{{/formParams}} + + final String[] {{localVariablePrefix}}accepts = { + {{#produces}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/produces}} + }; + final String {{localVariablePrefix}}accept = {{localVariablePrefix}}apiClient.selectHeaderAccept({{localVariablePrefix}}accepts); + if ({{localVariablePrefix}}accept != null) {{localVariablePrefix}}headerParams.put("Accept", {{localVariablePrefix}}accept); + + final String[] {{localVariablePrefix}}contentTypes = { + {{#consumes}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} + }; + final String {{localVariablePrefix}}contentType = {{localVariablePrefix}}apiClient.selectHeaderContentType({{localVariablePrefix}}contentTypes); + {{localVariablePrefix}}headerParams.put("Content-Type", {{localVariablePrefix}}contentType); + + String[] {{localVariablePrefix}}authNames = new String[] { {{#authMethods}}"{{name}}"{{#hasMore}}, {{/hasMore}}{{/authMethods}} }; + return {{localVariablePrefix}}apiClient.buildCall({{localVariablePrefix}}path, "{{httpMethod}}", {{localVariablePrefix}}queryParams, {{localVariablePrefix}}postBody, {{localVariablePrefix}}headerParams, {{localVariablePrefix}}formParams, {{localVariablePrefix}}authNames); + } + + /** + * {{summary}} + * {{notes}}{{#allParams}} + * @param {{paramName}} {{description}}{{/allParams}}{{#returnType}} + * @return {{{returnType}}}{{/returnType}} + */ + public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{nickname}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { + Call {{localVariablePrefix}}call = {{nickname}}Call({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{#returnType}}Type {{localVariablePrefix}}returnType = new TypeToken<{{{returnType}}}>(){}.getType(); + return {{localVariablePrefix}}apiClient.execute({{localVariablePrefix}}call, {{localVariablePrefix}}returnType);{{/returnType}}{{^returnType}}{{localVariablePrefix}}apiClient.execute({{localVariablePrefix}}call);{{/returnType}} + } + + /** + * {{summary}} (asynchronously) + * {{notes}}{{#allParams}} + * @param {{paramName}} {{description}}{{/allParams}} + * @param callback The callback to be executed when the API call finishes + * @return The request call + */ + public Call {{nickname}}Async({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}ApiCallback<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{localVariablePrefix}}callback) throws ApiException { + Call {{localVariablePrefix}}call = {{nickname}}Call({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{#returnType}}Type {{localVariablePrefix}}returnType = new TypeToken<{{{returnType}}}>(){}.getType(); + {{localVariablePrefix}}apiClient.executeAsync({{localVariablePrefix}}call, {{localVariablePrefix}}returnType, {{localVariablePrefix}}callback);{{/returnType}}{{^returnType}}{{localVariablePrefix}}apiClient.executeAsync({{localVariablePrefix}}call, {{localVariablePrefix}}callback);{{/returnType}} + return {{localVariablePrefix}}call; + } + {{/operation}} +} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/enumClass.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/enumClass.mustache new file mode 100644 index 00000000000..cc00e9d392e --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/enumClass.mustache @@ -0,0 +1,17 @@ +public enum {{datatypeWithEnum}} { + {{#allowableValues}}{{#enumVars}}@SerializedName("{{value}}") + {{name}}("{{value}}"){{^-last}}, + + {{/-last}}{{#-last}};{{/-last}}{{/enumVars}}{{/allowableValues}} + + private String value; + + StatusEnum(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/model.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/model.mustache new file mode 100644 index 00000000000..0987aa68f34 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/model.mustache @@ -0,0 +1,58 @@ +package {{package}}; + +import {{invokerPackage}}.StringUtil; +{{#imports}}import {{import}}; +{{/imports}} + +import com.google.gson.annotations.SerializedName; + +{{#serializableModel}} +import java.io.Serializable;{{/serializableModel}} + +import io.swagger.annotations.*; + +{{#models}} + +{{#model}}{{#description}} +/** + * {{description}} + **/{{/description}} +@ApiModel(description = "{{{description}}}") +public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#serializableModel}}implements Serializable{{/serializableModel}} { + {{#vars}}{{#isEnum}} + +{{>libraries/okhttp-gson/enumClass}}{{/isEnum}}{{#items.isEnum}}{{#items}} + +{{>libraries/okhttp-gson/enumClass}}{{/items}}{{/items.isEnum}} + @SerializedName("{{baseName}}") + private {{{datatypeWithEnum}}} {{name}} = {{{defaultValue}}}; + {{/vars}} + + {{#vars}} + /**{{#description}} + * {{{description}}}{{/description}}{{#minimum}} + * minimum: {{minimum}}{{/minimum}}{{#maximum}} + * maximum: {{maximum}}{{/maximum}} + **/ + @ApiModelProperty({{#required}}required = {{required}}, {{/required}}value = "{{{description}}}") + public {{{datatypeWithEnum}}} {{getter}}() { + return {{name}}; + } + public void {{setter}}({{{datatypeWithEnum}}} {{name}}) { + this.{{name}} = {{name}}; + } + + {{/vars}} + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class {{classname}} {\n"); + {{#parent}}sb.append(" ").append(StringUtil.toIndentedString(super.toString())).append("\n");{{/parent}} + {{#vars}}sb.append(" {{name}}: ").append(StringUtil.toIndentedString({{name}})).append("\n"); + {{/vars}}sb.append("}"); + return sb.toString(); + } +} +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/pom.mustache new file mode 100644 index 00000000000..67fc10e8323 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/pom.mustache @@ -0,0 +1,146 @@ + + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + + scm:git:git@github.com:swagger-api/swagger-mustache.git + scm:git:git@github.com:swagger-api/swagger-codegen.git + https://github.com/swagger-api/swagger-codegen + + + 2.2.0 + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + jar + test-jar + + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add_sources + generate-sources + + add-source + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + + + + + + + io.swagger + swagger-annotations + ${swagger-annotations-version} + + + com.squareup.okhttp + okhttp + ${okhttp-version} + + + com.google.code.gson + gson + ${gson-version} + + + com.brsanthu + migbase64 + 2.2 + + + + + junit + junit + ${junit-version} + test + + + + 1.5.0 + 2.4.0 + 2.3.1 + 1.0.0 + 4.8.1 + + diff --git a/modules/swagger-codegen/src/main/resources/Java/model.mustache b/modules/swagger-codegen/src/main/resources/Java/model.mustache index bfb65a3d419..f5e48ba997f 100644 --- a/modules/swagger-codegen/src/main/resources/Java/model.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/model.mustache @@ -1,5 +1,6 @@ package {{package}}; +import {{invokerPackage}}.StringUtil; {{#imports}}import {{import}}; {{/imports}} @@ -45,9 +46,9 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#seriali public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class {{classname}} {\n"); - {{#parent}}sb.append(" " + super.toString()).append("\n");{{/parent}} - {{#vars}}sb.append(" {{name}}: ").append({{name}}).append("\n"); - {{/vars}}sb.append("}\n"); + {{#parent}}sb.append(" ").append(StringUtil.toIndentedString(super.toString())).append("\n");{{/parent}} + {{#vars}}sb.append(" {{name}}: ").append(StringUtil.toIndentedString({{name}})).append("\n"); + {{/vars}}sb.append("}"); return sb.toString(); } }