[Java][Play] Remove swagger play dependency, cleanup (#5159)

* First commit of the Java Play Framework server generator. It is highly based on Spring so there might me a couple of things that don't make sense (like options or parameters) for the Play Framework.

* Fix suggestions in the PR discussion + add .bat and .sh file as requested.

* Updated Readme.md file

* Remove unused mustache file + fix baseName vs paramName in all the mustache files.

* Add an CLI option to generate interface. These interfaces are implemented by the controllerImp and help to generate the code with an IDE like IntelliJ because on updates of the code the controllerImp must follow the contract of the interface. If it don't, IDE will provide support to generate missing functions or parameters. I also did some cleanup of options we don't use in Play Framework.

* Fix the compilation error when we have a body which is a list or map. Doesn't fix the problem with the annotation itself.

* Fix the problem with the Http.MultipartFormData.FilePart

* Small fixes

* Remove everything related to swagger-play. No need for annotation anymore because we export the swagger.json directly and show the documentation using swagger-ui with the direct path. Also added the sample.

* Remove/Rename paramsX mustache because there is no more documentation. Remove unused file. updated sample

* Fix the problem with default values that was not set correctly.

* Small fix related to bad merging

* Add handleException CLI options

* Fix default values once and for all!

* Update sample files + fix bug

* Fix bug with body that is required and have a list as a parameter + add bean import for interface to prevent compilation error
This commit is contained in:
wing328
2017-03-22 23:00:30 +08:00
committed by GitHub
parent f064d29e82
commit 2b0efda3bd
44 changed files with 1249 additions and 592 deletions

View File

@@ -0,0 +1,30 @@
package swagger;
import com.google.inject.Inject;
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Result;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
public class ApiCall extends Action<SwaggerUtils.ApiAction> {
@Inject
private ApiCall() {}
public CompletionStage <Result> call(Http.Context ctx) {
try {
//TODO: Do stuff you want to handle with each API call (metrics, logging, etc..)
return delegate.call(ctx);
} catch (Throwable t) {
//TODO: handle error the way you want
return CompletableFuture.completedFuture(handleExceptions(t));
}
}
private Result handleExceptions(Throwable t) {
//TODO: Handle exception that need special response (return a special apimodel, etc..)
return ok();
}
}

View File

@@ -10,6 +10,6 @@ public class ApiDocController extends Controller {
}
public Result api() {
return redirect(String.format("/assets/lib/swagger-ui/index.html?/url=%s/api-docs", ""));
return redirect("/assets/lib/swagger-ui/index.html?/url=/assets/swagger.json");
}
}

View File

