Skip to content

Commit

Permalink
Microprofile REST Client-compliant Java code gernerator (OpenAPITools#1)
Browse files Browse the repository at this point in the history
* Create JavaMicroprofile generator

* Add ApiException and ApiExeptionMapper

* Add documentation for running the generator

* Update README

* Capitalize P in MicroProfile

* Clean up generated model for microprofile rest client

* Revert change in AbstractJavaCodegen and move the action into JavaMicroprofile

* Remove non-cxf templates in JavaMicroprofile and rename cxf to mp"

* Remove mp/server files

* Update pom.xml to have MpRestClient dependencies

* Generate tests using smallrye implementation

* Change JavaMicroprofile.java to JavaMicroprofileCodegen.java

* Add test for JavaMicroprofileCodegen

* Add scripts to generate petstore sample

* Add README

* Revert "Add scripts to generate petstore sample"

This reverts commit 25ee496.

* Revert "Add README"

This reverts commit 067b2a8.

* Revert "Revert "Add scripts to generate petstore sample""

This reverts commit 5a36390.

* Add README

* Move templates out of mp/ directory

* Remove CXF references

* Remove unnecessary templates

* Fix compilation errors

* Add missing jsonb dependency

* Add license header to code templates

* Increase copyright year to 2019

* Remove TODO comments and commented out code

* Add more javadocs to the model templates

* Fix issues with generated code

* Revert README back to master's README

* Add documentation for java microprofile client generator

* Add MicroProfile Rest Client to list of clients in README

* Remove smartbear copyright from java files

* Add disableMultipart option

* Fix licenseInfo

* Rename JavaMicroprofileCodegen to JavaMicroprofileRestClientCodegen

* Rename sample scripts to java-microprofile-rest-client

* Adjust test comments

* Better spacing between members for generated models

* Update getHelp() method

* Update javadoc for tests
  • Loading branch information
patricktiu authored Aug 28, 2019
1 parent 44d8b49 commit 0923fef
Show file tree
Hide file tree
Showing 35 changed files with 1,069 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ OpenAPI Generator allows generation of API client libraries (SDK generation), se

| | Languages/Frameworks |
|-|-|
**API clients** | **ActionScript**, **Ada**, **Apex**, **Bash**, **C**, **C#** (.net 2.0, 3.5 or later, .NET Standard 1.3 - 2.0, .NET Core 2.0), **C++** (cpp-restsdk, Qt5, Tizen), **Clojure**, **Dart (1.x, 2.x)**, **Elixir**, **Elm**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java, Rest-assured, Spring 5 Web Client), **Kotlin**, **Lua**, **Node.js/JavaScript** (ES5, ES6, AngularJS with Google Closure Compiler annotations, Flow types), **Objective-C**, **OCaml**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust** (rust, rust-server), **Scala** (akka, http4s, scalaz, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x), **Typescript** (AngularJS, Angular (2.x - 8.x), Aurelia, Axios, Fetch, Inversify, jQuery, Node, Rxjs)
**API clients** | **ActionScript**, **Ada**, **Apex**, **Bash**, **C**, **C#** (.net 2.0, 3.5 or later, .NET Standard 1.3 - 2.0, .NET Core 2.0), **C++** (cpp-restsdk, Qt5, Tizen), **Clojure**, **Dart (1.x, 2.x)**, **Elixir**, **Elm**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java, Rest-assured, Spring 5 Web Client), MicroProfile Rest Client), **Kotlin**, **Lua**, **Node.js/JavaScript** (ES5, ES6, AngularJS with Google Closure Compiler annotations, Flow types), **Objective-C**, **OCaml**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust** (rust, rust-server), **Scala** (akka, http4s, scalaz, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x), **Typescript** (AngularJS, Angular (2.x - 8.x), Aurelia, Axios, Fetch, Inversify, jQuery, Node, Rxjs)
**Server stubs** | **Ada**, **C#** (ASP.NET Core, NancyFx), **C++** (Pistache, Restbed, Qt5 QHTTPEngine), **Erlang**, **F#** (Giraffe), **Go** (net/http, Gin), **Haskell** (Servant), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, Jersey, RestEasy, Play Framework, [PKMST](https://github.com/ProKarma-Inc/pkmst-getting-started-examples)), **Kotlin** (Spring Boot, Ktor), **PHP** (Laravel, Lumen, Slim, Silex, [Symfony](https://symfony.com/), [Zend Expressive](https://github.com/zendframework/zend-expressive)), **Python** (Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust** (rust-server), **Scala** ([Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), [Play](https://www.playframework.com/), Scalatra)
**API documentation generators** | **HTML**, **Confluence Wiki**
**Configuration files** | [**Apache2**](https://httpd.apache.org/)
Expand Down
32 changes: 32 additions & 0 deletions bin/java-microprofile-rest-client.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/sh

SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"

while [ -h "$SCRIPT" ] ; do
ls=`ls -ld "$SCRIPT"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
SCRIPT="$link"
else
SCRIPT=`dirname "$SCRIPT"`/"$link"
fi
done

if [ ! -d "${APP_DIR}" ]; then
APP_DIR=`dirname "$SCRIPT"`/..
APP_DIR=`cd "${APP_DIR}"; pwd`
fi

executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"

if [ ! -f "$executable" ]
then
mvn -B clean package
fi

# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="generate --artifact-id "microprofile-rest-client" -t modules/openapi-generator/src/main/resources/JavaMicroprofile -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g microprofile-rest-client -o samples/client/petstore/microprofile-rest-client $@"

java $JAVA_OPTS -jar $executable $ags
10 changes: 10 additions & 0 deletions bin/windows/java-microprofile-rest-client.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
set executable=.\modules\openapi-generator-cli\target\openapi-generator-cli.jar

If Not Exist %executable% (
mvn clean package
)

REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -DloggerPath=conf/log4j.properties
set ags=generate --artifact-id "microprofile-rest-client" -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g microprofile-rest-client -o samples\client\petstore\microprofile-rest-client

java %JAVA_OPTS% -jar %executable% %ags%
46 changes: 46 additions & 0 deletions docs/generators/java-microprofile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

---
id: generator-opts-client-java-microprofile
title: Config Options for java-microprofile
sidebar_label: java-microprofile
---

| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
|modelPackage|package for generated models| |org.openapitools.model|
|apiPackage|package for generated api classes| |org.openapitools.api|
|invokerPackage|root package for generated code| |org.openapitools.api|
|groupId|groupId in generated pom.xml| |org.openapitools|
|artifactId|artifactId in generated pom.xml. This also becomes part of the generated library's filename| |openapi-jaxrs-client|
|artifactVersion|artifact version in generated pom.xml. This also becomes part of the generated library's filename| |1.0.0|
|artifactUrl|artifact URL in generated pom.xml| |https://github.com/openapitools/openapi-generator|
|artifactDescription|artifact description in generated pom.xml| |OpenAPI Java|
|scmConnection|SCM connection in generated pom.xml| |scm:git:git@github.com:openapitools/openapi-generator.git|
|scmDeveloperConnection|SCM developer connection in generated pom.xml| |scm:git:git@github.com:openapitools/openapi-generator.git|
|scmUrl|SCM URL in generated pom.xml| |https://github.com/openapitools/openapi-generator|
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|developerOrganizationUrl|developer organization URL in generated pom.xml| |http://openapitools.org|
|licenseName|The name of the license| |Unlicense|
|licenseUrl|The URL of the license| |http://unlicense.org|
|sourceFolder|source folder for generated code| |src/gen/java|
|serializableModel|boolean - toggle "implements Serializable" for generated models| |false|
|bigDecimalAsString|Treat BigDecimal values as Strings to avoid precision loss.| |false|
|fullJavaUtil|whether to use fully qualified name for classes under java.util. This option only works for Java API client| |false|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false|
|withXml|whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)| |false|
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date (if you really have a good reason not to use threetenbp</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+) - note: this also sets &quot;java8&quot; to true</dd><dt>**threetenbp**</dt><dd>Backport of JSR310 (preferred for jdk &lt; 1.8)</dd><dl>|legacy|
|java8|Option. Use Java8 classes instead of third party equivalents|<dl><dt>**true**</dt><dd>Use Java 8 classes such as Base64</dd><dt>**false**</dt><dd>Various third party libraries as needed</dd><dl>|false|
|disableHtmlEscaping|Disable HTML escaping of JSON strings when using gson (needed to avoid problems with byte[] fields)| |false|
|booleanGetterPrefix|Set booleanGetterPrefix| |get|
|parentGroupId|parent groupId in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null|
|parentArtifactId|parent artifactId in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null|
|parentVersion|parent version in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null|
|snapshotVersion|Uses a SNAPSHOT version.|<dl><dt>**true**</dt><dd>Use a SnapShot Version</dd><dt>**false**</dt><dd>Use a Release Version</dd><dl>|null|
|useGenericResponse|Use generic response| |false|
|disableMultipart|Disable multipart annotations| |false|
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright 2019 OpenAPI-Generator Contributors (https://openapi-generator.tech)
*
* 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
*
* http://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.
*/

package org.openapitools.codegen.languages;

import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.Schema;

import org.openapitools.codegen.CliOption;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.CodegenType;
import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.languages.features.BeanValidationFeatures;
import org.openapitools.codegen.languages.features.GzipTestFeatures;
import org.openapitools.codegen.languages.features.LoggingTestFeatures;
import org.openapitools.codegen.languages.features.UseGenericResponseFeatures;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.List;
import java.util.Map;

public class JavaMicroprofileRestClientCodegen extends AbstractJavaCodegen
implements BeanValidationFeatures, UseGenericResponseFeatures, GzipTestFeatures, LoggingTestFeatures {

private static final Logger LOGGER = LoggerFactory.getLogger(JavaMicroprofileRestClientCodegen.class);

/**
* Name of the sub-directory in "src/main/resource" where to find the
* Mustache template for the JAX-RS Codegen.
*/
protected static final String JAXRS_TEMPLATE_DIRECTORY_NAME = "JavaMicroprofile";

protected static final String DISABLE_MULTIPART = "disableMultipart";

protected boolean useBeanValidation = false;

protected boolean useGenericResponse = false;

protected boolean useGzipFeatureForTests = false;

protected boolean useLoggingFeatureForTests = false;

public JavaMicroprofileRestClientCodegen() {
super();

supportsInheritance = true;

sourceFolder = "src/gen/java";
invokerPackage = "org.openapitools.api";
artifactId = "gen-microprofile-rest-client";
dateLibrary = "legacy";

apiPackage = "org.openapitools.api";
modelPackage = "org.openapitools.model";

outputFolder = "generated-code/JavaJaxRS-CXF";

// clear model and api doc template as this codegen
// does not support auto-generated markdown doc at the moment
modelDocTemplateFiles.remove("model_doc.mustache");
apiDocTemplateFiles.remove("api_doc.mustache");


typeMapping.put("date", "LocalDate");

importMapping.put("LocalDate", "org.joda.time.LocalDate");

embeddedTemplateDir = templateDir = JAXRS_TEMPLATE_DIRECTORY_NAME;

cliOptions.add(CliOption.newBoolean(USE_BEANVALIDATION, "Use BeanValidation API annotations"));

cliOptions.add(CliOption.newBoolean(USE_GZIP_FEATURE_FOR_TESTS, "Use Gzip Feature for tests"));
cliOptions.add(CliOption.newBoolean(USE_LOGGING_FEATURE_FOR_TESTS, "Use Logging Feature for tests"));

cliOptions.add(CliOption.newBoolean(USE_GENERIC_RESPONSE, "Use generic response"));
cliOptions.add(CliOption.newBoolean(DISABLE_MULTIPART, "Disable multipart import"));
}


@Override
public void processOpts() {
super.processOpts();

if (additionalProperties.containsKey(USE_BEANVALIDATION)) {
boolean useBeanValidationProp = convertPropertyToBooleanAndWriteBack(USE_BEANVALIDATION);
this.setUseBeanValidation(useBeanValidationProp);
}

if (additionalProperties.containsKey(USE_GENERIC_RESPONSE)) {
this.setUseGenericResponse(convertPropertyToBoolean(USE_GENERIC_RESPONSE));
}

if (useGenericResponse) {
writePropertyBack(USE_GENERIC_RESPONSE, useGenericResponse);
}

this.setUseGzipFeatureForTests(convertPropertyToBooleanAndWriteBack(USE_GZIP_FEATURE_FOR_TESTS));
this.setUseLoggingFeatureForTests(convertPropertyToBooleanAndWriteBack(USE_LOGGING_FEATURE_FOR_TESTS));


supportingFiles.clear(); // Don't need extra files provided by AbstractJAX-RS & Java Codegen
String apiFolder = (sourceFolder + File.separator + apiPackage().replace('.', File.separatorChar)).replace('/', File.separatorChar);
supportingFiles.add(new SupportingFile("api_exception.mustache", apiFolder, "ApiException.java"));
supportingFiles.add(new SupportingFile("api_exception_mapper.mustache", apiFolder, "ApiExceptionMapper.java"));
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));

writeOptional(outputFolder, new SupportingFile("pom.mustache", "", "pom.xml"));

}

@Override
public String getName() {
return "microprofile-rest-client";
}


@Override
public CodegenType getTag() {
return CodegenType.CLIENT;
}

@Override
public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation co, Map<String, List<CodegenOperation>> operations) {
super.addOperationToGroup(tag, resourcePath, operation, co, operations);
co.subresourceOperation = !co.path.isEmpty();
}

@Override
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
super.postProcessModelProperty(model, property);
model.imports.remove("ApiModelProperty");
model.imports.remove("ApiModel");
model.imports.remove("JsonSerialize");
model.imports.remove("ToStringSerializer");
}

@Override
public CodegenModel fromModel(String name, Schema model) {
CodegenModel codegenModel = super.fromModel(name, model);
if (codegenModel.imports.contains("ApiModel")) {
// Remove io.swagger.annotations.ApiModel import
codegenModel.imports.remove("ApiModel");
}

return codegenModel;
}

@Override
public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> objs, List<Object> allModels) {
objs = super.postProcessOperationsWithModels(objs, allModels);
return AbstractJavaJAXRSServerCodegen.jaxrsPostProcessOperations(objs);
}

@Override
public String getHelp() {
return "Generates a MicroProfile Rest Client";
}

public void setUseBeanValidation(boolean useBeanValidation) {
this.useBeanValidation = useBeanValidation;
}

public void setUseGzipFeatureForTests(boolean useGzipFeatureForTests) {
this.useGzipFeatureForTests = useGzipFeatureForTests;
}

public void setUseLoggingFeatureForTests(boolean useLoggingFeatureForTests) {
this.useLoggingFeatureForTests = useLoggingFeatureForTests;
}

public void setUseGenericResponse(boolean useGenericResponse) {
this.useGenericResponse = useGenericResponse;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# {{appName}} - MicroProfile Rest Client

{{#appDescription}}
{{{appDescription}}}

{{/appDescription}}
## Overview
This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project.
[MicroProfile Rest Client](https://github.com/eclipse/microprofile-rest-client) is a type-safe way of calling
REST services. The generated client contains an interface which acts as the client, you can inject it into dependent classes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{{>licenseInfo}}
package {{package}};

{{#imports}}import {{import}};
{{/imports}}

import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;
{{^disableMultipart}}
import org.apache.cxf.jaxrs.ext.multipart.*;
{{/disableMultipart}}

import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

{{#appName}}
/**
* {{{appName}}}
*
{{#appDescription}}
* <p>{{{appDescription}}}
*
{{/appDescription}}
*/
{{/appName}}

@RegisterRestClient
@RegisterProvider(ApiExceptionMapper.class)
@Path("{{^useAnnotatedBasePath}}/{{/useAnnotatedBasePath}}{{#useAnnotatedBasePath}}{{contextPath}}{{/useAnnotatedBasePath}}")
public interface {{classname}} {
{{#operations}}
{{#operation}}

{{#summary}}
/**
* {{summary}}
*
{{#notes}}
* {{notes}}
*
{{/notes}}
*/
{{/summary}}
@{{httpMethod}}
{{#subresourceOperation}}@Path("{{{path}}}"){{/subresourceOperation}}
{{#hasConsumes}}
@Consumes({ {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} })
{{/hasConsumes}}
{{#hasProduces}}
@Produces({ {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} })
{{/hasProduces}}
public {{>returnTypes}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException, ProcessingException;
{{/operation}}
}
{{/operations}}

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{{>licenseInfo}}
package {{apiPackage}};

import javax.ws.rs.core.Response;

public class ApiException extends Exception {
private static final long serialVersionUID = 1L;
private Response response;
public ApiException() {
super();
}

public ApiException(Response response) {
super("Api response has status code " + response.getStatus());
this.response = response;
}

public Response getResponse() {
return this.response;
}
}
Loading

0 comments on commit 0923fef

Please sign in to comment.