From 85d4a8178d7f7a55924998d8a8f5557bf83c1b99 Mon Sep 17 00:00:00 2001 From: Marcel Koren Date: Thu, 29 Aug 2024 14:18:08 +0200 Subject: [PATCH 1/2] Add examples --- .github/workflows/generate_sdk_code.yml | 2 +- codegen-template/ApiClient.mustache | 892 ++++++++++++++++++ .../BeanValidationException.mustache | 29 + codegen-template/Configuration.mustache | 30 + .../CustomInstantDeserializer.mustache | 234 +++++ codegen-template/JSON.mustache | 539 +++++++++++ codegen-template/JavaTimeFormatter.mustache | 53 ++ codegen-template/Pair.mustache | 46 + codegen-template/README.mustache | 216 +++++ codegen-template/RFC3339DateFormat.mustache | 47 + codegen-template/ServerConfiguration.mustache | 61 ++ codegen-template/ServerVariable.mustache | 26 + codegen-template/StringUtil.mustache | 72 ++ .../additionalEnumTypeAnnotations.mustache | 2 + .../additionalModelTypeAnnotations.mustache | 2 + .../additionalOneOfTypeAnnotations.mustache | 2 + codegen-template/api.mustache | 120 +++ codegen-template/apiException.mustache | 91 ++ codegen-template/apiOperation.mustache | 28 + codegen-template/api_doc.mustache | 108 +++ codegen-template/api_test.mustache | 51 + codegen-template/beanValidation.mustache | 18 + codegen-template/beanValidationCore.mustache | 20 + .../beanValidationQueryParams.mustache | 1 + codegen-template/build.gradle.mustache | 168 ++++ codegen-template/build.sbt.mustache | 1 + codegen-template/enum_outer_doc.mustache | 7 + codegen-template/generatedAnnotation.mustache | 1 + codegen-template/git_push.sh.mustache | 57 ++ codegen-template/gitignore.mustache | 21 + .../gradle-wrapper.properties.mustache | 7 + codegen-template/gradle.properties.mustache | 9 + codegen-template/gradlew.bat.mustache | 92 ++ codegen-template/gradlew.mustache | 249 +++++ codegen-template/jackson_annotations.mustache | 14 + codegen-template/javaBuilder.mustache | 82 ++ codegen-template/licenseInfo.mustache | 11 + codegen-template/manifest.mustache | 3 + codegen-template/maven.yml.mustache | 31 + codegen-template/model.mustache | 68 ++ codegen-template/modelEnum.mustache | 120 +++ codegen-template/modelInnerEnum.mustache | 95 ++ codegen-template/model_doc.mustache | 4 + codegen-template/model_test.mustache | 43 + codegen-template/oneof_interface.mustache | 6 + codegen-template/openapi.mustache | 1 + codegen-template/pojo.mustache | 624 ++++++++++++ codegen-template/pojo_doc.mustache | 37 + codegen-template/pom.mustache | 382 ++++++++ codegen-template/settings.gradle.mustache | 1 + codegen-template/travis.mustache | 22 + codegen-template/typeInfoAnnotation.mustache | 17 + codegen-template/xmlAnnotation.mustache | 6 + examples/pushData/src/PushData.java | 32 + 54 files changed, 4900 insertions(+), 1 deletion(-) create mode 100644 codegen-template/ApiClient.mustache create mode 100644 codegen-template/BeanValidationException.mustache create mode 100644 codegen-template/Configuration.mustache create mode 100644 codegen-template/CustomInstantDeserializer.mustache create mode 100644 codegen-template/JSON.mustache create mode 100644 codegen-template/JavaTimeFormatter.mustache create mode 100644 codegen-template/Pair.mustache create mode 100644 codegen-template/README.mustache create mode 100644 codegen-template/RFC3339DateFormat.mustache create mode 100644 codegen-template/ServerConfiguration.mustache create mode 100644 codegen-template/ServerVariable.mustache create mode 100644 codegen-template/StringUtil.mustache create mode 100644 codegen-template/additionalEnumTypeAnnotations.mustache create mode 100644 codegen-template/additionalModelTypeAnnotations.mustache create mode 100644 codegen-template/additionalOneOfTypeAnnotations.mustache create mode 100644 codegen-template/api.mustache create mode 100644 codegen-template/apiException.mustache create mode 100644 codegen-template/apiOperation.mustache create mode 100644 codegen-template/api_doc.mustache create mode 100644 codegen-template/api_test.mustache create mode 100644 codegen-template/beanValidation.mustache create mode 100644 codegen-template/beanValidationCore.mustache create mode 100644 codegen-template/beanValidationQueryParams.mustache create mode 100644 codegen-template/build.gradle.mustache create mode 100644 codegen-template/build.sbt.mustache create mode 100644 codegen-template/enum_outer_doc.mustache create mode 100644 codegen-template/generatedAnnotation.mustache create mode 100755 codegen-template/git_push.sh.mustache create mode 100644 codegen-template/gitignore.mustache create mode 100644 codegen-template/gradle-wrapper.properties.mustache create mode 100644 codegen-template/gradle.properties.mustache create mode 100644 codegen-template/gradlew.bat.mustache create mode 100755 codegen-template/gradlew.mustache create mode 100644 codegen-template/jackson_annotations.mustache create mode 100644 codegen-template/javaBuilder.mustache create mode 100644 codegen-template/licenseInfo.mustache create mode 100644 codegen-template/manifest.mustache create mode 100644 codegen-template/maven.yml.mustache create mode 100644 codegen-template/model.mustache create mode 100644 codegen-template/modelEnum.mustache create mode 100644 codegen-template/modelInnerEnum.mustache create mode 100644 codegen-template/model_doc.mustache create mode 100644 codegen-template/model_test.mustache create mode 100644 codegen-template/oneof_interface.mustache create mode 100644 codegen-template/openapi.mustache create mode 100644 codegen-template/pojo.mustache create mode 100644 codegen-template/pojo_doc.mustache create mode 100644 codegen-template/pom.mustache create mode 100644 codegen-template/settings.gradle.mustache create mode 100644 codegen-template/travis.mustache create mode 100644 codegen-template/typeInfoAnnotation.mustache create mode 100644 codegen-template/xmlAnnotation.mustache create mode 100644 examples/pushData/src/PushData.java diff --git a/.github/workflows/generate_sdk_code.yml b/.github/workflows/generate_sdk_code.yml index bf5f8dd..a1e9c80 100644 --- a/.github/workflows/generate_sdk_code.yml +++ b/.github/workflows/generate_sdk_code.yml @@ -77,7 +77,7 @@ jobs: - name: Generate SDK run: | java --version - java -jar ${{ runner.temp }}/openapi-generator-cli.jar generate -i ${{ runner.temp }}/openapispec/openapi.yml -g java -o ./src -c ${{ runner.temp }}/${{ env.CONFIG_FILE }} --skip-validate-spec + java -jar ${{ runner.temp }}/openapi-generator-cli.jar generate -i ${{ runner.temp }}/openapispec/openapi.yml -g java -o ./src -c ${{ runner.temp }}/${{ env.CONFIG_FILE }} --skip-validate-spec -t ./codegen-template cp ./src/README.md ./README.md cp -r ./src/docs ./docs diff --git a/codegen-template/ApiClient.mustache b/codegen-template/ApiClient.mustache new file mode 100644 index 0000000..9cf548d --- /dev/null +++ b/codegen-template/ApiClient.mustache @@ -0,0 +1,892 @@ +{{>licenseInfo}} +package {{invokerPackage}}; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.databind.*; +{{#joda}} +import com.fasterxml.jackson.datatype.joda.JodaModule; +{{/joda}} +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.time.OffsetDateTime; +{{#useJakartaEe}} +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; +{{/useJakartaEe}} +{{^useJakartaEe}} +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +{{/useJakartaEe}} + +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.GenericType; +import com.sun.jersey.api.client.config.DefaultClientConfig; +import com.sun.jersey.api.client.filter.GZIPContentEncodingFilter; +import com.sun.jersey.api.client.filter.LoggingFilter; +import com.sun.jersey.api.client.WebResource.Builder; + +import com.sun.jersey.multipart.FormDataMultiPart; +import com.sun.jersey.multipart.file.FileDataBodyPart; + +import {{javaxPackage}}.ws.rs.core.Cookie; +import {{javaxPackage}}.ws.rs.core.Response.Status.Family; +import {{javaxPackage}}.ws.rs.core.MediaType; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Date; +import java.util.TimeZone; + +import java.net.URLEncoder; + +import java.io.File; +import java.io.UnsupportedEncodingException; + +import java.text.DateFormat; + +import {{invokerPackage}}.auth.Authentication; +{{#hasHttpBasicMethods}} +import {{invokerPackage}}.auth.HttpBasicAuth; +{{/hasHttpBasicMethods}} +{{#hasHttpBearerMethods}} +import {{invokerPackage}}.auth.HttpBearerAuth; +{{/hasHttpBearerMethods}} +{{#hasApiKeyMethods}} +import {{invokerPackage}}.auth.ApiKeyAuth; +{{/hasApiKeyMethods}} +{{#hasOAuthMethods}} +import {{invokerPackage}}.auth.OAuth; +{{/hasOAuthMethods}} + +{{>generatedAnnotation}} +public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} { + private Map defaultHeaderMap = new HashMap(); + private Map defaultCookieMap = new HashMap(); + private String basePath = "{{{basePath}}}"; + protected List servers = new ArrayList({{#servers}}{{#-first}}Arrays.asList( +{{/-first}} new ServerConfiguration( + "{{{url}}}", + "{{{description}}}{{^description}}No description provided{{/description}}", + new HashMap(){{#variables}}{{#-first}} {{ +{{/-first}} put("{{{name}}}", new ServerVariable( + "{{{description}}}{{^description}}No description provided{{/description}}", + "{{{defaultValue}}}", + new HashSet( + {{#enumValues}} + {{#-first}} + Arrays.asList( + {{/-first}} + "{{{.}}}"{{^-last}},{{/-last}} + {{#-last}} + ) + {{/-last}} + {{/enumValues}} + ) + )); + {{#-last}} + }}{{/-last}}{{/variables}} + ){{^-last}},{{/-last}} + {{#-last}} + ){{/-last}}{{/servers}}); + protected Integer serverIndex = 0; + protected Map serverVariables = null; + private boolean debugging = false; + private int connectionTimeout = 0; + + private Client httpClient; + private ObjectMapper objectMapper; + + private Map authentications; + + private int statusCode; + private Map> responseHeaders; + + private DateFormat dateFormat; + + public ApiClient() { + objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); + objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + {{#joda}} + objectMapper.registerModule(new JodaModule()); + {{/joda}} + objectMapper.registerModule(new JavaTimeModule()); + objectMapper.setDateFormat(ApiClient.buildDefaultDateFormat()); + + dateFormat = ApiClient.buildDefaultDateFormat(); + + // Set default User-Agent. + setUserAgent("{{{httpUserAgent}}}{{^httpUserAgent}}OpenAPI-Generator/{{{artifactVersion}}}/java{{/httpUserAgent}}"); + + // Setup authentications (key: authentication name, value: authentication). + authentications = new HashMap();{{#authMethods}}{{#isBasic}}{{#isBasicBasic}} + authentications.put("{{name}}", new HttpBasicAuth());{{/isBasicBasic}}{{#isBasicBearer}} + authentications.put("{{name}}", new HttpBearerAuth("{{scheme}}"));{{/isBasicBearer}}{{/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); + + rebuildHttpClient(); + } + + public static DateFormat buildDefaultDateFormat() { + return new RFC3339DateFormat(); + } + + /** + * Build the Client used to make HTTP requests with the latest settings, + * i.e. objectMapper and debugging. + * TODO: better to use the Builder Pattern? + * @return API client + */ + public ApiClient rebuildHttpClient() { + // Add the JSON serialization support to Jersey + JacksonJsonProvider jsonProvider = new JacksonJsonProvider(objectMapper); + DefaultClientConfig conf = new DefaultClientConfig(); + conf.getSingletons().add(jsonProvider); + Client client = Client.create(conf); + client.addFilter(new GZIPContentEncodingFilter({{#useGzipFeature}}true{{/useGzipFeature}}{{^useGzipFeature}}false{{/useGzipFeature}})); + if (debugging) { + client.addFilter(new LoggingFilter()); + } + this.httpClient = client; + return this; + } + + /** + * Returns the current object mapper used for JSON serialization/deserialization. + *

+ * Note: If you make changes to the object mapper, remember to set it back via + * setObjectMapper in order to trigger HTTP client rebuilding. + *

+ * @return Object mapper + */ + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + public ApiClient setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + // Need to rebuild the Client as it depends on object mapper. + rebuildHttpClient(); + return this; + } + + public Client getHttpClient() { + return httpClient; + } + + public ApiClient setHttpClient(Client httpClient) { + this.httpClient = httpClient; + return this; + } + + public String getBasePath() { + return basePath; + } + + public ApiClient setBasePath(String basePath) { + this.basePath = basePath; + this.serverIndex = null; + return this; + } + + public List getServers() { + return servers; + } + + public ApiClient setServers(List servers) { + this.servers = servers; + return this; + } + + public Integer getServerIndex() { + return serverIndex; + } + + public ApiClient setServerIndex(Integer serverIndex) { + this.serverIndex = serverIndex; + return this; + } + + public Map getServerVariables() { + return serverVariables; + } + + public ApiClient setServerVariables(Map serverVariables) { + this.serverVariables = serverVariables; + return this; + } + + /** + * Gets the status code of the previous request + * @return Status code + */ + public int getStatusCode() { + return statusCode; + } + + /** + * Gets the response headers of the previous request + * @return Response headers + */ + public Map> getResponseHeaders() { + return responseHeaders; + } + + /** + * Get authentications (key: authentication name, value: authentication). + * @return Map of 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); + } + + {{#hasHttpBearerMethods}} + /** + * Helper method to set access token for the first Bearer authentication. + * @param bearerToken Bearer token + * @return API client + */ + public void setBearerToken(String bearerToken) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBearerAuth) { + ((HttpBearerAuth) auth).setBearerToken(bearerToken); + return; + } + } + throw new RuntimeException("No Bearer authentication configured!"); + } + + {{/hasHttpBearerMethods}} + + {{#hasHttpBasicMethods}} + /** + * Helper method to set username for the first HTTP basic authentication. + * @param username Username + */ + 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. + * @param password Password + */ + 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!"); + } + + {{/hasHttpBasicMethods}} + + {{#hasApiKeyMethods}} + /** + * Helper method to set API key value for the first API key authentication. + * @param apiKey the API key + */ + 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. + * @param apiKeyPrefix API key prefix + */ + 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!"); + } + + {{/hasApiKeyMethods}} + + {{#hasOAuthMethods}} + /** + * Helper method to set access token for the first OAuth2 authentication. + * @param accessToken Access token + */ + public void setAccessToken(String accessToken) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setAccessToken(accessToken); + return; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + {{/hasOAuthMethods}} + + /** + * Set the User-Agent header's value (by adding to the default header map). + * @param userAgent User agent + * @return API client + */ + 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 + * @return API client + */ + public ApiClient addDefaultHeader(String key, String value) { + defaultHeaderMap.put(key, value); + return this; + } + + /** + * Add a default cookie. + * + * @param key The cookie's key + * @param value The cookie's value + * @return API client + */ + public ApiClient addDefaultCookie(String key, String value) { + defaultCookieMap.put(key, value); + return this; + } + + /** + * Check that whether debugging is enabled for this API client. + * @return True if debugging is on + */ + public boolean isDebugging() { + return debugging; + } + + /** + * Enable/disable debugging for this API client. + * + * @param debugging To enable (true) or disable (false) debugging + * @return API client + */ + public ApiClient setDebugging(boolean debugging) { + this.debugging = debugging; + // Need to rebuild the Client as it depends on the value of debugging. + rebuildHttpClient(); + return this; + } + + /** + * Connect timeout (in milliseconds). + * @return Connection timeout + */ + public int getConnectTimeout() { + return connectionTimeout; + } + + /** + * Set the connect timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link Integer#MAX_VALUE}. + * @param connectionTimeout Connection timeout in milliseconds + * @return API client + */ + public ApiClient setConnectTimeout(int connectionTimeout) { + this.connectionTimeout = connectionTimeout; + httpClient.setConnectTimeout(connectionTimeout); + return this; + } + + /** + * Get the date format used to parse/format date parameters. + * @return Date format + */ + public DateFormat getDateFormat() { + return dateFormat; + } + + /** + * Set the date format used to parse/format date parameters. + * @param dateFormat Date format + * @return API client + */ + public ApiClient setDateFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + // Also set the date format for model (de)serialization with Date properties. + this.objectMapper.setDateFormat((DateFormat) dateFormat.clone()); + // Need to rebuild the Client as objectMapper changes. + rebuildHttpClient(); + return this; + } + + /** + * Parse the given string into Date object. + * @param str String + * @return Date + */ + public Date parseDate(String str) { + try { + return dateFormat.parse(str); + } catch (java.text.ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 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); + } {{#jsr310}}else if (param instanceof OffsetDateTime) { + return formatOffsetDateTime((OffsetDateTime) param); + } {{/jsr310}}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); + } + } + + /** + * Formats the specified query parameter to a list containing a single {@code Pair} object. + * + * Note that {@code value} must not be a collection. + * + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list containing a single {@code Pair} object. + */ + public List parameterToPair(String name, Object value) { + List params = new ArrayList(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value instanceof Collection) return params; + + params.add(new Pair(name, parameterToString(value))); + return params; + } + + /** + * Formats the specified collection query parameters to a list of {@code Pair} objects. + * + * Note that the values of each of the returned Pair objects are percent-encoded. + * + * @param collectionFormat The collection format of the parameter. + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list of {@code Pair} objects. + */ + public List parameterToPairs(String collectionFormat, String name, Collection value) { + List params = new ArrayList(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value.isEmpty()) { + return params; + } + + // create the params based on the collection format + if ("multi".equals(collectionFormat)) { + for (Object item : value) { + params.add(new Pair(name, escapeString(parameterToString(item)))); + } + return params; + } + + // collectionFormat is assumed to be "csv" by default + String delimiter = ","; + + // escape all delimiters except commas, which are URI reserved + // characters + if ("ssv".equals(collectionFormat)) { + delimiter = escapeString(" "); + } else if ("tsv".equals(collectionFormat)) { + delimiter = escapeString("\t"); + } else if ("pipes".equals(collectionFormat)) { + delimiter = escapeString("|"); + } + + StringBuilder sb = new StringBuilder() ; + for (Object item : value) { + sb.append(delimiter); + sb.append(escapeString(parameterToString(item))); + } + + params.add(new Pair(name, sb.substring(delimiter.length()))); + + 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 MIME type is boolean + */ + public boolean isJsonMime(String mime) { + String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); + } + + /** + * 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; + } + 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, + * or matches "any", JSON will be used. + */ + public String selectHeaderContentType(String[] contentTypes) { + if (contentTypes.length == 0 || contentTypes[0].equals("*/*")) { + return "application/json"; + } + for (String contentType : contentTypes) { + if (isJsonMime(contentType)) { + return contentType; + } + } + return contentTypes[0]; + } + + /** + * Escape the given string to be used as URL query value. + * @param str String + * @return Escaped string + */ + public String escapeString(String str) { + try { + return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20"); + } catch (UnsupportedEncodingException e) { + return str; + } + } + + /** + * Serialize the given Java object into string according the given + * Content-Type (only JSON is supported for now). + * @param obj Object + * @param contentType Content type + * @param formParams Form parameters + * @return Object + * @throws ApiException API exception + */ + public Object serialize(Object obj, String contentType, Map formParams) throws ApiException { + if (contentType.startsWith("multipart/form-data")) { + FormDataMultiPart mp = new FormDataMultiPart(); + for (Entry param: formParams.entrySet()) { + if( param.getValue() instanceof List && !( ( List ) param.getValue() ).isEmpty() + && ( ( List ) param.getValue() ).get( 0 ) instanceof File ) { + @SuppressWarnings( "unchecked" ) + List files = ( List ) param.getValue(); + for( File file : files ) { + mp.bodyPart( new FileDataBodyPart( param.getKey(), file, MediaType.APPLICATION_OCTET_STREAM_TYPE ) ); + } + } else if (param.getValue() instanceof File) { + File file = (File) param.getValue(); + mp.bodyPart(new FileDataBodyPart(param.getKey(), file, MediaType.APPLICATION_OCTET_STREAM_TYPE)); + } else { + mp.field(param.getKey(), parameterToString(param.getValue()), MediaType.MULTIPART_FORM_DATA_TYPE); + } + } + return mp; + } else if (contentType.startsWith("application/x-www-form-urlencoded")) { + return this.getXWWWFormUrlencodedParams(formParams); + } else { + // We let Jersey attempt to serialize the body + return obj; + } + } + + /** + * Build full URL by concatenating base path, the given sub path and query parameters. + * + * @param path The sub path + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @return The full URL + */ + private String buildUrl(String path, List queryParams, List collectionQueryParams) { + String baseURL; + if (serverIndex != null) { + if (serverIndex < 0 || serverIndex >= servers.size()) { + throw new ArrayIndexOutOfBoundsException(String.format( + "Invalid index %d when selecting the host settings. Must be less than %d", serverIndex, servers.size() + )); + } + baseURL = servers.get(serverIndex).URL(serverVariables); + } else { + baseURL = basePath; + } + + final StringBuilder url = new StringBuilder(); + url.append(baseURL).append(path); + + if (queryParams != null && !queryParams.isEmpty()) { + // support (constant) query string in `path`, e.g. "/posts?draft=1" + String prefix = path.contains("?") ? "&" : "?"; + for (Pair param : queryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + url.append(escapeString(param.getName())).append("=").append(escapeString(value)); + } + } + } + + if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) { + String prefix = url.toString().contains("?") ? "&" : "?"; + for (Pair param : collectionQueryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + // collection query parameter value already escaped as part of parameterToPairs + url.append(escapeString(param.getName())).append("=").append(value); + } + } + } + + return url.toString(); + } + + private ClientResponse getAPIResponse(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String accept, String contentType, String[] authNames) throws ApiException { + if (body != null && !formParams.isEmpty()) { + throw new ApiException(500, "Cannot have body and form params"); + } + + updateParamsForAuth(authNames, queryParams, headerParams, cookieParams); + + final String url = buildUrl(path, queryParams, collectionQueryParams); + Builder builder; + if (accept == null) { + builder = httpClient.resource(url).getRequestBuilder(); + } else { + builder = httpClient.resource(url).accept(accept); + } + + for (Entry keyValue : headerParams.entrySet()) { + builder = builder.header(keyValue.getKey(), keyValue.getValue()); + } + for (Map.Entry keyValue : defaultHeaderMap.entrySet()) { + if (!headerParams.containsKey(keyValue.getKey())) { + builder = builder.header(keyValue.getKey(), keyValue.getValue()); + } + } + + for (Entry keyValue : cookieParams.entrySet()) { + builder = builder.cookie(new Cookie(keyValue.getKey(), keyValue.getValue())); + } + for (Map.Entry keyValue : defaultCookieMap.entrySet()) { + if (!cookieParams.containsKey(keyValue.getKey())) { + builder = builder.cookie(new Cookie(keyValue.getKey(), keyValue.getValue())); + } + } + + ClientResponse response = null; + + if ("GET".equals(method)) { + response = (ClientResponse) builder.get(ClientResponse.class); + } else if ("POST".equals(method)) { + response = builder.type(contentType).post(ClientResponse.class, serialize(body, contentType, formParams)); + } else if ("PUT".equals(method)) { + response = builder.type(contentType).put(ClientResponse.class, serialize(body, contentType, formParams)); + } else if ("DELETE".equals(method)) { + response = builder.type(contentType).delete(ClientResponse.class, serialize(body, contentType, formParams)); + } else if ("PATCH".equals(method)) { + response = builder.type(contentType).header("X-HTTP-Method-Override", "PATCH").post(ClientResponse.class, serialize(body, contentType, formParams)); + } else if ("HEAD".equals(method)) { + response = builder.head(); + } else { + throw new ApiException(500, "unknown method type " + method); + } + return response; + } + + /** + * 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", and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object - if it is not binary, otherwise null + * @param headerParams The header parameters + * @param cookieParams The cookie parameters + * @param formParams The form parameters + * @param accept The request's Accept header + * @param contentType The request's Content-Type header + * @param authNames The authentications to apply + * @param returnType Return type + * @return The response body in type of string + * @throws ApiException API exception + */ + public T invokeAPI(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String accept, String contentType, String[] authNames, GenericType returnType) throws ApiException { + + ClientResponse response = getAPIResponse(path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames); + + statusCode = response.getStatusInfo().getStatusCode(); + responseHeaders = response.getHeaders(); + + if(response.getStatusInfo().getStatusCode() == ClientResponse.Status.NO_CONTENT.getStatusCode()) { + return null; + } else if (response.getStatusInfo().getFamily() == Family.SUCCESSFUL) { + if (returnType == null) + return null; + else + return response.getEntity(returnType); + } else { + String message = "error"; + String respBody = null; + if (response.hasEntity()) { + try { + respBody = response.getEntity(String.class); + message = respBody; + } catch (RuntimeException e) { + // e.printStackTrace(); + } + } + throw new ApiException( + response.getStatusInfo().getStatusCode(), + message, + response.getHeaders(), + respBody); + } + } + + /** + * Update query and header parameters based on authentication settings. + * + * @param authNames The authentications to apply + * @param queryParams Query parameters + * @param headerParams Header parameters + * @param cookieParams Cookie parameters + */ + private void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams, Map cookieParams) { + for (String authName : authNames) { + Authentication auth = authentications.get(authName); + if (auth == null) throw new RuntimeException("Authentication undefined: " + authName); + auth.applyToParams(queryParams, headerParams, cookieParams); + } + } + + /** + * Encode the given form parameters as request body. + * @param formParams Form parameters + * @return HTTP form encoded parameters + */ + private String getXWWWFormUrlencodedParams(Map formParams) { + StringBuilder formParamBuilder = new StringBuilder(); + + for (Entry param : formParams.entrySet()) { + String valueStr = parameterToString(param.getValue()); + try { + formParamBuilder.append(URLEncoder.encode(param.getKey(), "utf8")) + .append("=") + .append(URLEncoder.encode(valueStr, "utf8")); + formParamBuilder.append("&"); + } catch (UnsupportedEncodingException e) { + // move on to next + } + } + + String encodedFormParams = formParamBuilder.toString(); + if (encodedFormParams.endsWith("&")) { + encodedFormParams = encodedFormParams.substring(0, encodedFormParams.length() - 1); + } + + return encodedFormParams; + } +} diff --git a/codegen-template/BeanValidationException.mustache b/codegen-template/BeanValidationException.mustache new file mode 100644 index 0000000..d8b0fa6 --- /dev/null +++ b/codegen-template/BeanValidationException.mustache @@ -0,0 +1,29 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +import java.util.Set; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ValidationException; + +public class BeanValidationException extends ValidationException { + /** + * + */ + private static final long serialVersionUID = -5294733947409491364L; + Set> violations; + + public BeanValidationException(Set> violations) { + this.violations = violations; + } + + public Set> getViolations() { + return violations; + } + + public void setViolations(Set> violations) { + this.violations = violations; + } + +} diff --git a/codegen-template/Configuration.mustache b/codegen-template/Configuration.mustache new file mode 100644 index 0000000..8e9720e --- /dev/null +++ b/codegen-template/Configuration.mustache @@ -0,0 +1,30 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +{{>generatedAnnotation}} +public class Configuration { + public static final String VERSION = "{{{artifactVersion}}}"; + + private static ApiClient defaultApiClient = new ApiClient(); + + /** + * Get the default API client, which would be used when creating API + * instances without providing an API client. + * + * @return Default API client + */ + public static ApiClient getDefaultApiClient() { + return defaultApiClient; + } + + /** + * Set the default API client, which would be used when creating API + * instances without providing an API client. + * + * @param apiClient API client + */ + public static void setDefaultApiClient(ApiClient apiClient) { + defaultApiClient = apiClient; + } +} diff --git a/codegen-template/CustomInstantDeserializer.mustache b/codegen-template/CustomInstantDeserializer.mustache new file mode 100644 index 0000000..d4a3ca9 --- /dev/null +++ b/codegen-template/CustomInstantDeserializer.mustache @@ -0,0 +1,234 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonTokenId; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.datatype.threetenbp.DecimalUtils; +import com.fasterxml.jackson.datatype.threetenbp.deser.ThreeTenDateTimeDeserializerBase; +import com.fasterxml.jackson.datatype.threetenbp.function.BiFunction; +import com.fasterxml.jackson.datatype.threetenbp.function.Function; +import org.threeten.bp.DateTimeException; +import org.threeten.bp.DateTimeUtils; +import org.threeten.bp.Instant; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.ZoneId; +import org.threeten.bp.ZonedDateTime; +import org.threeten.bp.format.DateTimeFormatter; +import org.threeten.bp.temporal.Temporal; +import org.threeten.bp.temporal.TemporalAccessor; + +import java.io.IOException; +import java.math.BigDecimal; + +/** + * Deserializer for ThreeTen temporal {@link Instant}s, {@link OffsetDateTime}, and {@link ZonedDateTime}s. + * Adapted from the jackson threetenbp InstantDeserializer to add support for deserializing rfc822 format. + * + * @author Nick Williams + */ +public class CustomInstantDeserializer + extends ThreeTenDateTimeDeserializerBase { + private static final long serialVersionUID = 1L; + + public static final CustomInstantDeserializer INSTANT = new CustomInstantDeserializer( + Instant.class, DateTimeFormatter.ISO_INSTANT, + new Function() { + @Override + public Instant apply(TemporalAccessor temporalAccessor) { + return Instant.from(temporalAccessor); + } + }, + new Function() { + @Override + public Instant apply(FromIntegerArguments a) { + return Instant.ofEpochMilli(a.value); + } + }, + new Function() { + @Override + public Instant apply(FromDecimalArguments a) { + return Instant.ofEpochSecond(a.integer, a.fraction); + } + }, + null + ); + + public static final CustomInstantDeserializer OFFSET_DATE_TIME = new CustomInstantDeserializer( + OffsetDateTime.class, DateTimeFormatter.ISO_OFFSET_DATE_TIME, + new Function() { + @Override + public OffsetDateTime apply(TemporalAccessor temporalAccessor) { + return OffsetDateTime.from(temporalAccessor); + } + }, + new Function() { + @Override + public OffsetDateTime apply(FromIntegerArguments a) { + return OffsetDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId); + } + }, + new Function() { + @Override + public OffsetDateTime apply(FromDecimalArguments a) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId); + } + }, + new BiFunction() { + @Override + public OffsetDateTime apply(OffsetDateTime d, ZoneId z) { + return d.withOffsetSameInstant(z.getRules().getOffset(d.toLocalDateTime())); + } + } + ); + + public static final CustomInstantDeserializer ZONED_DATE_TIME = new CustomInstantDeserializer( + ZonedDateTime.class, DateTimeFormatter.ISO_ZONED_DATE_TIME, + new Function() { + @Override + public ZonedDateTime apply(TemporalAccessor temporalAccessor) { + return ZonedDateTime.from(temporalAccessor); + } + }, + new Function() { + @Override + public ZonedDateTime apply(FromIntegerArguments a) { + return ZonedDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId); + } + }, + new Function() { + @Override + public ZonedDateTime apply(FromDecimalArguments a) { + return ZonedDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId); + } + }, + new BiFunction() { + @Override + public ZonedDateTime apply(ZonedDateTime zonedDateTime, ZoneId zoneId) { + return zonedDateTime.withZoneSameInstant(zoneId); + } + } + ); + + protected final Function fromMilliseconds; + + protected final Function fromNanoseconds; + + protected final Function parsedToValue; + + protected final BiFunction adjust; + + protected CustomInstantDeserializer(Class supportedType, + DateTimeFormatter parser, + Function parsedToValue, + Function fromMilliseconds, + Function fromNanoseconds, + BiFunction adjust) { + super(supportedType, parser); + this.parsedToValue = parsedToValue; + this.fromMilliseconds = fromMilliseconds; + this.fromNanoseconds = fromNanoseconds; + this.adjust = adjust == null ? new BiFunction() { + @Override + public T apply(T t, ZoneId zoneId) { + return t; + } + } : adjust; + } + + @SuppressWarnings("unchecked") + protected CustomInstantDeserializer(CustomInstantDeserializer base, DateTimeFormatter f) { + super((Class) base.handledType(), f); + parsedToValue = base.parsedToValue; + fromMilliseconds = base.fromMilliseconds; + fromNanoseconds = base.fromNanoseconds; + adjust = base.adjust; + } + + @Override + protected JsonDeserializer withDateFormat(DateTimeFormatter dtf) { + if (dtf == _formatter) { + return this; + } + return new CustomInstantDeserializer(this, dtf); + } + + @Override + public T deserialize(JsonParser parser, DeserializationContext context) throws IOException { + //NOTE: Timestamps contain no timezone info, and are always in configured TZ. Only + //string values have to be adjusted to the configured TZ. + switch (parser.getCurrentTokenId()) { + case JsonTokenId.ID_NUMBER_FLOAT: { + BigDecimal value = parser.getDecimalValue(); + long seconds = value.longValue(); + int nanoseconds = DecimalUtils.extractNanosecondDecimal(value, seconds); + return fromNanoseconds.apply(new FromDecimalArguments( + seconds, nanoseconds, getZone(context))); + } + + case JsonTokenId.ID_NUMBER_INT: { + long timestamp = parser.getLongValue(); + if (context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)) { + return this.fromNanoseconds.apply(new FromDecimalArguments( + timestamp, 0, this.getZone(context) + )); + } + return this.fromMilliseconds.apply(new FromIntegerArguments( + timestamp, this.getZone(context) + )); + } + + case JsonTokenId.ID_STRING: { + String string = parser.getText().trim(); + if (string.length() == 0) { + return null; + } + if (string.endsWith("+0000")) { + string = string.substring(0, string.length() - 5) + "Z"; + } + T value; + try { + TemporalAccessor acc = _formatter.parse(string); + value = parsedToValue.apply(acc); + if (context.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)) { + return adjust.apply(value, this.getZone(context)); + } + } catch (DateTimeException e) { + throw _peelDTE(e); + } + return value; + } + } + throw context.mappingException("Expected type float, integer, or string."); + } + + private ZoneId getZone(DeserializationContext context) { + // Instants are always in UTC, so don't waste compute cycles + return (_valueClass == Instant.class) ? null : DateTimeUtils.toZoneId(context.getTimeZone()); + } + + private static class FromIntegerArguments { + public final long value; + public final ZoneId zoneId; + + private FromIntegerArguments(long value, ZoneId zoneId) { + this.value = value; + this.zoneId = zoneId; + } + } + + private static class FromDecimalArguments { + public final long integer; + public final int fraction; + public final ZoneId zoneId; + + private FromDecimalArguments(long integer, int fraction, ZoneId zoneId) { + this.integer = integer; + this.fraction = fraction; + this.zoneId = zoneId; + } + } +} diff --git a/codegen-template/JSON.mustache b/codegen-template/JSON.mustache new file mode 100644 index 0000000..1d0a813 --- /dev/null +++ b/codegen-template/JSON.mustache @@ -0,0 +1,539 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.internal.bind.util.ISO8601Utils; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.google.gson.JsonElement; +import io.gsonfire.GsonFireBuilder; +import io.gsonfire.TypeSelector; +{{#joda}} +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.DateTimeFormatterBuilder; +import org.joda.time.format.ISODateTimeFormat; +{{/joda}} + +{{#models.0}} +import {{modelPackage}}.*; +{{/models.0}} +import okio.ByteString; + +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.ParsePosition; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import java.util.HashMap; + +public class JSON { + private Gson gson; + private boolean isLenientOnJson = false; + private DateTypeAdapter dateTypeAdapter = new DateTypeAdapter(); + private SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); + {{#joda}} + private DateTimeTypeAdapter dateTimeTypeAdapter = new DateTimeTypeAdapter(); + private LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); + {{/joda}} + {{#jsr310}} + private OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); + private LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); + {{/jsr310}} + private ByteArrayAdapter byteArrayAdapter = new ByteArrayAdapter(); + + @SuppressWarnings("unchecked") + public static GsonBuilder createGson() { + GsonFireBuilder fireBuilder = new GsonFireBuilder() + {{#models}} + {{#model}} + {{#discriminator}} + .registerTypeSelector({{classname}}.class, new TypeSelector<{{classname}}>() { + @Override + public Class getClassForElement(JsonElement readElement) { + Map classByDiscriminatorValue = new HashMap(); + {{#mappedModels}} + classByDiscriminatorValue.put("{{mappingName}}"{{^discriminatorCaseSensitive}}.toUpperCase(Locale.ROOT){{/discriminatorCaseSensitive}}, {{modelName}}.class); + {{/mappedModels}} + classByDiscriminatorValue.put("{{name}}"{{^discriminatorCaseSensitive}}.toUpperCase(Locale.ROOT){{/discriminatorCaseSensitive}}, {{classname}}.class); + return getClassByDiscriminator(classByDiscriminatorValue, + getDiscriminatorValue(readElement, "{{{propertyBaseName}}}")); + } + }) + {{/discriminator}} + {{/model}} + {{/models}} + ; + GsonBuilder builder = fireBuilder.createGsonBuilder(); + {{#disableHtmlEscaping}} + builder.disableHtmlEscaping(); + {{/disableHtmlEscaping}} + return builder; + } + + private static String getDiscriminatorValue(JsonElement readElement, String discriminatorField) { + JsonElement element = readElement.getAsJsonObject().get(discriminatorField); + if (null == element) { + throw new IllegalArgumentException("missing discriminator field: <" + discriminatorField + ">"); + } + return element.getAsString(); + } + + /** + * Returns the Java class that implements the OpenAPI schema for the specified discriminator value. + * + * @param classByDiscriminatorValue The map of discriminator values to Java classes. + * @param discriminatorValue The value of the OpenAPI discriminator in the input data. + * @return The Java class that implements the OpenAPI schema + */ + private static Class getClassByDiscriminator(Map classByDiscriminatorValue, String discriminatorValue) { + Class clazz = (Class) classByDiscriminatorValue.get(discriminatorValue{{^discriminatorCaseSensitive}}.toUpperCase(Locale.ROOT){{/discriminatorCaseSensitive}}); + if (null == clazz) { + throw new IllegalArgumentException("cannot determine model class of name: <" + discriminatorValue + ">"); + } + return clazz; + } + + public JSON() { + gson = createGson() + .registerTypeAdapter(Date.class, dateTypeAdapter) + .registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter) + {{#joda}} + .registerTypeAdapter(DateTime.class, dateTimeTypeAdapter) + .registerTypeAdapter(LocalDate.class, localDateTypeAdapter) + {{/joda}} + {{#jsr310}} + .registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter) + .registerTypeAdapter(LocalDate.class, localDateTypeAdapter) + {{/jsr310}} + .registerTypeAdapter(byte[].class, byteArrayAdapter) + .create(); + } + + /** + * Get Gson. + * + * @return Gson + */ + public Gson getGson() { + return gson; + } + + /** + * Set Gson. + * + * @param gson Gson + * @return JSON + */ + public JSON setGson(Gson gson) { + this.gson = gson; + return this; + } + + /** + * Configure the parser to be liberal in what it accepts. + * + * @param lenientOnJson Set it to true to ignore some syntax errors + * @return JSON + * @see https://www.javadoc.io/doc/com.google.code.gson/gson/2.8.5/com/google/gson/stream/JsonReader.html + */ + public JSON setLenientOnJson(boolean lenientOnJson) { + isLenientOnJson = lenientOnJson; + return this; + } + + /** + * Serialize the given Java object into JSON string. + * + * @param obj Object + * @return String representation of the JSON + */ + public String serialize(Object obj) { + return gson.toJson(obj); + } + + /** + * Deserialize the given JSON string to Java object. + * + * @param Type + * @param body The JSON string + * @param returnType The type to deserialize into + * @return The deserialized Java object + */ + @SuppressWarnings("unchecked") + public T deserialize(String body, Type returnType) { + try { + if (isLenientOnJson) { + JsonReader jsonReader = new JsonReader(new StringReader(body)); + // see https://www.javadoc.io/doc/com.google.code.gson/gson/2.8.5/com/google/gson/stream/JsonReader.html + 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; + if (returnType.equals(String.class)) { + return (T) body; + } else { + throw (e); + } + } + } + + /** + * Gson TypeAdapter for Byte Array type + */ + public class ByteArrayAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, byte[] value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(ByteString.of(value).base64()); + } + } + + @Override + public byte[] read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String bytesAsBase64 = in.nextString(); + ByteString byteString = ByteString.decodeBase64(bytesAsBase64); + return byteString.toByteArray(); + } + } + } + + {{#joda}} + /** + * Gson TypeAdapter for Joda DateTime type + */ + public static class DateTimeTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public DateTimeTypeAdapter() { + this(new DateTimeFormatterBuilder() + .append(ISODateTimeFormat.dateTime().getPrinter(), ISODateTimeFormat.dateOptionalTimeParser().getParser()) + .toFormatter()); + } + + public DateTimeTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, DateTime date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.print(date)); + } + } + + @Override + public DateTime read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return formatter.parseDateTime(date); + } + } + } + + /** + * Gson TypeAdapter for Joda LocalDate type + */ + public class LocalDateTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public LocalDateTypeAdapter() { + this(ISODateTimeFormat.date()); + } + + public LocalDateTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, LocalDate date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.print(date)); + } + } + + @Override + public LocalDate read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return formatter.parseLocalDate(date); + } + } + } + + public JSON setDateTimeFormat(DateTimeFormatter dateFormat) { + dateTimeTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { + localDateTypeAdapter.setFormat(dateFormat); + return this; + } + + {{/joda}} + {{#jsr310}} + /** + * Gson TypeAdapter for JSR310 OffsetDateTime type + */ + public static class OffsetDateTimeTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public OffsetDateTimeTypeAdapter() { + this(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } + + public OffsetDateTimeTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, OffsetDateTime date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } + + @Override + public OffsetDateTime read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + if (date.endsWith("+0000")) { + date = date.substring(0, date.length()-5) + "Z"; + } + return OffsetDateTime.parse(date, formatter); + } + } + } + + /** + * Gson TypeAdapter for JSR310 LocalDate type + */ + public class LocalDateTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public LocalDateTypeAdapter() { + this(DateTimeFormatter.ISO_LOCAL_DATE); + } + + public LocalDateTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, LocalDate date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } + + @Override + public LocalDate read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return LocalDate.parse(date, formatter); + } + } + } + + public JSON setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + offsetDateTimeTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { + localDateTypeAdapter.setFormat(dateFormat); + return this; + } + + {{/jsr310}} + /** + * Gson TypeAdapter for java.sql.Date type + * If the dateFormat is null, a simple "yyyy-MM-dd" format will be used + * (more efficient than SimpleDateFormat). + */ + public static class SqlDateTypeAdapter extends TypeAdapter { + + private DateFormat dateFormat; + + public SqlDateTypeAdapter() {} + + public SqlDateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, java.sql.Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = date.toString(); + } + out.value(value); + } + } + + @Override + public java.sql.Date read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return new java.sql.Date(dateFormat.parse(date).getTime()); + } + return new java.sql.Date(ISO8601Utils.parse(date, new ParsePosition(0)).getTime()); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } + } + + /** + * Gson TypeAdapter for java.util.Date type + * If the dateFormat is null, ISO8601Utils will be used. + */ + public static class DateTypeAdapter extends TypeAdapter { + + private DateFormat dateFormat; + + public DateTypeAdapter() {} + + public DateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = ISO8601Utils.format(date, true); + } + out.value(value); + } + } + + @Override + public Date read(JsonReader in) throws IOException { + try { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return dateFormat.parse(date); + } + return ISO8601Utils.parse(date, new ParsePosition(0)); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } catch (IllegalArgumentException e) { + throw new JsonParseException(e); + } + } + } + + public JSON setDateFormat(DateFormat dateFormat) { + dateTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setSqlDateFormat(DateFormat dateFormat) { + sqlDateTypeAdapter.setFormat(dateFormat); + return this; + } + +} diff --git a/codegen-template/JavaTimeFormatter.mustache b/codegen-template/JavaTimeFormatter.mustache new file mode 100644 index 0000000..f3fb34e --- /dev/null +++ b/codegen-template/JavaTimeFormatter.mustache @@ -0,0 +1,53 @@ +{{>licenseInfo}} +package {{invokerPackage}}; + +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Class that add parsing/formatting support for Java 8+ {@code OffsetDateTime} class. + * It's generated for java clients when {@code AbstractJavaCodegen#dateLibrary} specified as {@code java8}. + */ +{{>generatedAnnotation}} +public class JavaTimeFormatter { + + private DateTimeFormatter offsetDateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + + /** + * Get the date format used to parse/format {@code OffsetDateTime} parameters. + * @return DateTimeFormatter + */ + public DateTimeFormatter getOffsetDateTimeFormatter() { + return offsetDateTimeFormatter; + } + + /** + * Set the date format used to parse/format {@code OffsetDateTime} parameters. + * @param offsetDateTimeFormatter {@code DateTimeFormatter} + */ + public void setOffsetDateTimeFormatter(DateTimeFormatter offsetDateTimeFormatter) { + this.offsetDateTimeFormatter = offsetDateTimeFormatter; + } + + /** + * Parse the given string into {@code OffsetDateTime} object. + * @param str String + * @return {@code OffsetDateTime} + */ + public OffsetDateTime parseOffsetDateTime(String str) { + try { + return OffsetDateTime.parse(str, offsetDateTimeFormatter); + } catch (DateTimeParseException e) { + throw new RuntimeException(e); + } + } + /** + * Format the given {@code OffsetDateTime} object into string. + * @param offsetDateTime {@code OffsetDateTime} + * @return {@code OffsetDateTime} in string format + */ + public String formatOffsetDateTime(OffsetDateTime offsetDateTime) { + return offsetDateTimeFormatter.format(offsetDateTime); + } +} diff --git a/codegen-template/Pair.mustache b/codegen-template/Pair.mustache new file mode 100644 index 0000000..ed767ce --- /dev/null +++ b/codegen-template/Pair.mustache @@ -0,0 +1,46 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +{{>generatedAnnotation}} +public class Pair { + private String name = ""; + private String value = ""; + + public Pair (String name, String value) { + setName(name); + setValue(value); + } + + private void setName(String name) { + if (!isValidString(name)) { + return; + } + + this.name = name; + } + + private void setValue(String value) { + if (!isValidString(value)) { + return; + } + + this.value = value; + } + + public String getName() { + return this.name; + } + + public String getValue() { + return this.value; + } + + private boolean isValidString(String arg) { + if (arg == null) { + return false; + } + + return true; + } +} diff --git a/codegen-template/README.mustache b/codegen-template/README.mustache new file mode 100644 index 0000000..11997e5 --- /dev/null +++ b/codegen-template/README.mustache @@ -0,0 +1,216 @@ +# {{artifactId}} + +{{appName}} + +- API version: {{appVersion}} +{{^hideGenerationTimestamp}} + +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} + +- Generator version: {{generatorVersion}} + +{{{appDescriptionWithNewLines}}} + +{{#infoUrl}} + For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +*Automatically generated by the [OpenAPI Generator](https://openapi-generator.tech)* + +## Requirements + +Building the API client library requires: + +1. Java 1.8+ +{{#jersey2}} +2. Maven (3.8.3+)/Gradle (7.2+) +{{/jersey2}} +{{^jersey2}} +2. Maven/Gradle +{{/jersey2}} + +## Installation + +To install the API client library to your local Maven repository, simply execute: + +```shell +mvn clean install +``` + +To deploy it to a remote Maven repository instead, configure the settings of the repository and execute: + +```shell +mvn clean deploy +``` + +Refer to the [OSSRH Guide](http://central.sonatype.org/pages/ossrh-guide.html) for more information. + +### Maven users + +Add this dependency to your project's POM: + +```xml + + {{{groupId}}} + {{{artifactId}}} + {{{artifactVersion}}} + compile + +``` + +### Gradle users + +Add this dependency to your project's build file: + +```groovy + repositories { + mavenCentral() // Needed if the '{{{artifactId}}}' jar has been published to maven central. + mavenLocal() // Needed if the '{{{artifactId}}}' jar has been published to the local maven repo. + } + + dependencies { + implementation "{{{groupId}}}:{{{artifactId}}}:{{{artifactVersion}}}" + } +``` + +### Others + +At first generate the JAR by executing: + +```shell +mvn clean package +``` + +Then manually install the following JARs: + +- `target/{{{artifactId}}}-{{{artifactVersion}}}.jar` +- `target/lib/*.jar` + +{{#jersey2}} +## Usage + +To add a HTTP proxy for the API client, use `ClientConfig`: +```java +{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}} +import org.glassfish.jersey.apache.connector.ApacheConnectorProvider; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; +import {{{invokerPackage}}}.*; +import {{{package}}}.{{{classname}}}; + +... + +ApiClient defaultClient = Configuration.getDefaultApiClient(); +ClientConfig clientConfig = defaultClient.getClientConfig(); +clientConfig.connectorProvider(new ApacheConnectorProvider()); +clientConfig.property(ClientProperties.PROXY_URI, "http://proxy_url_here"); +clientConfig.property(ClientProperties.PROXY_USERNAME, "proxy_username"); +clientConfig.property(ClientProperties.PROXY_PASSWORD, "proxy_password"); +defaultClient.setClientConfig(clientConfig); + +{{{classname}}} apiInstance = new {{{classname}}}(defaultClient); +{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}} +``` + +{{/jersey2}} +## Getting Started + +Please follow the [installation](#installation) instruction and execute the following Java code: + +```java +import org.databox.ApiClient; +import org.databox.ApiException; +import org.databox.Configuration; +import org.databox.api.DefaultApi; +import org.databox.auth.HttpBasicAuth; +import org.openapitools.client.model.PushData; +import org.openapitools.client.model.PushDataAttribute; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://push.databox.com"); + defaultClient.addDefaultHeader("Accept", "application/vnd.databox.v2+json"); + + HttpBasicAuth basicAuth = (HttpBasicAuth) defaultClient.getAuthentication("basicAuth"); + basicAuth.setUsername("Your_Databox_API_Token"); + + PushData data = new PushData() + .key("Metric_name") // e.g. sales + .value(123f) + .date("Date_in_ISO8601") // e.g. "2024-08-01T00:00:00Z" + .unit("Unit") // optional - e.g. EUR + .attributes(List.of(new PushDataAttribute().key("Dimension_name").value("Dimension_value"))); // optional - e.g. currency & USD + + DefaultApi apiInstance = new DefaultApi(defaultClient); + try { + apiInstance.dataPost(List.of(data)); + } catch (ApiException e) { + e.printStackTrace(); + } + } +} +``` + +## Documentation for API Endpoints + +All URIs are relative to *{{basePath}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{commonPath}}{{path}} | {{summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + +## Documentation for Models + +{{#models}}{{#model}} - [{{classname}}]({{modelDocPath}}{{classname}}.md) +{{/model}}{{/models}} + + +## Documentation for Authorization + +{{^authMethods}}Endpoints do not require authorization.{{/authMethods}} +{{#hasAuthMethods}}Authentication schemes defined for the API:{{/hasAuthMethods}} +{{#authMethods}} + +### {{name}} + +{{#isApiKey}} + +- **Type**: API key +- **API key parameter name**: {{keyParamName}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} +{{/isApiKey}} +{{#isBasicBasic}} + +- **Type**: HTTP basic authentication +{{/isBasicBasic}} +{{#isBasicBearer}} + +- **Type**: HTTP Bearer Token authentication{{#bearerFormat}} ({{{.}}}){{/bearerFormat}} +{{/isBasicBearer}} +{{#isHttpSignature}} + +- **Type**: HTTP signature authentication +{{/isHttpSignature}} +{{#isOAuth}} + +- **Type**: OAuth +- **Flow**: {{flow}} +- **Authorization URL**: {{authorizationUrl}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - {{scope}}: {{description}} +{{/scopes}} +{{/isOAuth}} + +{{/authMethods}} + +## Recommendation + +It's recommended to create an instance of `ApiClient` per thread in a multithreaded environment to avoid any potential issues. + +## Author + +{{#apiInfo}}{{#apis}}{{#-last}}{{infoEmail}} +{{/-last}}{{/apis}}{{/apiInfo}} diff --git a/codegen-template/RFC3339DateFormat.mustache b/codegen-template/RFC3339DateFormat.mustache new file mode 100644 index 0000000..d35040f --- /dev/null +++ b/codegen-template/RFC3339DateFormat.mustache @@ -0,0 +1,47 @@ +{{>licenseInfo}} +package {{invokerPackage}}; + +import com.fasterxml.jackson.databind.util.StdDateFormat; + +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.ParsePosition; +import java.util.Date; +import java.text.DecimalFormat; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +{{>generatedAnnotation}} +public class RFC3339DateFormat extends DateFormat { + private static final long serialVersionUID = 1L; + private static final TimeZone TIMEZONE_Z = TimeZone.getTimeZone("UTC"); + + private final StdDateFormat fmt = new StdDateFormat() + .withTimeZone(TIMEZONE_Z) + .withColonInTimeZone(true); + + public RFC3339DateFormat() { + this.calendar = new GregorianCalendar(); + this.numberFormat = new DecimalFormat(); + } + + @Override + public Date parse(String source) { + return parse(source, new ParsePosition(0)); + } + + @Override + public Date parse(String source, ParsePosition pos) { + return fmt.parse(source, pos); + } + + @Override + public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { + return fmt.format(date, toAppendTo, fieldPosition); + } + + @Override + public Object clone() { + return super.clone(); + } +} diff --git a/codegen-template/ServerConfiguration.mustache b/codegen-template/ServerConfiguration.mustache new file mode 100644 index 0000000..7284004 --- /dev/null +++ b/codegen-template/ServerConfiguration.mustache @@ -0,0 +1,61 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +import java.util.Map; + +/** + * Representing a Server configuration. + */ +{{>generatedAnnotation}} +public class ServerConfiguration { + public String URL; + public String description; + public Map variables; + + /** + * @param URL A URL to the target host. + * @param description A description of the host designated by the URL. + * @param variables A map between a variable name and its value. The value is used for substitution in the server's URL template. + */ + public ServerConfiguration(String URL, String description, Map variables) { + this.URL = URL; + this.description = description; + this.variables = variables; + } + + /** + * Format URL template using given variables. + * + * @param variables A map between a variable name and its value. + * @return Formatted URL. + */ + public String URL(Map variables) { + String url = this.URL; + + // go through variables and replace placeholders + for (Map.Entry variable: this.variables.entrySet()) { + String name = variable.getKey(); + ServerVariable serverVariable = variable.getValue(); + String value = serverVariable.defaultValue; + + if (variables != null && variables.containsKey(name)) { + value = variables.get(name); + if (serverVariable.enumValues.size() > 0 && !serverVariable.enumValues.contains(value)) { + throw new IllegalArgumentException("The variable " + name + " in the server URL has invalid value " + value + "."); + } + } + url = url.replace("{" + name + "}", value); + } + return url; + } + + /** + * Format URL template using default server variables. + * + * @return Formatted URL. + */ + public String URL() { + return URL(null); + } +} diff --git a/codegen-template/ServerVariable.mustache b/codegen-template/ServerVariable.mustache new file mode 100644 index 0000000..41246a7 --- /dev/null +++ b/codegen-template/ServerVariable.mustache @@ -0,0 +1,26 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +import java.util.HashSet; + +/** + * Representing a Server Variable for server URL template substitution. + */ +{{>generatedAnnotation}} +public class ServerVariable { + public String description; + public String defaultValue; + public HashSet enumValues = null; + + /** + * @param description A description for the server variable. + * @param defaultValue The default value to use for substitution. + * @param enumValues An enumeration of string values to be used if the substitution options are from a limited set. + */ + public ServerVariable(String description, String defaultValue, HashSet enumValues) { + this.description = description; + this.defaultValue = defaultValue; + this.enumValues = enumValues; + } +} diff --git a/codegen-template/StringUtil.mustache b/codegen-template/StringUtil.mustache new file mode 100644 index 0000000..e3d5d6e --- /dev/null +++ b/codegen-template/StringUtil.mustache @@ -0,0 +1,72 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +import java.util.Collection; +import java.util.Iterator; + +{{>generatedAnnotation}} +public class StringUtil { + /** + * Check if the given array contains the given value (with case-insensitive comparison). + * + * @param array The array + * @param value The value to search + * @return true if the array contains the value + */ + public static boolean containsIgnoreCase(String[] array, String value) { + for (String str : array) { + if (value == null && str == null) { + return true; + } + if (value != null && value.equalsIgnoreCase(str)) { + return true; + } + } + return false; + } + + /** + * Join an array of strings with the given separator. + *

+ * Note: This might be replaced by utility method from commons-lang or guava someday + * if one of those libraries is added as dependency. + *

+ * + * @param array The array of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(String[] array, String separator) { + int len = array.length; + if (len == 0) { + return ""; + } + + StringBuilder out = new StringBuilder(); + out.append(array[0]); + for (int i = 1; i < len; i++) { + out.append(separator).append(array[i]); + } + return out.toString(); + } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } +} diff --git a/codegen-template/additionalEnumTypeAnnotations.mustache b/codegen-template/additionalEnumTypeAnnotations.mustache new file mode 100644 index 0000000..aa52479 --- /dev/null +++ b/codegen-template/additionalEnumTypeAnnotations.mustache @@ -0,0 +1,2 @@ +{{#additionalEnumTypeAnnotations}}{{{.}}} +{{/additionalEnumTypeAnnotations}} \ No newline at end of file diff --git a/codegen-template/additionalModelTypeAnnotations.mustache b/codegen-template/additionalModelTypeAnnotations.mustache new file mode 100644 index 0000000..f4871c0 --- /dev/null +++ b/codegen-template/additionalModelTypeAnnotations.mustache @@ -0,0 +1,2 @@ +{{#additionalModelTypeAnnotations}}{{{.}}} +{{/additionalModelTypeAnnotations}} \ No newline at end of file diff --git a/codegen-template/additionalOneOfTypeAnnotations.mustache b/codegen-template/additionalOneOfTypeAnnotations.mustache new file mode 100644 index 0000000..283f8f9 --- /dev/null +++ b/codegen-template/additionalOneOfTypeAnnotations.mustache @@ -0,0 +1,2 @@ +{{#additionalOneOfTypeAnnotations}}{{{.}}} +{{/additionalOneOfTypeAnnotations}} \ No newline at end of file diff --git a/codegen-template/api.mustache b/codegen-template/api.mustache new file mode 100644 index 0000000..8ce0ae1 --- /dev/null +++ b/codegen-template/api.mustache @@ -0,0 +1,120 @@ +{{>licenseInfo}} +package {{package}}; + +import com.sun.jersey.api.client.GenericType; + +import {{invokerPackage}}.ApiException; +import {{invokerPackage}}.ApiClient; +import {{invokerPackage}}.Configuration; +import {{modelPackage}}.*; +import {{invokerPackage}}.Pair; + +{{#imports}}import {{import}}; +{{/imports}} + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +{{>generatedAnnotation}} +{{#operations}} +public class {{classname}} { + private ApiClient apiClient; + + public {{classname}}() { + this(Configuration.getDefaultApiClient()); + } + + public {{classname}}(ApiClient apiClient) { + this.apiClient = apiClient; + } + + public ApiClient getApiClient() { + return apiClient; + } + + public void setApiClient(ApiClient apiClient) { + this.apiClient = apiClient; + } + + {{#operation}} + /** + * {{summary}} + * {{notes}} + {{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}} + {{/allParams}} + {{#returnType}} + * @return {{.}} + {{/returnType}} + * @throws ApiException if fails to make API call + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} + */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} + public {{#returnType}}{{{.}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException { + Object localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; + {{#allParams}}{{#required}} + // verify the required parameter '{{paramName}}' is set + if ({{paramName}} == null) { + throw new ApiException(400, "Missing the required parameter '{{paramName}}' when calling {{operationId}}"); + } + {{/required}}{{/allParams}} + // create path and map variables + String localVarPath = "{{{path}}}"{{#pathParams}} + .replaceAll("\\{" + "{{baseName}}" + "\\}", apiClient.escapeString({{{paramName}}}.toString())){{/pathParams}}; + + // query params + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + {{#queryParams}} + {{#collectionFormat}}localVarCollectionQueryParams.addAll(apiClient.parameterToPairs("{{{.}}}", {{/collectionFormat}}{{^collectionFormat}}localVarQueryParams.addAll(apiClient.parameterToPair({{/collectionFormat}}"{{baseName}}", {{paramName}})); + {{/queryParams}} + + {{#headerParams}}if ({{paramName}} != null) + localVarHeaderParams.put("{{baseName}}", apiClient.parameterToString({{paramName}})); + {{/headerParams}} + + {{#cookieParams}}if ({{paramName}} != null) + localVarCookieParams.put("{{baseName}}", apiClient.parameterToString({{paramName}})); + {{/cookieParams}} + + {{#formParams}}if ({{paramName}} != null) + localVarFormParams.put("{{baseName}}", {{paramName}}); + {{/formParams}} + + final String[] localVarAccepts = { + {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}} + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + + final String[] localVarContentTypes = { + {{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}} + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + + String[] localVarAuthNames = new String[] { {{#authMethods}}"{{name}}"{{^-last}}, {{/-last}}{{/authMethods}} }; + + {{#returnType}} + GenericType<{{{returnType}}}> localVarReturnType = new GenericType<{{{returnType}}}>() {}; + return apiClient.invokeAPI(localVarPath, "{{httpMethod}}", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAccept, localVarContentType, localVarAuthNames, localVarReturnType); + {{/returnType}}{{^returnType}} + apiClient.invokeAPI(localVarPath, "{{httpMethod}}", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAccept, localVarContentType, localVarAuthNames, null); + {{/returnType}} + } + {{/operation}} +} +{{/operations}} diff --git a/codegen-template/apiException.mustache b/codegen-template/apiException.mustache new file mode 100644 index 0000000..8944867 --- /dev/null +++ b/codegen-template/apiException.mustache @@ -0,0 +1,91 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +import java.util.Map; +import java.util.List; + +{{>generatedAnnotation}} +public class ApiException extends{{#useRuntimeException}} RuntimeException {{/useRuntimeException}}{{^useRuntimeException}} Exception {{/useRuntimeException}}{ + private static final long serialVersionUID = 1L; + + private int code = 0; + private Map> responseHeaders = null; + private String responseBody = null; + + public ApiException() {} + + 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("Response Code: " + code + " Response Body: " + responseBody, (Throwable) null, code, responseHeaders, responseBody); + } + + public ApiException(int code, String message) { + super(message); + this.code = code; + } + + public ApiException(int code, String message, Map> responseHeaders, String responseBody) { + this(code, message); + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + /** + * Get the HTTP status code. + * + * @return HTTP status code + */ + public int getCode() { + return code; + } + + /** + * Get the HTTP response headers. + * + * @return A map of list of string + */ + public Map> getResponseHeaders() { + return responseHeaders; + } + + /** + * Get the HTTP response body. + * + * @return Response body in the form of string + */ + public String getResponseBody() { + return responseBody; + } + + @Override + public String toString() { + return "ApiException{" + + "code=" + code + + ", responseHeaders=" + responseHeaders + + ", responseBody='" + responseBody + '\'' + + '}'; + } +} diff --git a/codegen-template/apiOperation.mustache b/codegen-template/apiOperation.mustache new file mode 100644 index 0000000..97adb0e --- /dev/null +++ b/codegen-template/apiOperation.mustache @@ -0,0 +1,28 @@ +{{>licenseInfo}} +package {{invokerPackage}}; + +import io.swagger.v3.oas.models.Operation; + +public class ApiOperation { + private final String path; + private final String method; + private final Operation operation; + + public ApiOperation(String path, String method, Operation operation) { + this.path = path; + this.method = method; + this.operation = operation; + } + + public Operation getOperation() { + return operation; + } + + public String getPath() { + return path; + } + + public String getMethod() { + return method; + } +} diff --git a/codegen-template/api_doc.mustache b/codegen-template/api_doc.mustache new file mode 100644 index 0000000..3d32039 --- /dev/null +++ b/codegen-template/api_doc.mustache @@ -0,0 +1,108 @@ +# {{classname}}{{#description}} + +{{.}}{{/description}} + +All URIs are relative to *{{basePath}}* + +| Method | HTTP request | Description | +|------------- | ------------- | -------------| +{{#operations}}{{#operation}}| [**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{commonPath}}{{path}} | {{summary}} | +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} + +## {{operationId}} + +> {{#returnType}}{{.}} {{/returnType}}{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) + +{{summary}}{{#notes}} + +{{.}}{{/notes}} + +### Example + +```java +// Import classes: +import {{{invokerPackage}}}.ApiClient; +import {{{invokerPackage}}}.ApiException; +import {{{invokerPackage}}}.Configuration;{{#hasAuthMethods}} +import {{{invokerPackage}}}.auth.*;{{/hasAuthMethods}} +import {{{invokerPackage}}}.models.*; +import {{{package}}}.{{{classname}}}; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("{{{basePath}}}"); + {{#hasAuthMethods}} + {{#authMethods}}{{#isBasic}}{{#isBasicBasic}} + // Configure HTTP basic authorization: {{{name}}} + HttpBasicAuth {{{name}}} = (HttpBasicAuth) defaultClient.getAuthentication("{{{name}}}"); + {{{name}}}.setUsername("YOUR USERNAME"); + {{{name}}}.setPassword("YOUR PASSWORD");{{/isBasicBasic}}{{#isBasicBearer}} + // Configure HTTP bearer authorization: {{{name}}} + HttpBearerAuth {{{name}}} = (HttpBearerAuth) defaultClient.getAuthentication("{{{name}}}"); + {{{name}}}.setBearerToken("BEARER TOKEN");{{/isBasicBearer}}{{/isBasic}}{{#isApiKey}} + // Configure API key authorization: {{{name}}} + ApiKeyAuth {{{name}}} = (ApiKeyAuth) defaultClient.getAuthentication("{{{name}}}"); + {{{name}}}.setApiKey("YOUR API KEY"); + // Uncomment the following line to set a prefix for the API key, e.g. "Token" (defaults to null) + //{{{name}}}.setApiKeyPrefix("Token");{{/isApiKey}}{{#isOAuth}} + // Configure OAuth2 access token for authorization: {{{name}}} + OAuth {{{name}}} = (OAuth) defaultClient.getAuthentication("{{{name}}}"); + {{{name}}}.setAccessToken("YOUR ACCESS TOKEN");{{/isOAuth}} + {{/authMethods}} + {{/hasAuthMethods}} + + {{{classname}}} apiInstance = new {{{classname}}}(defaultClient); + {{#allParams}} + {{{dataType}}} {{{paramName}}} = {{{example}}}; // {{{dataType}}} | {{{description}}} + {{/allParams}} + try { + {{#returnType}}{{{.}}} result = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}});{{#returnType}} + System.out.println(result);{{/returnType}} + } catch (ApiException e) { + System.err.println("Exception when calling {{{classname}}}#{{{operationId}}}"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} +| Name | Type | Description | Notes | +|------------- | ------------- | ------------- | -------------|{{/-last}}{{/allParams}} +{{#allParams}}| **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{^isContainer}}{{#defaultValue}} [default to {{.}}]{{/defaultValue}}{{/isContainer}}{{#allowableValues}} [enum: {{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}} | +{{/allParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{returnType}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}null (empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{name}}](../README.md#{{name}}){{^-last}}, {{/-last}}{{/authMethods}} + +### HTTP request headers + +- **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} +- **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}} + +{{#responses.0}} + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +{{#responses}} +| **{{code}}** | {{message}} | {{#headers}} * {{baseName}} - {{description}}
{{/headers}}{{^headers.0}} - {{/headers.0}} | +{{/responses}} +{{/responses.0}} + +{{/operation}} +{{/operations}} diff --git a/codegen-template/api_test.mustache b/codegen-template/api_test.mustache new file mode 100644 index 0000000..155f46e --- /dev/null +++ b/codegen-template/api_test.mustache @@ -0,0 +1,51 @@ +{{>licenseInfo}} + +package {{package}}; + +import {{invokerPackage}}.ApiException; +{{#imports}}import {{import}}; +{{/imports}} +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * API tests for {{classname}} + */ +class {{classname}}Test { + + private final {{classname}} api = new {{classname}}(); + + {{#operations}} + {{#operation}} + /** + {{#summary}} + * {{.}} + * + {{/summary}} + {{#notes}} + * {{.}} + * + {{/notes}} + * @throws ApiException + * if the Api call fails + */ + @Test + void {{operationId}}Test() throws ApiException { + //{{#allParams}} + //{{{dataType}}} {{paramName}} = null; + //{{/allParams}} + //{{#returnType}}{{{.}}} response = {{/returnType}}api.{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}); + + // TODO: test validations + } + {{/operation}} + {{/operations}} +} diff --git a/codegen-template/beanValidation.mustache b/codegen-template/beanValidation.mustache new file mode 100644 index 0000000..47f7109 --- /dev/null +++ b/codegen-template/beanValidation.mustache @@ -0,0 +1,18 @@ +{{#required}} +{{^isReadOnly}} + @NotNull +{{/isReadOnly}} +{{/required}} +{{#isContainer}} +{{^isPrimitiveType}} +{{^isEnum}} + @Valid +{{/isEnum}} +{{/isPrimitiveType}} +{{/isContainer}} +{{^isContainer}} +{{^isPrimitiveType}} + @Valid +{{/isPrimitiveType}} +{{/isContainer}} +{{>beanValidationCore}} \ No newline at end of file diff --git a/codegen-template/beanValidationCore.mustache b/codegen-template/beanValidationCore.mustache new file mode 100644 index 0000000..ff4ffe2 --- /dev/null +++ b/codegen-template/beanValidationCore.mustache @@ -0,0 +1,20 @@ +{{#pattern}}{{^isByteArray}} @Pattern(regexp="{{{pattern}}}"){{/isByteArray}}{{/pattern}}{{! +minLength && maxLength set +}}{{#minLength}}{{#maxLength}} @Size(min={{minLength}},max={{maxLength}}){{/maxLength}}{{/minLength}}{{! +minLength set, maxLength not +}}{{#minLength}}{{^maxLength}} @Size(min={{minLength}}){{/maxLength}}{{/minLength}}{{! +minLength not set, maxLength set +}}{{^minLength}}{{#maxLength}} @Size(max={{.}}){{/maxLength}}{{/minLength}}{{! +@Size: minItems && maxItems set +}}{{#minItems}}{{#maxItems}} @Size(min={{minItems}},max={{maxItems}}){{/maxItems}}{{/minItems}}{{! +@Size: minItems set, maxItems not +}}{{#minItems}}{{^maxItems}} @Size(min={{minItems}}){{/maxItems}}{{/minItems}}{{! +@Size: minItems not set && maxItems set +}}{{^minItems}}{{#maxItems}} @Size(max={{.}}){{/maxItems}}{{/minItems}}{{! +check for integer or long / all others=decimal type with @Decimal* +isInteger set +}}{{#isInteger}}{{#minimum}} @Min({{.}}){{/minimum}}{{#maximum}} @Max({{.}}){{/maximum}}{{/isInteger}}{{! +isLong set +}}{{#isLong}}{{#minimum}} @Min({{.}}L){{/minimum}}{{#maximum}} @Max({{.}}L){{/maximum}}{{/isLong}}{{! +Not Integer, not Long => we have a decimal value! +}}{{^isInteger}}{{^isLong}}{{#minimum}} @DecimalMin({{#exclusiveMinimum}}value={{/exclusiveMinimum}}"{{minimum}}"{{#exclusiveMinimum}},inclusive=false{{/exclusiveMinimum}}){{/minimum}}{{#maximum}} @DecimalMax({{#exclusiveMaximum}}value={{/exclusiveMaximum}}"{{maximum}}"{{#exclusiveMaximum}},inclusive=false{{/exclusiveMaximum}}){{/maximum}}{{/isLong}}{{/isInteger}} \ No newline at end of file diff --git a/codegen-template/beanValidationQueryParams.mustache b/codegen-template/beanValidationQueryParams.mustache new file mode 100644 index 0000000..0f99bff --- /dev/null +++ b/codegen-template/beanValidationQueryParams.mustache @@ -0,0 +1 @@ +{{#required}} @NotNull {{/required}}{{>beanValidationCore}} \ No newline at end of file diff --git a/codegen-template/build.gradle.mustache b/codegen-template/build.gradle.mustache new file mode 100644 index 0000000..06f9bd5 --- /dev/null +++ b/codegen-template/build.gradle.mustache @@ -0,0 +1,168 @@ +apply plugin: 'idea' +apply plugin: 'eclipse' + +group = '{{groupId}}' +version = '{{artifactVersion}}' + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.+' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' + } +} + +repositories { + mavenCentral() +} + +if(hasProperty('target') && target == 'android') { + + apply plugin: 'com.android.library' + apply plugin: 'com.github.dcendents.android-maven' + + android { + compileSdkVersion 25 + buildToolsVersion '25.0.2' + defaultConfig { + minSdkVersion 14 + targetSdkVersion 25 + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + // Rename the aar correctly + libraryVariants.all { variant -> + variant.outputs.each { output -> + def outputFile = output.outputFile + if (outputFile != null && outputFile.name.endsWith('.aar')) { + def fileName = "${project.name}-${variant.baseName}-${version}.aar" + output.outputFile = new File(outputFile.parent, fileName) + } + } + } + + dependencies { + provided "jakarta.annotation:jakarta.annotation-api:$jakarta_annotation_version" + } + } + + afterEvaluate { + android.libraryVariants.all { variant -> + def task = project.tasks.create "jar${variant.name.capitalize()}", Jar + task.description = "Create jar artifact for ${variant.name}" + task.dependsOn variant.javaCompile + task.from variant.javaCompile.destinationDirectory + task.destinationDirectory = project.file("${project.buildDir}/outputs/jar") + task.archiveFileName = "${project.name}-${variant.baseName}-${version}.jar" + artifacts.add('archives', task); + } + } + + task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' + } + + artifacts { + archives sourcesJar + } + +} else { + + apply plugin: 'java' + apply plugin: 'maven-publish' + + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + + publishing { + publications { + maven(MavenPublication) { + artifactId = '{{artifactId}}' + + from components.java + } + } + } + + task execute(type:JavaExec) { + main = System.getProperty('mainClass') + classpath = sourceSets.main.runtimeClasspath + } + + task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource + } + + task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir + } + + artifacts { + archives sourcesJar + archives javadocJar + } +} + +ext { + swagger_annotations_version = "1.6.3" + jackson_version = "2.17.1" + jackson_databind_version = "2.17.1" + {{#openApiNullable}} + jackson_databind_nullable_version = "0.2.6" + {{/openApiNullable}} + jakarta_annotation_version = "1.3.5" + {{#useBeanValidation}} + bean_validation_version = "3.0.2" + {{/useBeanValidation}} + jersey_version = "1.19.4" + jodatime_version = "2.9.9" + junit_version = "5.10.2" +} + +dependencies { + implementation "io.swagger:swagger-annotations:$swagger_annotations_version" + implementation "com.google.code.findbugs:jsr305:3.0.2" + implementation "com.sun.jersey:jersey-client:$jersey_version" + implementation "com.sun.jersey.contribs:jersey-multipart:$jersey_version" + implementation "com.fasterxml.jackson.core:jackson-core:$jackson_version" + implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version" + implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_databind_version" + implementation "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson_version" + {{#openApiNullable}} + implementation "org.openapitools:jackson-databind-nullable:$jackson_databind_nullable_version" + {{/openApiNullable}} + {{#joda}} + implementation "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson_version" + {{/joda}} + implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version" + implementation "jakarta.annotation:jakarta.annotation-api:$jakarta_annotation_version" + {{#useBeanValidation}} + implementation "jakarta.validation:jakarta.validation-api:$bean_validation_version" + {{/useBeanValidation}} + testImplementation "junit:junit:$junit_version" + testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_version" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit_version" +} + +test { + // Enable JUnit 5 (Gradle 4.6+). + useJUnitPlatform() + + // Always run tests, even when nothing changed. + dependsOn 'cleanTest' + + // Show test results. + testLogging { + events "passed", "skipped", "failed" + } + +} diff --git a/codegen-template/build.sbt.mustache b/codegen-template/build.sbt.mustache new file mode 100644 index 0000000..4640904 --- /dev/null +++ b/codegen-template/build.sbt.mustache @@ -0,0 +1 @@ +# TODO diff --git a/codegen-template/enum_outer_doc.mustache b/codegen-template/enum_outer_doc.mustache new file mode 100644 index 0000000..20c512a --- /dev/null +++ b/codegen-template/enum_outer_doc.mustache @@ -0,0 +1,7 @@ +# {{classname}} + +## Enum + +{{#allowableValues}}{{#enumVars}} +* `{{name}}` (value: `{{{value}}}`) +{{/enumVars}}{{/allowableValues}} diff --git a/codegen-template/generatedAnnotation.mustache b/codegen-template/generatedAnnotation.mustache new file mode 100644 index 0000000..e05689d --- /dev/null +++ b/codegen-template/generatedAnnotation.mustache @@ -0,0 +1 @@ +@{{javaxPackage}}.annotation.Generated(value = "{{generatorClass}}"{{^hideGenerationTimestamp}}, date = "{{generatedDate}}"{{/hideGenerationTimestamp}}, comments = "Generator version: {{generatorVersion}}") \ No newline at end of file diff --git a/codegen-template/git_push.sh.mustache b/codegen-template/git_push.sh.mustache new file mode 100755 index 0000000..0e3776a --- /dev/null +++ b/codegen-template/git_push.sh.mustache @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="{{{gitHost}}}" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="{{{gitUserId}}}" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="{{{gitRepoId}}}" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="{{{releaseNote}}}" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/codegen-template/gitignore.mustache b/codegen-template/gitignore.mustache new file mode 100644 index 0000000..a530464 --- /dev/null +++ b/codegen-template/gitignore.mustache @@ -0,0 +1,21 @@ +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# exclude jar for gradle wrapper +!gradle/wrapper/*.jar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# build files +**/target +target +.gradle +build diff --git a/codegen-template/gradle-wrapper.properties.mustache b/codegen-template/gradle-wrapper.properties.mustache new file mode 100644 index 0000000..b82aa23 --- /dev/null +++ b/codegen-template/gradle-wrapper.properties.mustache @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/codegen-template/gradle.properties.mustache b/codegen-template/gradle.properties.mustache new file mode 100644 index 0000000..095ab31 --- /dev/null +++ b/codegen-template/gradle.properties.mustache @@ -0,0 +1,9 @@ +# This file is automatically generated by OpenAPI Generator (https://github.com/openAPITools/openapi-generator). +# To include other gradle properties as part of the code generation process, please use the `gradleProperties` option. +# +# Gradle properties reference: https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties +# For example, uncomment below to build for Android +#target = android +{{#gradleProperties}} +{{{.}}} +{{/gradleProperties}} diff --git a/codegen-template/gradlew.bat.mustache b/codegen-template/gradlew.bat.mustache new file mode 100644 index 0000000..25da30d --- /dev/null +++ b/codegen-template/gradlew.bat.mustache @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/codegen-template/gradlew.mustache b/codegen-template/gradlew.mustache new file mode 100755 index 0000000..9d0ce63 --- /dev/null +++ b/codegen-template/gradlew.mustache @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while +APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path +[ -h "$app_path" ] +do +ls=$( ls -ld "$app_path" ) +link=${ls#*' -> '} +case $link in #( +/*) app_path=$link ;; #( +*) app_path=$APP_HOME$link ;; +esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { +echo "$*" +} >&2 + +die () { +echo +echo "$*" +echo +exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( +CYGWIN* ) cygwin=true ;; #( +Darwin* ) darwin=true ;; #( +MSYS* | MINGW* ) msys=true ;; #( +NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then +if [ -x "$JAVA_HOME/jre/sh/java" ] ; then +# IBM's JDK on AIX uses strange locations for the executables +JAVACMD=$JAVA_HOME/jre/sh/java +else +JAVACMD=$JAVA_HOME/bin/java +fi +if [ ! -x "$JAVACMD" ] ; then +die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi +else +JAVACMD=java +if ! command -v java >/dev/null 2>&1 +then +die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then +case $MAX_FD in #( +max*) +# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. +# shellcheck disable=SC2039,SC3045 +MAX_FD=$( ulimit -H -n ) || +warn "Could not query maximum file descriptor limit" +esac +case $MAX_FD in #( +'' | soft) :;; #( +*) +# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. +# shellcheck disable=SC2039,SC3045 +ulimit -n "$MAX_FD" || +warn "Could not set maximum file descriptor limit to $MAX_FD" +esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then +APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) +CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + +JAVACMD=$( cygpath --unix "$JAVACMD" ) + +# Now convert the arguments - kludge to limit ourselves to /bin/sh +for arg do +if +case $arg in #( +-*) false ;; # don't mess with options #( +/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath +[ -e "$t" ] ;; #( +*) false ;; +esac +then +arg=$( cygpath --path --ignore --mixed "$arg" ) +fi +# Roll the args list around exactly as many times as the number of +# args, so each arg winds up back in the position where it started, but +# possibly modified. +# +# NB: a `for` loop captures its iteration list before it begins, so +# changing the positional parameters here affects neither the number of +# iterations, nor the values presented in `arg`. +shift # remove old arg +set -- "$@" "$arg" # push replacement arg +done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ +"-Dorg.gradle.appname=$APP_BASE_NAME" \ +-classpath "$CLASSPATH" \ +org.gradle.wrapper.GradleWrapperMain \ +"$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then +die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( +printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | +xargs -n1 | +sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | +tr '\n' ' ' +)" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/codegen-template/jackson_annotations.mustache b/codegen-template/jackson_annotations.mustache new file mode 100644 index 0000000..4fbaf98 --- /dev/null +++ b/codegen-template/jackson_annotations.mustache @@ -0,0 +1,14 @@ +{{! + If this is map and items are nullable, make sure that nulls are included. + To determine what JsonInclude.Include method to use, consider the following: + * If the field is required, always include it, even if it is null. + * Else use custom behaviour, IOW use whatever is defined on the object mapper + }} + @JsonProperty(JSON_PROPERTY_{{nameInSnakeCase}}) + @JsonInclude({{#isMap}}{{#items.isNullable}}content = JsonInclude.Include.ALWAYS, {{/items.isNullable}}{{/isMap}}value = JsonInclude.Include.{{#required}}ALWAYS{{/required}}{{^required}}USE_DEFAULTS{{/required}}) +{{#withXml}} + @JacksonXmlProperty(localName = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#isXmlAttribute}}, isAttribute = true{{/isXmlAttribute}}{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}}) + {{#isContainer}} + @JacksonXmlElementWrapper({{#isXmlWrapped}}localName = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}", {{#xmlNamespace}}namespace = "{{.}}", {{/xmlNamespace}}{{/isXmlWrapped}}useWrapping = {{isXmlWrapped}}) + {{/isContainer}} +{{/withXml}} \ No newline at end of file diff --git a/codegen-template/javaBuilder.mustache b/codegen-template/javaBuilder.mustache new file mode 100644 index 0000000..c027300 --- /dev/null +++ b/codegen-template/javaBuilder.mustache @@ -0,0 +1,82 @@ +public static class Builder {{#parentModel}}extends {{classname}}.Builder {{/parentModel}}{ + + private {{classname}} instance; + + public Builder() { + this(new {{classname}}()); + } + + protected Builder({{classname}} instance) { + {{#parentModel}} + super(instance); + {{/parentModel}} + this.instance = instance; + } + + {{#vars}} + public {{classname}}.Builder {{name}}({{{datatypeWithEnum}}} {{name}}) { + {{#vendorExtensions.x-is-jackson-optional-nullable}} + this.instance.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{name}}); + {{/vendorExtensions.x-is-jackson-optional-nullable}} + {{^vendorExtensions.x-is-jackson-optional-nullable}} + this.instance.{{name}} = {{name}}; + {{/vendorExtensions.x-is-jackson-optional-nullable}} + return this; + } + {{#vendorExtensions.x-is-jackson-optional-nullable}} + public {{classname}}.Builder {{name}}(JsonNullable<{{{datatypeWithEnum}}}> {{name}}) { + this.instance.{{name}} = {{name}}; + return this; + } + {{/vendorExtensions.x-is-jackson-optional-nullable}} + {{/vars}} + +{{#parentVars}} + public {{classname}}.Builder {{name}}({{{datatypeWithEnum}}} {{name}}) { // inherited: {{isInherited}} + super.{{name}}({{name}}); + return this; + } + {{#vendorExtensions.x-is-jackson-optional-nullable}} + public {{classname}}.Builder {{name}}(JsonNullable<{{{datatypeWithEnum}}}> {{name}}) { + this.instance.{{name}} = {{name}}; + return this; + } + {{/vendorExtensions.x-is-jackson-optional-nullable}} + + {{/parentVars}} + + /** + * returns a built {{classname}} instance. + * + * The builder is not reusable. + */ + public {{classname}} build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused{{#parentModel}} + super.build();{{/parentModel}} + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field. + */ + public static {{classname}}.Builder builder() { + return new {{classname}}.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public {{classname}}.Builder toBuilder() { + return new {{classname}}.Builder(){{#allVars}} + .{{name}}({{getter}}()){{/allVars}}; + } diff --git a/codegen-template/licenseInfo.mustache b/codegen-template/licenseInfo.mustache new file mode 100644 index 0000000..c66209f --- /dev/null +++ b/codegen-template/licenseInfo.mustache @@ -0,0 +1,11 @@ +/* + * {{{appName}}} + * {{{appDescription}}} + * + * {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}} + * {{#infoEmail}}Contact: {{{.}}}{{/infoEmail}} + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ diff --git a/codegen-template/manifest.mustache b/codegen-template/manifest.mustache new file mode 100644 index 0000000..f44bd07 --- /dev/null +++ b/codegen-template/manifest.mustache @@ -0,0 +1,3 @@ + + + diff --git a/codegen-template/maven.yml.mustache b/codegen-template/maven.yml.mustache new file mode 100644 index 0000000..69ad415 --- /dev/null +++ b/codegen-template/maven.yml.mustache @@ -0,0 +1,31 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven +# +# This file is auto-generated by OpenAPI Generator (https://openapi-generator.tech) + +name: Java CI with Maven + +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] + +jobs: + build: + name: Build {{{appName}}} + runs-on: ubuntu-latest + strategy: + matrix: + java: [ 17, 21 ] + steps: + - uses: actions/checkout@v4 + - name: Set up JDK + uses: actions/setup-java@v4 + with: + {{=< >=}} + java-version: ${{ matrix.java }} + distribution: 'temurin' + cache: maven + - name: Build with Maven + run: mvn -B package --no-transfer-progress --file pom.xml diff --git a/codegen-template/model.mustache b/codegen-template/model.mustache new file mode 100644 index 0000000..b504167 --- /dev/null +++ b/codegen-template/model.mustache @@ -0,0 +1,68 @@ +{{>licenseInfo}} + +package {{package}}; + +{{#useReflectionEqualsHashCode}} +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +{{/useReflectionEqualsHashCode}} +import java.util.Objects; +import java.util.Arrays; +{{#imports}} +import {{import}}; +{{/imports}} +{{#serializableModel}} +import java.io.Serializable; +{{/serializableModel}} +{{#jackson}} +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonTypeName; +{{#withXml}} +import com.fasterxml.jackson.dataformat.xml.annotation.*; +{{/withXml}} +{{#vendorExtensions.x-has-readonly-properties}} +import com.fasterxml.jackson.annotation.JsonCreator; +{{/vendorExtensions.x-has-readonly-properties}} +{{/jackson}} +{{#withXml}} +import {{javaxPackage}}.xml.bind.annotation.*; +import {{javaxPackage}}.xml.bind.annotation.adapters.*; +import io.github.threetenjaxb.core.*; +{{/withXml}} +{{#jsonb}} +import java.lang.reflect.Type; +import {{javaxPackage}}.json.bind.annotation.JsonbTypeDeserializer; +import {{javaxPackage}}.json.bind.annotation.JsonbTypeSerializer; +import {{javaxPackage}}.json.bind.serializer.DeserializationContext; +import {{javaxPackage}}.json.bind.serializer.JsonbDeserializer; +import {{javaxPackage}}.json.bind.serializer.JsonbSerializer; +import {{javaxPackage}}.json.bind.serializer.SerializationContext; +import {{javaxPackage}}.json.stream.JsonGenerator; +import {{javaxPackage}}.json.stream.JsonParser; +import {{javaxPackage}}.json.bind.annotation.JsonbProperty; +{{#vendorExtensions.x-has-readonly-properties}} +import {{javaxPackage}}.json.bind.annotation.JsonbCreator; +{{/vendorExtensions.x-has-readonly-properties}} +{{/jsonb}} +{{#parcelableModel}} +import android.os.Parcelable; +import android.os.Parcel; +{{/parcelableModel}} +{{#useBeanValidation}} +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; +{{/useBeanValidation}} +{{#performBeanValidation}} +import org.hibernate.validator.constraints.*; +{{/performBeanValidation}} +{{#supportUrlQuery}} +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.StringJoiner; +{{/supportUrlQuery}} + +{{#models}} +{{#model}} +{{#isEnum}}{{>modelEnum}}{{/isEnum}}{{^isEnum}}{{#vendorExtensions.x-is-one-of-interface}}{{>oneof_interface}}{{/vendorExtensions.x-is-one-of-interface}}{{^vendorExtensions.x-is-one-of-interface}}{{>pojo}}{{/vendorExtensions.x-is-one-of-interface}}{{/isEnum}} +{{/model}} +{{/models}} diff --git a/codegen-template/modelEnum.mustache b/codegen-template/modelEnum.mustache new file mode 100644 index 0000000..d1ba359 --- /dev/null +++ b/codegen-template/modelEnum.mustache @@ -0,0 +1,120 @@ +{{#jackson}} +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +{{/jackson}} +{{#gson}} +import java.io.IOException; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +{{/gson}} +{{#isUri}} +import java.net.URI; +{{/isUri}} + +/** + * {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}} + */ +{{#gson}} +@JsonAdapter({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.Adapter.class) +{{/gson}} +{{#jsonb}} +@JsonbTypeSerializer({{datatypeWithEnum}}.Serializer.class) +@JsonbTypeDeserializer({{datatypeWithEnum}}.Deserializer.class) +{{/jsonb}} +{{>additionalEnumTypeAnnotations}}public enum {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} { + {{#allowableValues}}{{#enumVars}} + {{#enumDescription}} + /** + * {{.}} + */ + {{/enumDescription}} + {{#withXml}} + @XmlEnumValue({{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isLong}}"{{/isLong}}{{#isFloat}}"{{/isFloat}}{{{value}}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isLong}}"{{/isLong}}{{#isFloat}}"{{/isFloat}}) + {{/withXml}} + {{{name}}}({{{value}}}){{^-last}}, + {{/-last}}{{#-last}};{{/-last}}{{/enumVars}}{{/allowableValues}} + + private {{{dataType}}} value; + + {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}({{{dataType}}} value) { + this.value = value; + } + +{{#jackson}} + @JsonValue +{{/jackson}} + public {{{dataType}}} getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + +{{#jackson}} + @JsonCreator +{{/jackson}} + public static {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue({{{dataType}}} value) { + for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { + if (b.value.{{^isString}}equals{{/isString}}{{#isString}}{{#useEnumCaseInsensitive}}equalsIgnoreCase{{/useEnumCaseInsensitive}}{{^useEnumCaseInsensitive}}equals{{/useEnumCaseInsensitive}}{{/isString}}(value)) { + return b; + } + } + {{#isNullable}}return null;{{/isNullable}}{{^isNullable}}{{#enumUnknownDefaultCase}}{{#allowableValues}}{{#enumVars}}{{#-last}}return {{{name}}};{{/-last}}{{/enumVars}}{{/allowableValues}}{{/enumUnknownDefaultCase}}{{^enumUnknownDefaultCase}}throw new IllegalArgumentException("Unexpected value '" + value + "'");{{/enumUnknownDefaultCase}}{{/isNullable}} + } +{{#gson}} + + public static class Adapter extends TypeAdapter<{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { + @Override + public void write(final JsonWriter jsonWriter, final {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} enumeration) throws IOException { + jsonWriter.value(enumeration.getValue(){{#isUri}}.toASCIIString(){{/isUri}}); + } + + @Override + public {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} read(final JsonReader jsonReader) throws IOException { + {{^isNumber}}{{{dataType}}}{{/isNumber}}{{#isNumber}}String{{/isNumber}} value = {{#isFloat}}(float){{/isFloat}}{{#isUri}}URI.create({{/isUri}}jsonReader.{{#isNumber}}nextString(){{/isNumber}}{{#isInteger}}nextInt(){{/isInteger}}{{#isUri}}nextString()){{/isUri}}{{^isNumber}}{{^isInteger}}{{^isUri}}{{#isFloat}}nextDouble{{/isFloat}}{{^isFloat}}next{{{dataType}}}{{/isFloat}}(){{/isUri}}{{/isInteger}}{{/isNumber}}; + return {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}.fromValue({{#isNumber}}new BigDecimal({{/isNumber}}value{{#isNumber}}){{/isNumber}}); + } + } +{{/gson}} +{{#jsonb}} + + public static final class Deserializer implements JsonbDeserializer<{{datatypeWithEnum}}> { + @Override + public {{datatypeWithEnum}} deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { + if (String.valueOf(b.value).equals(parser.getString())) { + return b; + } + } + {{#useNullForUnknownEnumValue}}return null;{{/useNullForUnknownEnumValue}}{{^useNullForUnknownEnumValue}}throw new IllegalArgumentException("Unexpected value '" + parser.getString() + "'");{{/useNullForUnknownEnumValue}} + } + } + + public static final class Serializer implements JsonbSerializer<{{datatypeWithEnum}}> { + @Override + public void serialize({{datatypeWithEnum}} obj, JsonGenerator generator, SerializationContext ctx) { + generator.write(obj.value); + } + } +{{/jsonb}} +{{#supportUrlQuery}} + + /** + * Convert the instance into URL query string. + * + * @param prefix prefix of the query string + * @return URL query string + */ + public String toUrlQueryString(String prefix) { + if (prefix == null) { + prefix = ""; + } + + return String.format("%s=%s", prefix, this.toString()); + } +{{/supportUrlQuery}} +} diff --git a/codegen-template/modelInnerEnum.mustache b/codegen-template/modelInnerEnum.mustache new file mode 100644 index 0000000..0096d84 --- /dev/null +++ b/codegen-template/modelInnerEnum.mustache @@ -0,0 +1,95 @@ + /** + * {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}} + */ +{{#gson}} + @JsonAdapter({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}.Adapter.class) +{{/gson}} +{{#jsonb}} + @JsonbTypeSerializer({{datatypeWithEnum}}.Serializer.class) + @JsonbTypeDeserializer({{datatypeWithEnum}}.Deserializer.class) +{{/jsonb}} +{{#withXml}} + @XmlType(name="{{datatypeWithEnum}}") + @XmlEnum({{dataType}}.class) +{{/withXml}} + {{>additionalEnumTypeAnnotations}}public enum {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} { + {{#allowableValues}} + {{#enumVars}} + {{#enumDescription}} + /** + * {{.}} + */ + {{/enumDescription}} + {{#withXml}} + @XmlEnumValue({{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isLong}}"{{/isLong}}{{#isFloat}}"{{/isFloat}}{{{value}}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isLong}}"{{/isLong}}{{#isFloat}}"{{/isFloat}}) + {{/withXml}} + {{{name}}}({{{value}}}){{^-last}}, + {{/-last}}{{#-last}};{{/-last}} + {{/enumVars}} + {{/allowableValues}} + + private {{{dataType}}} value; + + {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}({{{dataType}}} value) { + this.value = value; + } + +{{#jackson}} + @JsonValue +{{/jackson}} + public {{{dataType}}} getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + +{{#jackson}} + @JsonCreator +{{/jackson}} + public static {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue({{{dataType}}} value) { + for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { + if (b.value.{{^isString}}equals{{/isString}}{{#isString}}{{#useEnumCaseInsensitive}}equalsIgnoreCase{{/useEnumCaseInsensitive}}{{^useEnumCaseInsensitive}}equals{{/useEnumCaseInsensitive}}{{/isString}}(value)) { + return b; + } + } + {{#isNullable}}return null;{{/isNullable}}{{^isNullable}}{{#enumUnknownDefaultCase}}{{#allowableValues}}{{#enumVars}}{{#-last}}return {{{name}}};{{/-last}}{{/enumVars}}{{/allowableValues}}{{/enumUnknownDefaultCase}}{{^enumUnknownDefaultCase}}throw new IllegalArgumentException("Unexpected value '" + value + "'");{{/enumUnknownDefaultCase}}{{/isNullable}} + } +{{#gson}} + + public static class Adapter extends TypeAdapter<{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}> { + @Override + public void write(final JsonWriter jsonWriter, final {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} enumeration) throws IOException { + jsonWriter.value(enumeration.getValue(){{#isUri}}.toASCIIString(){{/isUri}}); + } + + @Override + public {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} read(final JsonReader jsonReader) throws IOException { + {{^isNumber}}{{{dataType}}}{{/isNumber}}{{#isNumber}}String{{/isNumber}} value = {{#isFloat}}(float){{/isFloat}} {{#isUri}}URI.create({{/isUri}}jsonReader.{{#isNumber}}nextString(){{/isNumber}}{{#isInteger}}nextInt(){{/isInteger}}{{#isUri}}nextString()){{/isUri}}{{^isNumber}}{{^isInteger}}{{^isUri}}{{#isFloat}}nextDouble{{/isFloat}}{{^isFloat}}next{{{dataType}}}{{/isFloat}}(){{/isUri}}{{/isInteger}}{{/isNumber}}; + return {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}.fromValue({{#isNumber}}new BigDecimal({{/isNumber}}value{{#isNumber}}){{/isNumber}}); + } + } +{{/gson}} +{{#jsonb}} + public static final class Deserializer implements JsonbDeserializer<{{datatypeWithEnum}}> { + @Override + public {{datatypeWithEnum}} deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { + if (String.valueOf(b.value).equals(parser.getString())) { + return b; + } + } + {{#useNullForUnknownEnumValue}}return null;{{/useNullForUnknownEnumValue}}{{^useNullForUnknownEnumValue}}throw new IllegalArgumentException("Unexpected value '" + parser.getString() + "'");{{/useNullForUnknownEnumValue}} + } + } + + public static final class Serializer implements JsonbSerializer<{{datatypeWithEnum}}> { + @Override + public void serialize({{datatypeWithEnum}} obj, JsonGenerator generator, SerializationContext ctx) { + generator.write(obj.value); + } + } +{{/jsonb}} + } diff --git a/codegen-template/model_doc.mustache b/codegen-template/model_doc.mustache new file mode 100644 index 0000000..9a7fe14 --- /dev/null +++ b/codegen-template/model_doc.mustache @@ -0,0 +1,4 @@ +{{#models}}{{#model}} + +{{#isEnum}}{{>enum_outer_doc}}{{/isEnum}}{{^isEnum}}{{>pojo_doc}}{{/isEnum}} +{{/model}}{{/models}} diff --git a/codegen-template/model_test.mustache b/codegen-template/model_test.mustache new file mode 100644 index 0000000..931a5da --- /dev/null +++ b/codegen-template/model_test.mustache @@ -0,0 +1,43 @@ +{{>licenseInfo}} + +package {{package}}; + +{{#imports}}import {{import}}; +{{/imports}} +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * Model tests for {{classname}} + */ +class {{classname}}Test { + {{#models}} + {{#model}} + {{^vendorExtensions.x-is-one-of-interface}} + {{^isEnum}} + private final {{classname}} model = new {{classname}}(); + + {{/isEnum}} + /** + * Model tests for {{classname}} + */ + @Test + void test{{classname}}() { + // TODO: test {{classname}} + } + + {{#allVars}} + /** + * Test the property '{{name}}' + */ + @Test + void {{name}}Test() { + // TODO: test {{name}} + } + + {{/allVars}} + {{/vendorExtensions.x-is-one-of-interface}} + {{/model}} + {{/models}} +} diff --git a/codegen-template/oneof_interface.mustache b/codegen-template/oneof_interface.mustache new file mode 100644 index 0000000..d672772 --- /dev/null +++ b/codegen-template/oneof_interface.mustache @@ -0,0 +1,6 @@ +{{>additionalOneOfTypeAnnotations}}{{>generatedAnnotation}}{{>typeInfoAnnotation}}{{>xmlAnnotation}} +public interface {{classname}} {{#vendorExtensions.x-implements}}{{#-first}}extends {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} { + {{#discriminator}} + public {{propertyType}} {{propertyGetter}}(); + {{/discriminator}} +} diff --git a/codegen-template/openapi.mustache b/codegen-template/openapi.mustache new file mode 100644 index 0000000..34fbb53 --- /dev/null +++ b/codegen-template/openapi.mustache @@ -0,0 +1 @@ +{{{openapi-yaml}}} diff --git a/codegen-template/pojo.mustache b/codegen-template/pojo.mustache new file mode 100644 index 0000000..05be7e5 --- /dev/null +++ b/codegen-template/pojo.mustache @@ -0,0 +1,624 @@ +/** + * {{description}}{{^description}}{{classname}}{{/description}}{{#isDeprecated}} + * @deprecated{{/isDeprecated}} + */{{#isDeprecated}} +@Deprecated{{/isDeprecated}} +{{#swagger1AnnotationLibrary}} +{{#description}} +@ApiModel(description = "{{{.}}}") +{{/description}} +{{/swagger1AnnotationLibrary}} +{{#swagger2AnnotationLibrary}} +{{#description}} +@Schema(description = "{{{.}}}") +{{/description}} +{{/swagger2AnnotationLibrary}} +{{#jackson}} +@JsonPropertyOrder({ +{{#vars}} + {{classname}}.JSON_PROPERTY_{{nameInSnakeCase}}{{^-last}},{{/-last}} +{{/vars}} +}) +{{#isClassnameSanitized}} +{{^hasDiscriminatorWithNonEmptyMapping}} +@JsonTypeName("{{name}}") +{{/hasDiscriminatorWithNonEmptyMapping}} +{{/isClassnameSanitized}} +{{/jackson}} +{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}} +{{#vendorExtensions.x-class-extra-annotation}} +{{{vendorExtensions.x-class-extra-annotation}}} +{{/vendorExtensions.x-class-extra-annotation}} +public class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{{#vendorExtensions.x-implements}}{{#-first}}implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{#-last}} {{/-last}}{{/vendorExtensions.x-implements}}{ +{{#serializableModel}} + private static final long serialVersionUID = 1L; + +{{/serializableModel}} + {{#vars}} + {{#isEnum}} + {{^isContainer}} +{{>modelInnerEnum}} + {{/isContainer}} + {{#isContainer}} + {{#mostInnerItems}} +{{>modelInnerEnum}} + {{/mostInnerItems}} + {{/isContainer}} + {{/isEnum}} + {{#gson}} + public static final String SERIALIZED_NAME_{{nameInSnakeCase}} = "{{baseName}}"; + {{/gson}} + {{#jackson}} + public static final String JSON_PROPERTY_{{nameInSnakeCase}} = "{{baseName}}"; + {{/jackson}} + {{#withXml}} + @Xml{{#isXmlAttribute}}Attribute{{/isXmlAttribute}}{{^isXmlAttribute}}Element{{/isXmlAttribute}}(name = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}}) + {{#isXmlWrapped}} + @XmlElementWrapper(name = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}}) + {{/isXmlWrapped}} + {{^isXmlAttribute}} + {{#isDateTime}} + @XmlJavaTypeAdapter(OffsetDateTimeXmlAdapter.class) + {{/isDateTime}} + {{/isXmlAttribute}} + {{/withXml}} + {{#gson}} + @SerializedName(SERIALIZED_NAME_{{nameInSnakeCase}}) + {{/gson}} + {{#vendorExtensions.x-field-extra-annotation}} + {{{vendorExtensions.x-field-extra-annotation}}} + {{/vendorExtensions.x-field-extra-annotation}} + {{#vendorExtensions.x-is-jackson-optional-nullable}} + {{#isContainer}} + {{#hasChildren}}protected{{/hasChildren}}{{^hasChildren}}private{{/hasChildren}} JsonNullable<{{{datatypeWithEnum}}}> {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>undefined(); + {{/isContainer}} + {{^isContainer}} + {{#hasChildren}}protected{{/hasChildren}}{{^hasChildren}}private{{/hasChildren}} JsonNullable<{{{datatypeWithEnum}}}> {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>{{#defaultValue}}of({{{.}}}){{/defaultValue}}{{^defaultValue}}undefined(){{/defaultValue}}; + {{/isContainer}} + {{/vendorExtensions.x-is-jackson-optional-nullable}} + {{^vendorExtensions.x-is-jackson-optional-nullable}} + {{#isContainer}} + {{#hasChildren}}protected{{/hasChildren}}{{^hasChildren}}private{{/hasChildren}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}; + {{/isContainer}} + {{^isContainer}} + {{#hasChildren}}protected{{/hasChildren}}{{^hasChildren}}private{{/hasChildren}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}; + {{/isContainer}} + {{/vendorExtensions.x-is-jackson-optional-nullable}} + + {{/vars}} + public {{classname}}() { + {{#parent}} + {{#parcelableModel}} + super();{{/parcelableModel}} + {{/parent}} + {{#gson}} + {{#discriminator}} + {{#discriminator.isEnum}} + this.{{{discriminatorName}}} = this.getClass().getSimpleName(); + {{/discriminator.isEnum}} + {{/discriminator}} + {{/gson}} + } + {{#vendorExtensions.x-has-readonly-properties}} + {{^withXml}} + /** + * Constructor with only readonly parameters{{#generateConstructorWithAllArgs}}{{^vendorExtensions.x-java-all-args-constructor}} and all parameters{{/vendorExtensions.x-java-all-args-constructor}}{{/generateConstructorWithAllArgs}} + */ + {{#jsonb}}@JsonbCreator{{/jsonb}}{{#jackson}}@JsonCreator{{/jackson}} + public {{classname}}( + {{#readOnlyVars}} + {{#jsonb}}@JsonbProperty(value = "{{baseName}}"{{^required}}, nullable = true{{/required}}){{/jsonb}}{{#jackson}}@JsonProperty(JSON_PROPERTY_{{nameInSnakeCase}}){{/jackson}} {{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}} + {{/readOnlyVars}} + ) { + this(); + {{#readOnlyVars}} + this.{{name}} = {{#vendorExtensions.x-is-jackson-optional-nullable}}{{name}} == null ? JsonNullable.<{{{datatypeWithEnum}}}>undefined() : JsonNullable.of({{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{name}}{{/vendorExtensions.x-is-jackson-optional-nullable}}; + {{/readOnlyVars}} + } + {{/withXml}} + {{/vendorExtensions.x-has-readonly-properties}} +{{#vendorExtensions.x-java-all-args-constructor}} + + /** + * Constructor with all args parameters + */ + public {{classname}}({{#vendorExtensions.x-java-all-args-constructor-vars}}{{#jsonb}}@JsonbProperty(value = "{{baseName}}"{{^required}}, nullable = true{{/required}}){{/jsonb}}{{#jackson}}@JsonProperty(JSON_PROPERTY_{{nameInSnakeCase}}){{/jackson}} {{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-java-all-args-constructor-vars}}) { +{{#parent}} + super({{#parentVars}}{{name}}{{^-last}}, {{/-last}}{{/parentVars}}); +{{/parent}} + {{#vars}} + this.{{name}} = {{#vendorExtensions.x-is-jackson-optional-nullable}}{{name}} == null ? JsonNullable.<{{{datatypeWithEnum}}}>undefined() : JsonNullable.of({{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{name}}{{/vendorExtensions.x-is-jackson-optional-nullable}}; +{{/vars}} + } +{{/vendorExtensions.x-java-all-args-constructor}} + +{{#vars}} + {{^isReadOnly}} + public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) { + {{#vendorExtensions.x-is-jackson-optional-nullable}}this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{name}});{{/vendorExtensions.x-is-jackson-optional-nullable}} + {{^vendorExtensions.x-is-jackson-optional-nullable}}this.{{name}} = {{name}};{{/vendorExtensions.x-is-jackson-optional-nullable}} + return this; + } + {{#isArray}} + + public {{classname}} add{{nameInPascalCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { + {{#vendorExtensions.x-is-jackson-optional-nullable}} + if (this.{{name}} == null || !this.{{name}}.isPresent()) { + this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}}); + } + try { + this.{{name}}.get().add({{name}}Item); + } catch (java.util.NoSuchElementException e) { + // this can never happen, as we make sure above that the value is present + } + return this; + {{/vendorExtensions.x-is-jackson-optional-nullable}} + {{^vendorExtensions.x-is-jackson-optional-nullable}} + if (this.{{name}} == null) { + this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}}; + } + this.{{name}}.add({{name}}Item); + return this; + {{/vendorExtensions.x-is-jackson-optional-nullable}} + } + {{/isArray}} + {{#isMap}} + + public {{classname}} put{{nameInPascalCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) { + {{#vendorExtensions.x-is-jackson-optional-nullable}} + if (this.{{name}} == null || !this.{{name}}.isPresent()) { + this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{{defaultValue}}}{{^defaultValue}}new HashMap<>(){{/defaultValue}}); + } + try { + this.{{name}}.get().put(key, {{name}}Item); + } catch (java.util.NoSuchElementException e) { + // this can never happen, as we make sure above that the value is present + } + return this; + {{/vendorExtensions.x-is-jackson-optional-nullable}} + {{^vendorExtensions.x-is-jackson-optional-nullable}} + {{^required}} + if (this.{{name}} == null) { + this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new HashMap<>(){{/defaultValue}}; + } + {{/required}} + this.{{name}}.put(key, {{name}}Item); + return this; + {{/vendorExtensions.x-is-jackson-optional-nullable}} + } + {{/isMap}} + + {{/isReadOnly}} + /** + {{#description}} + * {{.}} + {{/description}} + {{^description}} + * Get {{name}} + {{/description}} + {{#minimum}} + * minimum: {{.}} + {{/minimum}} + {{#maximum}} + * maximum: {{.}} + {{/maximum}} + * @return {{name}} + {{#deprecated}} + * @deprecated + {{/deprecated}} + */ +{{#deprecated}} + @Deprecated +{{/deprecated}} +{{#required}} +{{#isNullable}} + @{{javaxPackage}}.annotation.Nullable +{{/isNullable}} +{{^isNullable}} + @{{javaxPackage}}.annotation.Nonnull +{{/isNullable}} +{{/required}} +{{^required}} + @{{javaxPackage}}.annotation.Nullable +{{/required}} +{{#jsonb}} + @JsonbProperty("{{baseName}}") +{{/jsonb}} +{{#useBeanValidation}} +{{>beanValidation}} +{{/useBeanValidation}} +{{#swagger1AnnotationLibrary}} + @ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}value = "{{{description}}}") +{{/swagger1AnnotationLibrary}} +{{#swagger2AnnotationLibrary}} + @Schema({{#example}}example = "{{{.}}}", {{/example}}requiredMode = {{#required}}Schema.RequiredMode.REQUIRED{{/required}}{{^required}}Schema.RequiredMode.NOT_REQUIRED{{/required}}, description = "{{{description}}}") +{{/swagger2AnnotationLibrary}} +{{#vendorExtensions.x-extra-annotation}} + {{{vendorExtensions.x-extra-annotation}}} +{{/vendorExtensions.x-extra-annotation}} +{{#vendorExtensions.x-is-jackson-optional-nullable}} + {{!unannotated, Jackson would pick this up automatically and add it *in addition* to the _JsonNullable getter field}} + @JsonIgnore +{{/vendorExtensions.x-is-jackson-optional-nullable}} +{{^vendorExtensions.x-is-jackson-optional-nullable}}{{#jackson}}{{> jackson_annotations}}{{/jackson}}{{/vendorExtensions.x-is-jackson-optional-nullable}} + public {{{datatypeWithEnum}}} {{getter}}() { + {{#vendorExtensions.x-is-jackson-optional-nullable}} + {{#isReadOnly}}{{! A readonly attribute doesn't have setter => jackson will set null directly if explicitly returned by API, so make sure we have an empty JsonNullable}} + if ({{name}} == null) { + {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>{{#defaultValue}}of({{{.}}}){{/defaultValue}}{{^defaultValue}}undefined(){{/defaultValue}}; + } + {{/isReadOnly}} + return {{name}}.orElse(null); + {{/vendorExtensions.x-is-jackson-optional-nullable}} + {{^vendorExtensions.x-is-jackson-optional-nullable}} + return {{name}}; + {{/vendorExtensions.x-is-jackson-optional-nullable}} + } + + {{#vendorExtensions.x-is-jackson-optional-nullable}} +{{> jackson_annotations}} + public JsonNullable<{{{datatypeWithEnum}}}> {{getter}}_JsonNullable() { + return {{name}}; + } + {{/vendorExtensions.x-is-jackson-optional-nullable}}{{#vendorExtensions.x-is-jackson-optional-nullable}} + @JsonProperty(JSON_PROPERTY_{{nameInSnakeCase}}) + {{#isReadOnly}}private{{/isReadOnly}}{{^isReadOnly}}public{{/isReadOnly}} void {{setter}}_JsonNullable(JsonNullable<{{{datatypeWithEnum}}}> {{name}}) { + {{! For getters/setters that have name differing from attribute name, we must include setter (albeit private) for jackson to be able to set the attribute}} + this.{{name}} = {{name}}; + } + {{/vendorExtensions.x-is-jackson-optional-nullable}} + + {{^isReadOnly}} +{{#vendorExtensions.x-setter-extra-annotation}} {{{vendorExtensions.x-setter-extra-annotation}}} +{{/vendorExtensions.x-setter-extra-annotation}}{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{> jackson_annotations}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}} public void {{setter}}({{{datatypeWithEnum}}} {{name}}) { + {{#vendorExtensions.x-is-jackson-optional-nullable}} + this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{name}}); + {{/vendorExtensions.x-is-jackson-optional-nullable}} + {{^vendorExtensions.x-is-jackson-optional-nullable}} + this.{{name}} = {{name}}; + {{/vendorExtensions.x-is-jackson-optional-nullable}} + } + {{/isReadOnly}} + + {{/vars}} + {{#parent}} + {{#readWriteVars}} + {{#isOverridden}} + @Override + public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) { + {{#vendorExtensions.x-is-jackson-optional-nullable}} + this.{{setter}}(JsonNullable.<{{{datatypeWithEnum}}}>of({{name}})); + {{/vendorExtensions.x-is-jackson-optional-nullable}} + {{^vendorExtensions.x-is-jackson-optional-nullable}} + this.{{setter}}({{name}}); + {{/vendorExtensions.x-is-jackson-optional-nullable}} + return this; + } + + {{/isOverridden}} + {{/readWriteVars}} + {{/parent}} + @Override + public boolean equals(Object o) { + {{#useReflectionEqualsHashCode}} + return EqualsBuilder.reflectionEquals(this, o, false, null, true); + {{/useReflectionEqualsHashCode}} + {{^useReflectionEqualsHashCode}} + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + }{{#hasVars}} + {{classname}} {{classVarName}} = ({{classname}}) o; + return {{#vars}}{{#vendorExtensions.x-is-jackson-optional-nullable}}equalsNullable(this.{{name}}, {{classVarName}}.{{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{#isByteArray}}Arrays{{/isByteArray}}{{^isByteArray}}Objects{{/isByteArray}}.equals(this.{{name}}, {{classVarName}}.{{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^-last}} && + {{/-last}}{{/vars}}{{#parent}} && + super.equals(o){{/parent}};{{/hasVars}}{{^hasVars}} + return {{#parent}}super.equals(o){{/parent}}{{^parent}}true{{/parent}};{{/hasVars}} + {{/useReflectionEqualsHashCode}} + }{{#vendorExtensions.x-jackson-optional-nullable-helpers}} + + private static boolean equalsNullable(JsonNullable a, JsonNullable b) { + return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); + }{{/vendorExtensions.x-jackson-optional-nullable-helpers}} + + @Override + public int hashCode() { + {{#useReflectionEqualsHashCode}} + return HashCodeBuilder.reflectionHashCode(this); + {{/useReflectionEqualsHashCode}} + {{^useReflectionEqualsHashCode}} + return Objects.hash({{#vars}}{{#vendorExtensions.x-is-jackson-optional-nullable}}hashCodeNullable({{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{^isByteArray}}{{name}}{{/isByteArray}}{{#isByteArray}}Arrays.hashCode({{name}}){{/isByteArray}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{^-last}}, {{/-last}}{{/vars}}{{#parent}}{{#hasVars}}, {{/hasVars}}super.hashCode(){{/parent}}); + {{/useReflectionEqualsHashCode}} + }{{#vendorExtensions.x-jackson-optional-nullable-helpers}} + + private static int hashCodeNullable(JsonNullable a) { + if (a == null) { + return 1; + } + return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; + }{{/vendorExtensions.x-jackson-optional-nullable-helpers}} + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class {{classname}} {\n"); + {{#parent}} + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); + {{/parent}} + {{#vars}} + sb.append(" {{name}}: ").append({{#isPassword}}"*"{{/isPassword}}{{^isPassword}}toIndentedString({{name}}){{/isPassword}}).append("\n"); + {{/vars}} + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private{{#jsonb}} static{{/jsonb}} String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +{{#supportUrlQuery}} + + /** + * Convert the instance into URL query string. + * + * @return URL query string + */ + public String toUrlQueryString() { + return toUrlQueryString(null); + } + + /** + * Convert the instance into URL query string. + * + * @param prefix prefix of the query string + * @return URL query string + */ + public String toUrlQueryString(String prefix) { + String suffix = ""; + String containerSuffix = ""; + String containerPrefix = ""; + if (prefix == null) { + // style=form, explode=true, e.g. /pet?name=cat&type=manx + prefix = ""; + } else { + // deepObject style e.g. /pet?id[name]=cat&id[type]=manx + prefix = prefix + "["; + suffix = "]"; + containerSuffix = "]"; + containerPrefix = "["; + } + + StringJoiner joiner = new StringJoiner("&"); + + {{#allVars}} + // add `{{baseName}}` to the URL query string + {{#isArray}} + {{#items.isPrimitiveType}} + {{#uniqueItems}} + if ({{getter}}() != null) { + int i = 0; + for ({{{items.dataType}}} _item : {{getter}}()) { + try { + joiner.add(String.format("%s{{baseName}}%s%s=%s", prefix, suffix, + "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, i, containerSuffix), + URLEncoder.encode(String.valueOf(_item), "UTF-8").replaceAll("\\+", "%20"))); + } catch (UnsupportedEncodingException e) { + // Should never happen, UTF-8 is always supported + throw new RuntimeException(e); + } + } + i++; + } + {{/uniqueItems}} + {{^uniqueItems}} + if ({{getter}}() != null) { + for (int i = 0; i < {{getter}}().size(); i++) { + try { + joiner.add(String.format("%s{{baseName}}%s%s=%s", prefix, suffix, + "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, i, containerSuffix), + URLEncoder.encode(String.valueOf({{getter}}().get(i)), "UTF-8").replaceAll("\\+", "%20"))); + } catch (UnsupportedEncodingException e) { + // Should never happen, UTF-8 is always supported + throw new RuntimeException(e); + } + } + } + {{/uniqueItems}} + {{/items.isPrimitiveType}} + {{^items.isPrimitiveType}} + {{#items.isModel}} + {{#uniqueItems}} + if ({{getter}}() != null) { + int i = 0; + for ({{{items.dataType}}} _item : {{getter}}()) { + if (_item != null) { + joiner.add(_item.toUrlQueryString(String.format("%s{{baseName}}%s%s", prefix, suffix, + "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, i, containerSuffix)))); + } + } + i++; + } + {{/uniqueItems}} + {{^uniqueItems}} + if ({{getter}}() != null) { + for (int i = 0; i < {{getter}}().size(); i++) { + if ({{getter}}().get(i) != null) { + joiner.add({{getter}}().get(i).toUrlQueryString(String.format("%s{{baseName}}%s%s", prefix, suffix, + "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, i, containerSuffix)))); + } + } + } + {{/uniqueItems}} + {{/items.isModel}} + {{^items.isModel}} + {{#uniqueItems}} + if ({{getter}}() != null) { + int i = 0; + for ({{{items.dataType}}} _item : {{getter}}()) { + if (_item != null) { + try { + joiner.add(String.format("%s{{baseName}}%s%s=%s", prefix, suffix, + "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, i, containerSuffix), + URLEncoder.encode(String.valueOf(_item), "UTF-8").replaceAll("\\+", "%20"))); + } catch (UnsupportedEncodingException e) { + // Should never happen, UTF-8 is always supported + throw new RuntimeException(e); + } + } + i++; + } + } + {{/uniqueItems}} + {{^uniqueItems}} + if ({{getter}}() != null) { + for (int i = 0; i < {{getter}}().size(); i++) { + if ({{getter}}().get(i) != null) { + try { + joiner.add(String.format("%s{{baseName}}%s%s=%s", prefix, suffix, + "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, i, containerSuffix), + URLEncoder.encode(String.valueOf({{getter}}().get(i)), "UTF-8").replaceAll("\\+", "%20"))); + } catch (UnsupportedEncodingException e) { + // Should never happen, UTF-8 is always supported + throw new RuntimeException(e); + } + } + } + } + {{/uniqueItems}} + {{/items.isModel}} + {{/items.isPrimitiveType}} + {{/isArray}} + {{^isArray}} + {{#isMap}} + {{^items.isModel}} + if ({{getter}}() != null) { + for (String _key : {{getter}}().keySet()) { + try { + joiner.add(String.format("%s{{baseName}}%s%s=%s", prefix, suffix, + "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, _key, containerSuffix), + {{getter}}().get(_key), URLEncoder.encode(String.valueOf({{getter}}().get(_key)), "UTF-8").replaceAll("\\+", "%20"))); + } catch (UnsupportedEncodingException e) { + // Should never happen, UTF-8 is always supported + throw new RuntimeException(e); + } + } + } + {{/items.isModel}} + {{#items.isModel}} + if ({{getter}}() != null) { + for (String _key : {{getter}}().keySet()) { + if ({{getter}}().get(_key) != null) { + joiner.add({{getter}}().get(_key).toUrlQueryString(String.format("%s{{baseName}}%s%s", prefix, suffix, + "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, _key, containerSuffix)))); + } + } + } + {{/items.isModel}} + {{/isMap}} + {{^isMap}} + {{#isPrimitiveType}} + if ({{getter}}() != null) { + try { + joiner.add(String.format("%s{{{baseName}}}%s=%s", prefix, suffix, URLEncoder.encode(String.valueOf({{{getter}}}()), "UTF-8").replaceAll("\\+", "%20"))); + } catch (UnsupportedEncodingException e) { + // Should never happen, UTF-8 is always supported + throw new RuntimeException(e); + } + } + {{/isPrimitiveType}} + {{^isPrimitiveType}} + {{#isModel}} + if ({{getter}}() != null) { + joiner.add({{getter}}().toUrlQueryString(prefix + "{{{baseName}}}" + suffix)); + } + {{/isModel}} + {{^isModel}} + if ({{getter}}() != null) { + try { + joiner.add(String.format("%s{{{baseName}}}%s=%s", prefix, suffix, URLEncoder.encode(String.valueOf({{{getter}}}()), "UTF-8").replaceAll("\\+", "%20"))); + } catch (UnsupportedEncodingException e) { + // Should never happen, UTF-8 is always supported + throw new RuntimeException(e); + } + } + {{/isModel}} + {{/isPrimitiveType}} + {{/isMap}} + {{/isArray}} + + {{/allVars}} + return joiner.toString(); + } +{{/supportUrlQuery}} +{{#parcelableModel}} + + public void writeToParcel(Parcel out, int flags) { +{{#model}} +{{#isArray}} + out.writeList(this); +{{/isArray}} +{{^isArray}} +{{#parent}} + super.writeToParcel(out, flags); +{{/parent}} +{{#vars}} + out.writeValue({{name}}); +{{/vars}} +{{/isArray}} +{{/model}} + } + + {{classname}}(Parcel in) { +{{#isArray}} + in.readTypedList(this, {{arrayModelType}}.CREATOR); +{{/isArray}} +{{^isArray}} +{{#parent}} + super(in); +{{/parent}} +{{#vars}} +{{#isPrimitiveType}} + {{name}} = ({{{datatypeWithEnum}}})in.readValue(null); +{{/isPrimitiveType}} +{{^isPrimitiveType}} + {{name}} = ({{{datatypeWithEnum}}})in.readValue({{complexType}}.class.getClassLoader()); +{{/isPrimitiveType}} +{{/vars}} +{{/isArray}} + } + + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<{{classname}}> CREATOR = new Parcelable.Creator<{{classname}}>() { + public {{classname}} createFromParcel(Parcel in) { +{{#model}} +{{#isArray}} + {{classname}} result = new {{classname}}(); + result.addAll(in.readArrayList({{arrayModelType}}.class.getClassLoader())); + return result; +{{/isArray}} +{{^isArray}} + return new {{classname}}(in); +{{/isArray}} +{{/model}} + } + public {{classname}}[] newArray(int size) { + return new {{classname}}[size]; + } + }; +{{/parcelableModel}} +{{#generateBuilders}} + + {{>javaBuilder}} +{{/generateBuilders}} + +} diff --git a/codegen-template/pojo_doc.mustache b/codegen-template/pojo_doc.mustache new file mode 100644 index 0000000..bae0bc4 --- /dev/null +++ b/codegen-template/pojo_doc.mustache @@ -0,0 +1,37 @@ +# {{#vendorExtensions.x-is-one-of-interface}}Interface {{/vendorExtensions.x-is-one-of-interface}}{{classname}} + +{{#description}}{{&description}} +{{/description}} +{{^vendorExtensions.x-is-one-of-interface}} + +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +{{#vars}}|**{{name}}** | {{#isEnum}}[**{{datatypeWithEnum}}**](#{{datatypeWithEnum}}){{/isEnum}}{{^isEnum}}{{#isContainer}}{{#isArray}}{{#items}}{{#isModel}}[{{/isModel}}{{/items}}**{{baseType}}{{#items}}<{{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{#isModel}}[{{/isModel}}**Map<String, {{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{#isModel}}[{{/isModel}}**{{dataType}}**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/isContainer}}{{/isEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} | +{{/vars}} +{{#vars}}{{#isEnum}} + + +## Enum: {{datatypeWithEnum}} + +| Name | Value | +|---- | -----|{{#allowableValues}}{{#enumVars}} +| {{name}} | {{value}} |{{/enumVars}}{{/allowableValues}} +{{/isEnum}}{{/vars}} +{{#vendorExtensions.x-implements.0}} + +## Implemented Interfaces + +{{#vendorExtensions.x-implements}} +* {{{.}}} +{{/vendorExtensions.x-implements}} +{{/vendorExtensions.x-implements.0}} +{{/vendorExtensions.x-is-one-of-interface}} +{{#vendorExtensions.x-is-one-of-interface}} +## Implementing Classes + +{{#oneOf}} +* {{{.}}} +{{/oneOf}} +{{/vendorExtensions.x-is-one-of-interface}} diff --git a/codegen-template/pom.mustache b/codegen-template/pom.mustache new file mode 100644 index 0000000..b733ab9 --- /dev/null +++ b/codegen-template/pom.mustache @@ -0,0 +1,382 @@ + + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{artifactUrl}} + {{artifactDescription}} + + {{scmConnection}} + {{scmDeveloperConnection}} + {{scmUrl}} + +{{#parentOverridden}} + + {{{parentGroupId}}} + {{{parentArtifactId}}} + {{{parentVersion}}} + +{{/parentOverridden}} + + + + {{licenseName}} + {{licenseUrl}} + repo + + + + + + {{developerName}} + {{developerEmail}} + {{developerOrganization}} + {{developerOrganizationUrl}} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + true + 128m + 512m + + -Xlint:all + -J-Xss4m + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + + org.junit.jupiter + junit-jupiter-engine + ${junit-version} + + + + + 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 + 1.10 + + + 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-javadoc-plugin + 3.3.2 + + none + 1.8 + + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + + + + + sign-artifacts + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + + + + {{#swagger1AnnotationLibrary}} + + io.swagger + swagger-annotations + ${swagger-annotations-version} + + {{/swagger1AnnotationLibrary}} + {{#swagger2AnnotationLibrary}} + + io.swagger.core.v3 + swagger-annotations + ${swagger-annotations-version} + + {{/swagger2AnnotationLibrary}} + + + + com.google.code.findbugs + jsr305 + 3.0.2 + + + + + + com.sun.jersey + jersey-client + ${jersey-version} + + + com.sun.jersey.contribs + jersey-multipart + ${jersey-version} + + + + + com.fasterxml.jackson.core + jackson-core + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-databind-version} + + {{^useJakartaEe}} + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + ${jackson-version} + + {{/useJakartaEe}} + {{#useJakartaEe}} + + com.fasterxml.jackson.jakarta.rs + jackson-jakarta-rs-json-provider + ${jackson-version} + + {{/useJakartaEe}} + {{#withXml}} + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + ${jackson-version} + + + {{/withXml}} + {{#joda}} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson-version} + + {{/joda}} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-version} + + {{#useBeanValidation}} + + + jakarta.validation + jakarta.validation-api + ${beanvalidation-version} + provided + + {{/useBeanValidation}} + {{#performBeanValidation}} + + + org.hibernate + hibernate-validator + 5.4.1.Final + + {{/performBeanValidation}} + {{#parcelableModel}} + + + com.google.android + android + 4.1.1.4 + provided + + {{/parcelableModel}} + + jakarta.annotation + jakarta.annotation-api + ${jakarta-annotation-version} + provided + + + + org.junit.jupiter + junit-jupiter-engine + ${junit-version} + test + + + org.junit.platform + junit-platform-runner + ${junit-platform-runner.version} + test + + + + UTF-8 + {{#swagger1AnnotationLibrary}} + 1.6.6 + {{/swagger1AnnotationLibrary}} + {{#swagger2AnnotationLibrary}} + 2.2.15 + {{/swagger2AnnotationLibrary}} + 1.19.4 + 2.17.1 + 2.17.1 + {{#useJakartaEe}} + 2.1.1 + {{/useJakartaEe}} + {{^useJakartaEe}} + 1.3.5 + {{/useJakartaEe}} +{{#useBeanValidation}} + 3.0.2 +{{/useBeanValidation}} + 1.0.0 + 5.10.2 + 1.10.0 + + diff --git a/codegen-template/settings.gradle.mustache b/codegen-template/settings.gradle.mustache new file mode 100644 index 0000000..b8fd6c4 --- /dev/null +++ b/codegen-template/settings.gradle.mustache @@ -0,0 +1 @@ +rootProject.name = "{{artifactId}}" \ No newline at end of file diff --git a/codegen-template/travis.mustache b/codegen-template/travis.mustache new file mode 100644 index 0000000..1b6741c --- /dev/null +++ b/codegen-template/travis.mustache @@ -0,0 +1,22 @@ +# +# Generated by OpenAPI Generator: https://openapi-generator.tech +# +# Ref: https://docs.travis-ci.com/user/languages/java/ +# +language: java +jdk: + - openjdk12 + - openjdk11 + - openjdk10 + - openjdk9 + - openjdk8 +before_install: + # ensure gradlew has proper permission + - chmod a+x ./gradlew +script: + # test using maven + #- mvn test + # test using gradle + - gradle test + # test using sbt + # - sbt test diff --git a/codegen-template/typeInfoAnnotation.mustache b/codegen-template/typeInfoAnnotation.mustache new file mode 100644 index 0000000..c833321 --- /dev/null +++ b/codegen-template/typeInfoAnnotation.mustache @@ -0,0 +1,17 @@ +{{#jackson}} + +@JsonIgnoreProperties( + value = "{{{discriminator.propertyBaseName}}}", // ignore manually set {{{discriminator.propertyBaseName}}}, it will be automatically generated by Jackson during serialization + allowSetters = true // allows the {{{discriminator.propertyBaseName}}} to be set during deserialization +) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "{{{discriminator.propertyBaseName}}}", visible = true) +{{#discriminator.mappedModels}} +{{#-first}} +@JsonSubTypes({ +{{/-first}} + @JsonSubTypes.Type(value = {{modelName}}.class, name = "{{^vendorExtensions.x-discriminator-value}}{{mappingName}}{{/vendorExtensions.x-discriminator-value}}{{#vendorExtensions.x-discriminator-value}}{{{vendorExtensions.x-discriminator-value}}}{{/vendorExtensions.x-discriminator-value}}"), +{{#-last}} +}) +{{/-last}} +{{/discriminator.mappedModels}} +{{/jackson}} diff --git a/codegen-template/xmlAnnotation.mustache b/codegen-template/xmlAnnotation.mustache new file mode 100644 index 0000000..4f3b448 --- /dev/null +++ b/codegen-template/xmlAnnotation.mustache @@ -0,0 +1,6 @@ +{{#withXml}} + +@XmlRootElement({{#xmlNamespace}}namespace="{{.}}", {{/xmlNamespace}}name = "{{xmlName}}{{^xmlName}}{{classname}}{{/xmlName}}") +@XmlAccessorType(XmlAccessType.FIELD) +{{#jackson}} +@JacksonXmlRootElement({{#xmlNamespace}}namespace="{{.}}", {{/xmlNamespace}}localName = "{{xmlName}}{{^xmlName}}{{classname}}{{/xmlName}}"){{/jackson}}{{/withXml}} \ No newline at end of file diff --git a/examples/pushData/src/PushData.java b/examples/pushData/src/PushData.java new file mode 100644 index 0000000..0a60268 --- /dev/null +++ b/examples/pushData/src/PushData.java @@ -0,0 +1,32 @@ +import org.databox.ApiClient; +import org.databox.ApiException; +import org.databox.Configuration; +import org.databox.api.DefaultApi; +import org.databox.auth.HttpBasicAuth; +import org.openapitools.client.model.PushData; +import org.openapitools.client.model.PushDataAttribute; + +public class PushData { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://push.databox.com"); + defaultClient.addDefaultHeader("Accept", "application/vnd.databox.v2+json"); + + HttpBasicAuth basicAuth = (HttpBasicAuth) defaultClient.getAuthentication("basicAuth"); + basicAuth.setUsername(""); + + PushData data = new PushData() + .key("") // e.g. sales + .value(123f) + .date("") // e.g. "2024-08-01T00:00:00Z" + .unit("") // optional - e.g. EUR + .attributes(List.of(new PushDataAttribute().key("").value(""))); // optional - e.g. currency & USD + + DefaultApi apiInstance = new DefaultApi(defaultClient); + try { + apiInstance.dataPost(List.of(data)); + } catch (ApiException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file From fec6629ded8a1da7b3212bc127cfb193f3266e82 Mon Sep 17 00:00:00 2001 From: Marcel Koren Date: Fri, 30 Aug 2024 07:43:04 +0200 Subject: [PATCH 2/2] Update unit comments --- codegen-template/README.mustache | 2 +- examples/pushData/src/PushData.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/codegen-template/README.mustache b/codegen-template/README.mustache index 11997e5..6ee70f2 100644 --- a/codegen-template/README.mustache +++ b/codegen-template/README.mustache @@ -140,7 +140,7 @@ public class Example { .key("Metric_name") // e.g. sales .value(123f) .date("Date_in_ISO8601") // e.g. "2024-08-01T00:00:00Z" - .unit("Unit") // optional - e.g. EUR + .unit("Unit") // optional - e.g. USD .attributes(List.of(new PushDataAttribute().key("Dimension_name").value("Dimension_value"))); // optional - e.g. currency & USD DefaultApi apiInstance = new DefaultApi(defaultClient); diff --git a/examples/pushData/src/PushData.java b/examples/pushData/src/PushData.java index 0a60268..ec51bd6 100644 --- a/examples/pushData/src/PushData.java +++ b/examples/pushData/src/PushData.java @@ -19,7 +19,7 @@ public static void main(String[] args) { .key("") // e.g. sales .value(123f) .date("") // e.g. "2024-08-01T00:00:00Z" - .unit("") // optional - e.g. EUR + .unit("") // optional - e.g. USD .attributes(List.of(new PushDataAttribute().key("").value(""))); // optional - e.g. currency & USD DefaultApi apiInstance = new DefaultApi(defaultClient);