@@ -1,9 +1,3 @@
springfox.documentation.swagger.v2.path=/api-docs
server.contextPath={{^useAnnotatedBasePath}}/{{/useAnnotatedBasePath}}{{#useAnnotatedBasePath}}{{contextPath}}{{/useAnnotatedBasePath}}
server.port={{#serverPort}}{{serverPort}}{{/serverPort}}{{^serverPort}}9000{{/serverPort}}
spring.jackson.date-format={{basePackage}}.RFC3339DateFormat
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false
# This is the main configuration file for the application.
# https://www.playframework.com/documentation/latest/ConfigFile
# ~~~~~
@@ -64,9 +58,6 @@ play.modules {
# By default, Play will load any class called Module that is defined
# in the root package (the "app" directory), or you can define them
# explicitly below.
# If there are any built-in modules that you want to disable, you can list them here.
enabled += "play.modules.swagger.SwaggerModule"
# If there are any built-in modules that you want to disable, you can list them here.
#disabled += ""
}

View File

@@ -10,8 +10,7 @@ libraryDependencies ++= Seq(
javaJdbc,
cache,
javaWs,
"io.swagger" %% "swagger-play2" % "1.5.3",
"org.webjars" % "swagger-ui" % "2.2.8"{{#useBeanValidation}},
"org.webjars" % "swagger-ui" % "2.2.10-1"{{#useBeanValidation}},
"javax.validation" % "validation-api" % "1.1.0.Final"
{{/useBeanValidation}}
)

View File

@@ -12,9 +12,9 @@ import javax.validation.constraints.*;
{{/useBeanValidation}}
{{>generatedAnnotation}}
{{#operations}}
public class {{classname}}ControllerImp {
public class {{classname}}ControllerImp {{#useInterfaces}}implements {{classname}}ControllerImpInterface{{/useInterfaces}} {
{{#operation}}
{{>returnTypes}} {{operationId}}({{#allParams}}{{>pathParamsNoDoc}}{{>queryParamsNoDoc}}{{>bodyParamsNoDoc}}{{>formParamsNoDoc}}{{>headerParamsNoDoc}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) {
public {{>returnTypes}} {{operationId}}({{#allParams}}{{>pathParams}}{{>queryParams}}{{>bodyParams}}{{>formParams}}{{>headerParams}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) {
//Do your magic!!!
{{#returnType}}return new {{>returnTypesNoVoidNoAbstract}}();{{/returnType}}
}

View File

@@ -3,7 +3,6 @@ package {{package}};
{{#imports}}import {{import}};
{{/imports}}
import io.swagger.annotations.*;
import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.Http;
@@ -14,15 +13,17 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.google.inject.Inject;
import java.io.IOException;
import swagger.SwaggerUtils;
import javafx.util.Pair;
import com.fasterxml.jackson.core.type.TypeReference;
{{#useBeanValidation}}
import javax.validation.constraints.*;
{{/useBeanValidation}}
{{#handleExceptions}}
import swagger.SwaggerUtils.ApiAction;
{{/handleExceptions}}
{{>generatedAnnotation}}
@Api(value = "{{{baseName}}}", description = "the {{{baseName}}} API")
{{#operations}}
public class {{classname}}Controller extends Controller {
@@ -37,21 +38,7 @@ public class {{classname}}Controller extends Controller {
{{#operation}}
@ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}"{{#returnType}}, response = {{{returnType}}}.class{{/returnType}}{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = {
{{#authMethods}}@Authorization(value = "{{name}}"{{#isOAuth}}, scopes = {
{{#scopes}}@AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}},
{{/hasMore}}{{/scopes}}
}{{/isOAuth}}){{#hasMore}},
{{/hasMore}}{{/authMethods}}
}{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}",{{/vendorExtensions.x-tags}} })
@ApiResponses(value = { {{#responses}}
@ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#returnType}}, response = {{{returnType}}}.class{{/returnType}}){{#hasMore}}, {{/hasMore}}{{/responses}} })
{{#hasParams}}
@ApiImplicitParams({
{{#allParams}}{{^isPathParam}}@ApiImplicitParam(name = "{{baseName}}", value = "{{{description}}}"{{#required}}, required = true{{/required}}{{#defaultValue}}, defaultValue = "{{{defaultValue}}}"{{/defaultValue}}, dataType = "{{{dataTypeForImplicitParam}}}", paramType = "{{>paramType}}"){{#hasMore}},
{{/hasMore}}{{/isPathParam}}{{/allParams}}
})
{{/hasParams}}
{{#handleExceptions}}@ApiAction{{/handleExceptions}}
public Result {{operationId}}({{#pathParams}}{{>pathParams}}{{#hasMore}},{{/hasMore}}{{/pathParams}}) {{#bodyParams}}throws IOException{{/bodyParams}} {
{{#bodyParams}}
{{#collectionFormat}}
@@ -64,7 +51,7 @@ public class {{classname}}Controller extends Controller {
{{^required}}
if (node{{paramName}} != null) {
{{paramName}} = mapper.readValue(node{{paramName}}.toString(), {{#isMapContainer}}new TypeReference<Map<{{{dataType}}}>>(){}{{/isMapContainer}}{{#isListContainer}}new TypeReference<List<{{{dataType}}}>>(){}{{/isListContainer}}{{^isListContainer}}{{^isMapContainer}}{{{dataType}}}.class{{/isMapContainer}}{{/isListContainer}});{{/required}}
{{#required}}{{paramName}} = mapper.readValue(node{{paramName}}.toString(), {{{dataType}}}.class);{{/required}}
{{#required}}{{paramName}} = mapper.readValue(node{{paramName}}.toString(), {{#isMapContainer}}new TypeReference<Map<{{{dataType}}}>>(){}{{/isMapContainer}}{{#isListContainer}}new TypeReference<List<{{{dataType}}}>>(){}{{/isListContainer}}{{^isListContainer}}{{^isMapContainer}}{{{dataType}}}.class{{/isMapContainer}}{{/isListContainer}});{{/required}}
{{^required}}
} else {
{{paramName}} = null;

View File

@@ -0,0 +1,22 @@
package {{package}};
{{#imports}}import {{import}};
{{/imports}}
import play.mvc.Http;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
{{#useBeanValidation}}
import javax.validation.constraints.*;
{{/useBeanValidation}}
{{#operations}}
public interface {{classname}}ControllerImpInterface {
{{#operation}}
{{>returnTypes}} {{operationId}}({{#allParams}}{{>pathParams}}{{>queryParams}}{{>bodyParams}}{{>formParams}}{{>headerParams}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
{{/operation}}
}
{{/operations}}

View File

@@ -1,10 +0,0 @@
package {{apiPackage}};
{{>generatedAnnotation}}
public class NotFoundException extends ApiException {
private int code;
public NotFoundException (int code, String msg) {
super(code, msg);
this.code = code;
}
}

View File

@@ -1 +1 @@
{{#isPathParam}}{{#useBeanValidation}}{{>beanValidationPathParams}}{{/useBeanValidation}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}, allowableValues="{{{allowableValues}}}"{{/allowableValues}} {{#defaultValue}}, defaultValue="{{{defaultValue}}}"{{/defaultValue}}) {{{dataType}}} {{paramName}}{{/isPathParam}}
{{#isPathParam}}{{#useBeanValidation}}{{>beanValidationPathParams}}{{/useBeanValidation}}{{{dataType}}} {{paramName}}{{/isPathParam}}

View File

@@ -1 +0,0 @@
{{#isPathParam}}{{#useBeanValidation}}{{>beanValidationPathParams}}{{/useBeanValidation}} {{{dataType}}} {{paramName}}{{/isPathParam}}

View File

@@ -1,7 +1,6 @@
/**
* {{#description}}{{.}}{{/description}}{{^description}}{{classname}}{{/description}}
*/{{#description}}
@ApiModel(description = "{{{description}}}"){{/description}}
*/
{{>generatedAnnotation}}
public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#serializableModel}}implements Serializable{{/serializableModel}} {
{{#vars}}
@@ -64,7 +63,6 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#seriali
{{#vendorExtensions.extraAnnotation}}
{{{vendorExtensions.extraAnnotation}}}
{{/vendorExtensions.extraAnnotation}}
@ApiModelProperty({{#example}}example = "{{example}}", {{/example}}{{#required}}required = {{required}}, {{/required}}value = "{{{description}}}")
{{#useBeanValidation}}{{>beanValidation}}{{/useBeanValidation}} public {{{datatypeWithEnum}}} {{getter}}() {
return {{name}};
}

View File

@@ -1,9 +0,0 @@
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.8.4")
libraryDependencies <+= sbtVersion(v => v match {
case "0.11.0" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.0-0.2.8"
case "0.11.1" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.1-0.2.10"
case "0.11.2" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.2-0.2.11"
case "0.11.3" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.3-0.2.11.1"
case x if (x.startsWith("0.12")) => "com.github.siasia" %% "xsbt-web-plugin" % "0.12.0-0.2.11.1"
})

View File

@@ -10,18 +10,11 @@ GET /api controllers.ApiDocController.api
#Functions for {{{baseName}}} API
{{#operations}}
{{#operation}}
{{httpMethod}} {{path}} controllers.{{classname}}Controller.{{operationId}}({{#pathParams}}{{paramName}}: {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/pathParams}})
{{httpMethod}} {{contextPath}}{{path}} controllers.{{classname}}Controller.{{operationId}}({{#pathParams}}{{paramName}}: {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/pathParams}})
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
GET /api-docs controllers.ApiHelpController.getResources
{{#apiInfo}}
{{#apis}}
GET /api-docs.json/{{{baseName}}} controllers.ApiHelpController.getResource(path = "/{{{baseName}}}")
{{/apis}}
{{/apiInfo}}
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)

View File

@@ -0,0 +1 @@
{{{swagger-json}}}

View File

@@ -1,108 +1,101 @@
package swagger;
import javafx.util.Pair;
import play.mvc.With;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
public class SwaggerUtils {
/**
* Format to {@code Pair} objects.
*
* @param collectionFormat collection format (e.g. csv, tsv)
* @param name Name
* @param value Value
* @return A list of Pair objects
*/
public static List<Pair> parameterToPairs(String collectionFormat, String name, Object value){
List<Pair> params = new ArrayList<Pair>();
{{#handleExceptions}}
@With(ApiCall.class)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiAction {
}
{{/handleExceptions}}
// preconditions
if (name == null || name.isEmpty() || value == null) return params;
public static Map<String, String> parameterToPairs(String collectionFormat, String name, Object value){
Map<String, String> params = new HashMap<>();
Collection valueCollection = null;
if (value instanceof Collection) {
valueCollection = (Collection) value;
} else {
params.add(new Pair(name, parameterToString(value)));
return params;
}
// preconditions
if (name == null || name.isEmpty() || value == null) return params;
if (valueCollection.isEmpty()){
return params;
}
Collection valueCollection = null;
if (value instanceof Collection) {
valueCollection = (Collection) value;
} else {
params.put(name, parameterToString(value));
return params;
}
// get the collection format
collectionFormat = (collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat); // default: csv
if (valueCollection.isEmpty()){
return params;
}
// create the params based on the collection format
if (collectionFormat.equals("multi")) {
for (Object item : valueCollection) {
params.add(new Pair(name, parameterToString(item)));
}
// get the collection format
collectionFormat = (collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat); // default: csv
return params;
}
// create the params based on the collection format
if (collectionFormat.equals("multi")) {
for (Object item : valueCollection) {
params.put(name, parameterToString(item));
}
String delimiter = ",";
return params;
}
if (collectionFormat.equals("csv")) {
delimiter = ",";
} else if (collectionFormat.equals("ssv")) {
delimiter = " ";
} else if (collectionFormat.equals("tsv")) {
delimiter = "\t";
} else if (collectionFormat.equals("pipes")) {
delimiter = "|";
}
String delimiter = ",";
StringBuilder sb = new StringBuilder() ;
for (Object item : valueCollection) {
sb.append(delimiter);
sb.append(parameterToString(item));
}
if (collectionFormat.equals("csv")) {
delimiter = ",";
} else if (collectionFormat.equals("ssv")) {
delimiter = " ";
} else if (collectionFormat.equals("tsv")) {
delimiter = "\t";
} else if (collectionFormat.equals("pipes")) {
delimiter = "|";
}
params.add(new Pair(name, sb.substring(1)));
StringBuilder sb = new StringBuilder() ;
for (Object item : valueCollection) {
sb.append(delimiter);
sb.append(parameterToString(item));
}
return params;
}
params.put(name, sb.substring(1));
/**
* Format the given parameter object into string.
*
* @param param Parameter
* @return String representation of the parameter
*/
public static String parameterToString(Object param) {
if (param == null) {
return "";
} else if (param instanceof Date) {
return formatDatetime((Date) param);
} else if (param instanceof Collection) {
StringBuilder b = new StringBuilder();
for (Object o : (Collection)param) {
if (b.length() > 0) {
b.append(",");
}
b.append(String.valueOf(o));
}
return b.toString();
} else {
return String.valueOf(param);
}
}
return params;
}
/**
* Format the given Date object into string (Datetime format).
*
* @param date Date object
* @return Formatted datetime in string representation
*/
public static String formatDatetime(Date date) {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").format(date);
}
public static String parameterToString(Object param) {
if (param == null) {
return "";
} else if (param instanceof Date) {
return formatDatetime((Date) param);
} else if (param instanceof Collection) {
StringBuilder b = new StringBuilder();
for (Object o : (Collection)param) {
if (b.length() > 0) {
b.append(",");
}
b.append(String.valueOf(o));
}
return b.toString();
} else {
return String.valueOf(param);
}
}
public static String formatDatetime(Date date) {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").format(date);
}
}