Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[enhancement] [jaxrs-spec] Add builders to models #4930

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/generators/jaxrs-cxf-cdi.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ sidebar_label: jaxrs-cxf-cdi
|disableHtmlEscaping|Disable HTML escaping of JSON strings when using gson (needed to avoid problems with byte[] fields)| |false|
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|fullJavaUtil|whether to use fully qualified name for classes under java.util. This option only works for Java API client| |false|
|generateBuilders|Whether to generate builders for models.| |false|
|generatePom|Whether to generate pom.xml if the file does not already exist.| |true|
|groupId|groupId in generated pom.xml| |org.openapitools|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false|
Expand Down
1 change: 1 addition & 0 deletions docs/generators/jaxrs-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ sidebar_label: jaxrs-spec
|disableHtmlEscaping|Disable HTML escaping of JSON strings when using gson (needed to avoid problems with byte[] fields)| |false|
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|fullJavaUtil|whether to use fully qualified name for classes under java.util. This option only works for Java API client| |false|
|generateBuilders|Whether to generate builders for models.| |false|
|generatePom|Whether to generate pom.xml if the file does not already exist.| |true|
|groupId|groupId in generated pom.xml| |org.openapitools|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class JavaJAXRSSpecServerCodegen extends AbstractJavaJAXRSServerCodegen {
public static final String USE_SWAGGER_ANNOTATIONS = "useSwaggerAnnotations";
public static final String JACKSON = "jackson";
public static final String OPEN_API_SPEC_FILE_LOCATION = "openApiSpecFileLocation";
public static final String GENERATE_BUILDERS = "generateBuilders";

public static final String QUARKUS_LIBRARY = "quarkus";
public static final String THORNTAIL_LIBRARY = "thorntail";
Expand All @@ -47,6 +48,7 @@ public class JavaJAXRSSpecServerCodegen extends AbstractJavaJAXRSServerCodegen {
private boolean interfaceOnly = false;
private boolean returnResponse = false;
private boolean generatePom = true;
private boolean generateBuilders = false;
private boolean useSwaggerAnnotations = true;
private boolean useJackson = false;
private String openApiSpecFileLocation = "src/main/openapi/openapi.yaml";
Expand Down Expand Up @@ -101,6 +103,7 @@ public JavaJAXRSSpecServerCodegen() {

cliOptions.add(library);
cliOptions.add(CliOption.newBoolean(GENERATE_POM, "Whether to generate pom.xml if the file does not already exist.").defaultValue(String.valueOf(generatePom)));
cliOptions.add(CliOption.newBoolean(GENERATE_BUILDERS, "Whether to generate builders for models.").defaultValue(String.valueOf(generateBuilders)));
cliOptions.add(CliOption.newBoolean(INTERFACE_ONLY, "Whether to generate only API interface stubs without the server files.").defaultValue(String.valueOf(interfaceOnly)));
cliOptions.add(CliOption.newBoolean(RETURN_RESPONSE, "Whether generate API interface should return javax.ws.rs.core.Response instead of a deserialized entity. Only useful if interfaceOnly is true.").defaultValue(String.valueOf(returnResponse)));
cliOptions.add(CliOption.newBoolean(USE_SWAGGER_ANNOTATIONS, "Whether to generate Swagger annotations.", useSwaggerAnnotations));
Expand All @@ -124,14 +127,20 @@ public void processOpts() {
additionalProperties.remove(RETURN_RESPONSE);
}
}
if(QUARKUS_LIBRARY.equals(library) || THORNTAIL_LIBRARY.equals(library) || HELIDON_LIBRARY.equals(library) || OPEN_LIBERTY_LIBRARY.equals(library)) {
if (QUARKUS_LIBRARY.equals(library) || THORNTAIL_LIBRARY.equals(library) || HELIDON_LIBRARY.equals(library) || OPEN_LIBERTY_LIBRARY.equals(library)) {
useSwaggerAnnotations = false;
} else {
if (additionalProperties.containsKey(USE_SWAGGER_ANNOTATIONS)) {
useSwaggerAnnotations = Boolean.valueOf(additionalProperties.get(USE_SWAGGER_ANNOTATIONS).toString());
}
}
writePropertyBack(USE_SWAGGER_ANNOTATIONS, useSwaggerAnnotations);

if (additionalProperties.containsKey(GENERATE_BUILDERS)) {
generateBuilders = Boolean.valueOf(additionalProperties.get(GENERATE_BUILDERS).toString());
}
additionalProperties.put(GENERATE_BUILDERS, generateBuilders);

if (additionalProperties.containsKey(OPEN_API_SPEC_FILE_LOCATION)) {
openApiSpecFileLocation = additionalProperties.get(OPEN_API_SPEC_FILE_LOCATION).toString();
} else if(QUARKUS_LIBRARY.equals(library) || THORNTAIL_LIBRARY.equals(library) || HELIDON_LIBRARY.equals(library)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,19 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{#description}}
/**
{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{#description}}/**
* {{description}}
**/{{/description}}{{#useSwaggerAnnotations}}
{{#description}}{{>additionalModelTypeAnnotations}}@ApiModel(description = "{{{description}}}"){{/description}}{{/useSwaggerAnnotations}}
**/{{/description}}
{{#useSwaggerAnnotations}}{{#description}}{{>additionalModelTypeAnnotations}}@ApiModel(description = "{{{description}}}"){{/description}}{{/useSwaggerAnnotations}}
{{>generatedAnnotation}}public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#serializableModel}}implements Serializable{{/serializableModel}} {
{{#vars}}{{#isEnum}}{{^isContainer}}

{{>enumClass}}{{/isContainer}}{{#isContainer}}{{#mostInnerItems}}

{{>enumClass}}{{/mostInnerItems}}{{/isContainer}}{{/isEnum}}
private {{#useBeanValidation}}@Valid{{/useBeanValidation}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/vars}}
private {{#useBeanValidation}}@Valid {{/useBeanValidation}}{{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/vars}}

{{#vars}}
/**
{{#vars}}/**
{{#description}}
* {{description}}
{{/description}}
Expand All @@ -36,17 +34,22 @@ import com.fasterxml.jackson.annotation.JsonValue;
return this;
}

{{#generateBuilders}}public {{classname}}({{#vars}}{{{datatypeWithEnum}}} {{name}}{{#hasMore}}, {{/hasMore}}{{/vars}}) {
{{#vars}}
this.{{name}} = {{name}};
{{/vars}}
}{{/generateBuilders}}

{{#vendorExtensions.x-extra-annotation}}{{{vendorExtensions.x-extra-annotation}}}{{/vendorExtensions.x-extra-annotation}}{{#useSwaggerAnnotations}}
@ApiModelProperty({{#example}}example = "{{{example}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}value = "{{{description}}}"){{/useSwaggerAnnotations}}
@JsonProperty("{{baseName}}")
{{#useBeanValidation}}{{>beanValidation}}{{/useBeanValidation}} public {{{datatypeWithEnum}}} {{getter}}() {
return {{name}};
}

public void {{setter}}({{{datatypeWithEnum}}} {{name}}) {
this.{{name}} = {{name}};
}

{{/vars}}
}{{/vars}}

@Override
public boolean equals(java.lang.Object o) {
Expand Down Expand Up @@ -88,4 +91,37 @@ import com.fasterxml.jackson.annotation.JsonValue;
}
return o.toString().replace("\n", "\n ");
}

{{#generateBuilders}}
public static Builder builder() {
return new Builder();
}

public static class Builder {
{{#vars}}
private {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};
{{/vars}}

{{#vars}}
/**
{{#description}}
* {{description}}
{{/description}}
{{#minimum}}
* minimum: {{minimum}}
{{/minimum}}
{{#maximum}}
* maximum: {{maximum}}
{{/maximum}}
**/
public Builder {{name}}({{{datatypeWithEnum}}} {{name}}) {
this.{{name}} = {{name}};
return this;
}
{{/vars}}

public {{classname}} build() {
return new {{classname}}({{#vars}}{{name}}{{#hasMore}}, {{/hasMore}}{{/vars}});
}
}{{/generateBuilders}}
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.3.1-SNAPSHOT
4.3.1-SNAPSHOT
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.3.0-SNAPSHOT
4.2.3-SNAPSHOT
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,15 @@
import javax.validation.constraints.*;
import javax.validation.Valid;

@Path("/another-fake")
@Api(description = "the another-fake API")
@Path("/AnotherFake")
@Api(description = "the AnotherFake API")
public interface AnotherFakeApi {

@PATCH
@Path("/dummy")
@Consumes({ "application/json" })
@Produces({ "application/json" })
@ApiOperation(value = "To test special tags", notes = "To test special tags and operation ID starting with number", tags={ "$another-fake?" })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "successful operation", response = Client.class) })
Response call123testSpecialTags(@Valid Client client);
Response call123testSpecialTags(@Valid Client body);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import org.openapitools.model.FileSchemaTestClass;
import org.joda.time.LocalDate;
import java.util.Map;
import org.openapitools.model.ModelApiResponse;
import org.openapitools.model.OuterComposite;
import org.openapitools.model.User;
import org.openapitools.model.XmlItem;

import javax.ws.rs.*;
import javax.ws.rs.core.Response;
Expand All @@ -22,65 +22,66 @@
import javax.validation.constraints.*;
import javax.validation.Valid;

@Path("/fake")
@Api(description = "the fake API")
@Path("/Fake")
@Api(description = "the Fake API")
public interface FakeApi {

@POST
@Path("/outer/boolean")
@Consumes({ "application/xml", "application/xml; charset=utf-8", "application/xml; charset=utf-16", "text/xml", "text/xml; charset=utf-8", "text/xml; charset=utf-16" })
@ApiOperation(value = "creates an XmlItem", notes = "this route creates an XmlItem", tags={ "fake", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "successful operation", response = Void.class) })
Response createXmlItem(@Valid XmlItem xmlItem);

@POST
@Produces({ "*/*" })
@ApiOperation(value = "", notes = "Test serialization of outer boolean types", tags={ "fake", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Output boolean", response = Boolean.class) })
Response fakeOuterBooleanSerialize(@Valid Boolean body);

@POST
@Path("/outer/composite")
@Produces({ "*/*" })
@ApiOperation(value = "", notes = "Test serialization of object with outer number type", tags={ "fake", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Output composite", response = OuterComposite.class) })
Response fakeOuterCompositeSerialize(@Valid OuterComposite outerComposite);
Response fakeOuterCompositeSerialize(@Valid OuterComposite body);

@POST
@Path("/outer/number")
@Produces({ "*/*" })
@ApiOperation(value = "", notes = "Test serialization of outer number types", tags={ "fake", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Output number", response = BigDecimal.class) })
Response fakeOuterNumberSerialize(@Valid BigDecimal body);

@POST
@Path("/outer/string")
@Produces({ "*/*" })
@ApiOperation(value = "", notes = "Test serialization of outer string types", tags={ "fake", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Output string", response = String.class) })
Response fakeOuterStringSerialize(@Valid String body);

@PUT
@Path("/body-with-file-schema")
@Consumes({ "application/json" })
@ApiOperation(value = "", notes = "For this test, the body for this request much reference a schema named `File`.", tags={ "fake", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Success", response = Void.class) })
Response testBodyWithFileSchema(@Valid FileSchemaTestClass fileSchemaTestClass);
Response testBodyWithFileSchema(@Valid FileSchemaTestClass body);

@PUT
@Path("/body-with-query-params")
@Consumes({ "application/json" })
@ApiOperation(value = "", notes = "", tags={ "fake", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Success", response = Void.class) })
Response testBodyWithQueryParams(@QueryParam("query") @NotNull String query,@Valid User user);
Response testBodyWithQueryParams(@QueryParam("query") @NotNull String query,@Valid User body);

@PATCH
@Consumes({ "application/json" })
@Produces({ "application/json" })
@ApiOperation(value = "To test \"client\" model", notes = "To test \"client\" model", tags={ "fake", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "successful operation", response = Client.class) })
Response testClientModel(@Valid Client client);
Response testClientModel(@Valid Client body);

@POST
@Consumes({ "application/x-www-form-urlencoded" })
Expand All @@ -98,35 +99,31 @@ public interface FakeApi {
@ApiResponses(value = {
@ApiResponse(code = 400, message = "Invalid request", response = Void.class),
@ApiResponse(code = 404, message = "Not found", response = Void.class) })
Response testEnumParameters(@HeaderParam("enum_header_string_array") @DefaultValue("new ArrayList<String>()") @ApiParam("Header parameter enum test (string array)") List<String> enumHeaderStringArray,@HeaderParam("enum_header_string") @DefaultValue("-efg") @ApiParam("Header parameter enum test (string)") String enumHeaderString,@QueryParam("enum_query_string_array") @DefaultValue("new ArrayList<String>()") @ApiParam("Query parameter enum test (string array)") List<String> enumQueryStringArray,@QueryParam("enum_query_string") @DefaultValue("-efg") @ApiParam("Query parameter enum test (string)") String enumQueryString,@QueryParam("enum_query_integer") @ApiParam("Query parameter enum test (double)") Integer enumQueryInteger,@QueryParam("enum_query_double") @ApiParam("Query parameter enum test (double)") Double enumQueryDouble,@FormParam(value = "enum_form_string_array") List<String> enumFormStringArray,@FormParam(value = "enum_form_string") String enumFormString);
Response testEnumParameters(@HeaderParam("enum_header_string_array") @DefaultValue("new ArrayList<String>()") @ApiParam("Header parameter enum test (string array)") List<String> enumHeaderStringArray,@HeaderParam("enum_header_string") @DefaultValue("-efg") @ApiParam("Header parameter enum test (string)") String enumHeaderString,@QueryParam("enum_query_string_array") @ApiParam("Query parameter enum test (string array)") List<String> enumQueryStringArray,@QueryParam("enum_query_string") @DefaultValue("-efg") @ApiParam("Query parameter enum test (string)") String enumQueryString,@QueryParam("enum_query_integer") @ApiParam("Query parameter enum test (double)") Integer enumQueryInteger,@QueryParam("enum_query_double") @ApiParam("Query parameter enum test (double)") Double enumQueryDouble,@FormParam(value = "enum_form_string_array") List<String> enumFormStringArray,@FormParam(value = "enum_form_string") String enumFormString);

@DELETE
@ApiOperation(value = "Fake endpoint to test group parameters (optional)", notes = "Fake endpoint to test group parameters (optional)", tags={ "fake", })
@ApiResponses(value = {
@ApiResponse(code = 400, message = "Someting wrong", response = Void.class) })
Response testGroupParameters(@QueryParam("required_string_group") @NotNull @ApiParam("Required String in group parameters") Integer requiredStringGroup,@HeaderParam("required_boolean_group") @NotNull @ApiParam("Required Boolean in group parameters") Boolean requiredBooleanGroup,@QueryParam("required_int64_group") @NotNull @ApiParam("Required Integer in group parameters") Long requiredInt64Group,@QueryParam("string_group") @ApiParam("String in group parameters") Integer stringGroup,@HeaderParam("boolean_group") @ApiParam("Boolean in group parameters") Boolean booleanGroup,@QueryParam("int64_group") @ApiParam("Integer in group parameters") Long int64Group);

@POST
@Path("/inline-additionalProperties")
@Consumes({ "application/json" })
@ApiOperation(value = "test inline additionalProperties", notes = "", tags={ "fake", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "successful operation", response = Void.class) })
Response testInlineAdditionalProperties(@Valid Map<String, String> requestBody);
Response testInlineAdditionalProperties(@Valid Map<String, String> param);

@GET
@Path("/jsonFormData")
@Consumes({ "application/x-www-form-urlencoded" })
@ApiOperation(value = "test json serialization of form data", notes = "", tags={ "fake", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "successful operation", response = Void.class) })
Response testJsonFormData(@FormParam(value = "param") String param,@FormParam(value = "param2") String param2);

@POST
@Path("/{petId}/uploadImageWithRequiredFile")
@Consumes({ "multipart/form-data" })
@Produces({ "application/json" })
@ApiOperation(value = "uploads an image (required)", notes = "", authorizations = {
@Authorization(value = "petstore_auth", scopes = {
@AuthorizationScope(scope = "write:pets", description = "modify pets in your account"),
@AuthorizationScope(scope = "read:pets", description = "read your pets")
})
}, tags={ "pet" })
@PUT
@ApiOperation(value = "", notes = "To test the collection format in query parameters", tags={ "fake" })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "successful operation", response = ModelApiResponse.class) })
Response uploadFileWithRequiredFile(@PathParam("petId") @ApiParam("ID of pet to update") Long petId, @FormParam(value = "requiredFile") InputStream requiredFileInputStream,@FormParam(value = "additionalMetadata") String additionalMetadata);
@ApiResponse(code = 200, message = "Success", response = Void.class) })
Response testQueryParameterCollectionFormat(@QueryParam("pipe") @NotNull List<String> pipe,@QueryParam("ioutil") @NotNull List<String> ioutil,@QueryParam("http") @NotNull List<String> http,@QueryParam("url") @NotNull List<String> url,@QueryParam("context") @NotNull List<String> context);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.openapitools.api;

import org.openapitools.model.Client;

import javax.ws.rs.*;
import javax.ws.rs.core.Response;

import io.swagger.annotations.*;

import java.io.InputStream;
import java.util.Map;
import java.util.List;
import javax.validation.constraints.*;
import javax.validation.Valid;

@Path("/FakeClassnameTags123")
@Api(description = "the FakeClassnameTags123 API")
public interface FakeClassnameTags123Api {

@PATCH
@Consumes({ "application/json" })
@Produces({ "application/json" })
@ApiOperation(value = "To test class name in snake case", notes = "To test class name in snake case", authorizations = {
@Authorization(value = "api_key_query")
}, tags={ "fake_classname_tags 123#$%^" })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "successful operation", response = Client.class) })
Response testClassname(@Valid Client body);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ public interface FakeClassnameTestApi {
}, tags={ "fake_classname_tags 123#$%^" })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "successful operation", response = Client.class) })
Response testClassname(@Valid Client client);
Response testClassname(@Valid Client body);
}
Loading