diff --git a/bin/configs/jaxrs-spec-required-and-readonly-property.yaml b/bin/configs/jaxrs-spec-required-and-readonly-property.yaml new file mode 100644 index 00000000000..c6a1f257385 --- /dev/null +++ b/bin/configs/jaxrs-spec-required-and-readonly-property.yaml @@ -0,0 +1,10 @@ +generatorName: jaxrs-spec +outputDir: samples/server/petstore/jaxrs-spec-required-and-readonly-property +inputSpec: modules/openapi-generator/src/test/resources/3_0/required-and-readonly-property.yaml +templateDir: modules/openapi-generator/src/main/resources/JavaJaxRS/spec +additionalProperties: + artifactId: jaxrs-spec-petstore-server + serializableModel: "true" + hideGenerationTimestamp: "true" + implicitHeadersRegex: (api_key|enum_header_string) + generateBuilders: "true" diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/beanValidation.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/beanValidation.mustache index c8c6946fef6..6bed33003cb 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/beanValidation.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/beanValidation.mustache @@ -1,4 +1,2 @@ -{{#required}} - @NotNull -{{/required}} -{{>beanValidationCore}} \ No newline at end of file +{{#required}}{{^isReadOnly}} @NotNull +{{/isReadOnly}}{{/required}}{{>beanValidationCore}} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java index c9b9e27a4c7..d196bf25766 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java @@ -6,6 +6,7 @@ import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.parser.core.models.ParseOptions; +import org.assertj.core.condition.AllOf; import org.openapitools.codegen.*; import org.openapitools.codegen.config.CodegenConfigurator; import org.openapitools.codegen.java.assertions.JavaFileAssert; @@ -754,4 +755,44 @@ public class JavaJAXRSSpecServerCodegenTest extends JavaJaxrsBaseTest { "@GZIP\n" ); } + + @Test + public void testHandleRequiredAndReadOnlyPropertiesCorrectly() throws Exception { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + + OpenAPI openAPI = new OpenAPIParser() + .readLocation("src/test/resources/3_0/required-and-readonly-property.yaml", null, new ParseOptions()).getOpenAPI(); + + codegen.setOutputDir(output.getAbsolutePath()); + + ClientOptInput input = new ClientOptInput() + .openAPI(openAPI) + .config(codegen); + + DefaultGenerator generator = new DefaultGenerator(); + Map files = generator.opts(input).generate().stream() + .collect(Collectors.toMap(File::getName, Function.identity())); + + JavaFileAssert.assertThat(files.get("ReadonlyAndRequiredProperties.java")) + .hasProperty("requiredYesReadonlyYes") + .toType() + .assertMethod("getRequiredYesReadonlyYes") + .assertMethodAnnotations() + .hasSize(2) + .containsWithNameAndAttributes("ApiModelProperty", ImmutableMap.of("required", "true")) + // Mysteriously, but we need to surround the value with quotes if the Annotation only contains a single value + .containsWithNameAndAttributes("JsonProperty", ImmutableMap.of("value", "\"requiredYesReadonlyYes\"")) + .toMethod() + .toFileAssert() + .hasProperty("requiredYesReadonlyNo") + .toType() + .assertMethod("getRequiredYesReadonlyNo") + .assertMethodAnnotations() + .hasSize(3) + .containsWithNameAndAttributes("ApiModelProperty", ImmutableMap.of("required", "true")) + // Mysteriously, but we need to surround the value with quotes if the Annotation only contains a single value + .containsWithNameAndAttributes("JsonProperty", ImmutableMap.of("value", "\"requiredYesReadonlyNo\"")) + .containsWithName("NotNull"); + } } diff --git a/modules/openapi-generator/src/test/resources/3_0/required-and-readonly-property.yaml b/modules/openapi-generator/src/test/resources/3_0/required-and-readonly-property.yaml new file mode 100644 index 00000000000..25c43f7d202 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/required-and-readonly-property.yaml @@ -0,0 +1,38 @@ +openapi: 3.0.3 +info: + title: Title + description: Title + version: 1.0.0 +servers: + - url: 'https' +paths: + '/user': + get: + responses: + 200: + description: "success" + content: + application/json: + schema: + $ref: '#/components/schemas/ReadonlyAndRequiredProperties' + +components: + schemas: + ReadonlyAndRequiredProperties: + type: object + required: + - requiredYesReadonlyYes + - requiredYesReadonlyNo + properties: + requiredYesReadonlyYes: + type: string + readOnly: true + requiredYesReadonlyNo: + type: string + requiredNoReadonlyYes: + type: string + readOnly: true + requiredNoReadonlyNo: + type: string + + diff --git a/samples/server/petstore/jaxrs-spec-required-and-readonly-property/.openapi-generator-ignore b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/jaxrs-spec-required-and-readonly-property/.openapi-generator/FILES b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/.openapi-generator/FILES new file mode 100644 index 00000000000..14bd2cb5106 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/.openapi-generator/FILES @@ -0,0 +1,7 @@ +.openapi-generator-ignore +README.md +pom.xml +src/gen/java/org/openapitools/api/RestApplication.java +src/gen/java/org/openapitools/api/UserApi.java +src/gen/java/org/openapitools/model/ReadonlyAndRequiredProperties.java +src/main/openapi/openapi.yaml diff --git a/samples/server/petstore/jaxrs-spec-required-and-readonly-property/.openapi-generator/VERSION b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/.openapi-generator/VERSION new file mode 100644 index 00000000000..89648de3311 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.0.1-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/jaxrs-spec-required-and-readonly-property/README.md b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/README.md new file mode 100644 index 00000000000..0f7a255fe52 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/README.md @@ -0,0 +1,27 @@ +# JAX-RS server with OpenAPI + +## Overview +This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using an +[OpenAPI-Spec](https://openapis.org), you can easily generate a server stub. + +This is an example of building a OpenAPI-enabled JAX-RS server. +This example uses the [JAX-RS](https://jax-rs-spec.java.net/) framework. + + +The JAX-RS implementation needs to be provided by the application server you are deploying on. + +To run the server from the command line, you can use maven to provision an start a TomEE Server. +Please execute the following: + +``` +mvn -Dtomee-embedded-plugin.http=8080 package org.apache.tomee.maven:tomee-embedded-maven-plugin:7.0.5:run +``` + +You can then call your server endpoints under: + +``` +http://localhost:8080/ +``` + +Note that if you have configured the `host` to be something other than localhost, the calls through +swagger-ui will be directed to that host and not localhost! diff --git a/samples/server/petstore/jaxrs-spec-required-and-readonly-property/pom.xml b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/pom.xml new file mode 100644 index 00000000000..6bcd26f49ad --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/pom.xml @@ -0,0 +1,139 @@ + + 4.0.0 + org.openapitools + jaxrs-spec-petstore-server + war + jaxrs-spec-petstore-server + 1.0.0 + + + + src/main/java + + + org.codehaus.mojo + build-helper-maven-plugin + 1.9.1 + + + add-source + generate-sources + + add-source + + + + src/gen/java + + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.1.0 + + false + + + + maven-failsafe-plugin + 2.6 + + + + integration-test + verify + + + + + + + + + jakarta.ws.rs + jakarta.ws.rs-api + ${jakarta.ws.rs-version} + provided + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-version} + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + ${jackson-version} + + + joda-time + joda-time + ${joda-version} + + + javax.annotation + javax.annotation-api + ${javax.annotation-api-version} + + + io.swagger + swagger-annotations + provided + 1.5.3 + + + + com.google.code.findbugs + jsr305 + 3.0.2 + + + junit + junit + ${junit-version} + test + + + org.testng + testng + 6.8.8 + test + + + junit + junit + + + snakeyaml + org.yaml + + + bsh + org.beanshell + + + + + + jakarta.validation + jakarta.validation-api + ${beanvalidation-version} + provided + + + + 1.8 + ${java.version} + ${java.version} + UTF-8 + 2.9.9 + 4.13.2 + 2.10.13 + 1.3.2 + 2.0.2 + 2.1.6 + + diff --git a/samples/server/petstore/jaxrs-spec-required-and-readonly-property/src/gen/java/org/openapitools/api/RestApplication.java b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/src/gen/java/org/openapitools/api/RestApplication.java new file mode 100644 index 00000000000..b85041070e9 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/src/gen/java/org/openapitools/api/RestApplication.java @@ -0,0 +1,9 @@ +package org.openapitools.api; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +@ApplicationPath("") +public class RestApplication extends Application { + +} diff --git a/samples/server/petstore/jaxrs-spec-required-and-readonly-property/src/gen/java/org/openapitools/api/UserApi.java b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/src/gen/java/org/openapitools/api/UserApi.java new file mode 100644 index 00000000000..d7cc59f0ee5 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/src/gen/java/org/openapitools/api/UserApi.java @@ -0,0 +1,30 @@ +package org.openapitools.api; + +import org.openapitools.model.ReadonlyAndRequiredProperties; + +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("/user") +@Api(description = "the user API") +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen") +public class UserApi { + + @GET + @Produces({ "application/json" }) + @ApiOperation(value = "", notes = "", response = ReadonlyAndRequiredProperties.class, tags={ }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "success", response = ReadonlyAndRequiredProperties.class) + }) + public Response userGet() { + return Response.ok().entity("magic!").build(); + } +} diff --git a/samples/server/petstore/jaxrs-spec-required-and-readonly-property/src/gen/java/org/openapitools/model/ReadonlyAndRequiredProperties.java b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/src/gen/java/org/openapitools/model/ReadonlyAndRequiredProperties.java new file mode 100644 index 00000000000..41eb7aa571a --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/src/gen/java/org/openapitools/model/ReadonlyAndRequiredProperties.java @@ -0,0 +1,200 @@ +package org.openapitools.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.Serializable; +import javax.validation.constraints.*; +import javax.validation.Valid; + +import io.swagger.annotations.*; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("ReadonlyAndRequiredProperties") +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen") +public class ReadonlyAndRequiredProperties implements Serializable { + + private @Valid String requiredYesReadonlyYes; + private @Valid String requiredYesReadonlyNo; + private @Valid String requiredNoReadonlyYes; + private @Valid String requiredNoReadonlyNo; + + protected ReadonlyAndRequiredProperties(ReadonlyAndRequiredPropertiesBuilder b) { + this.requiredYesReadonlyYes = b.requiredYesReadonlyYes;this.requiredYesReadonlyNo = b.requiredYesReadonlyNo;this.requiredNoReadonlyYes = b.requiredNoReadonlyYes;this.requiredNoReadonlyNo = b.requiredNoReadonlyNo; + } + + public ReadonlyAndRequiredProperties() { } + + /** + **/ + public ReadonlyAndRequiredProperties requiredYesReadonlyYes(String requiredYesReadonlyYes) { + this.requiredYesReadonlyYes = requiredYesReadonlyYes; + return this; + } + + + @ApiModelProperty(required = true, value = "") + @JsonProperty("requiredYesReadonlyYes") + public String getRequiredYesReadonlyYes() { + return requiredYesReadonlyYes; + } + + @JsonProperty("requiredYesReadonlyYes") + public void setRequiredYesReadonlyYes(String requiredYesReadonlyYes) { + this.requiredYesReadonlyYes = requiredYesReadonlyYes; + } + +/** + **/ + public ReadonlyAndRequiredProperties requiredYesReadonlyNo(String requiredYesReadonlyNo) { + this.requiredYesReadonlyNo = requiredYesReadonlyNo; + return this; + } + + + @ApiModelProperty(required = true, value = "") + @JsonProperty("requiredYesReadonlyNo") + @NotNull + public String getRequiredYesReadonlyNo() { + return requiredYesReadonlyNo; + } + + @JsonProperty("requiredYesReadonlyNo") + public void setRequiredYesReadonlyNo(String requiredYesReadonlyNo) { + this.requiredYesReadonlyNo = requiredYesReadonlyNo; + } + +/** + **/ + public ReadonlyAndRequiredProperties requiredNoReadonlyYes(String requiredNoReadonlyYes) { + this.requiredNoReadonlyYes = requiredNoReadonlyYes; + return this; + } + + + @ApiModelProperty(value = "") + @JsonProperty("requiredNoReadonlyYes") + public String getRequiredNoReadonlyYes() { + return requiredNoReadonlyYes; + } + + @JsonProperty("requiredNoReadonlyYes") + public void setRequiredNoReadonlyYes(String requiredNoReadonlyYes) { + this.requiredNoReadonlyYes = requiredNoReadonlyYes; + } + +/** + **/ + public ReadonlyAndRequiredProperties requiredNoReadonlyNo(String requiredNoReadonlyNo) { + this.requiredNoReadonlyNo = requiredNoReadonlyNo; + return this; + } + + + @ApiModelProperty(value = "") + @JsonProperty("requiredNoReadonlyNo") + public String getRequiredNoReadonlyNo() { + return requiredNoReadonlyNo; + } + + @JsonProperty("requiredNoReadonlyNo") + public void setRequiredNoReadonlyNo(String requiredNoReadonlyNo) { + this.requiredNoReadonlyNo = requiredNoReadonlyNo; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ReadonlyAndRequiredProperties readonlyAndRequiredProperties = (ReadonlyAndRequiredProperties) o; + return Objects.equals(this.requiredYesReadonlyYes, readonlyAndRequiredProperties.requiredYesReadonlyYes) && + Objects.equals(this.requiredYesReadonlyNo, readonlyAndRequiredProperties.requiredYesReadonlyNo) && + Objects.equals(this.requiredNoReadonlyYes, readonlyAndRequiredProperties.requiredNoReadonlyYes) && + Objects.equals(this.requiredNoReadonlyNo, readonlyAndRequiredProperties.requiredNoReadonlyNo); + } + + @Override + public int hashCode() { + return Objects.hash(requiredYesReadonlyYes, requiredYesReadonlyNo, requiredNoReadonlyYes, requiredNoReadonlyNo); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ReadonlyAndRequiredProperties {\n"); + + sb.append(" requiredYesReadonlyYes: ").append(toIndentedString(requiredYesReadonlyYes)).append("\n"); + sb.append(" requiredYesReadonlyNo: ").append(toIndentedString(requiredYesReadonlyNo)).append("\n"); + sb.append(" requiredNoReadonlyYes: ").append(toIndentedString(requiredNoReadonlyYes)).append("\n"); + sb.append(" requiredNoReadonlyNo: ").append(toIndentedString(requiredNoReadonlyNo)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + + public static ReadonlyAndRequiredPropertiesBuilder builder() { + return new ReadonlyAndRequiredPropertiesBuilderImpl(); + } + + private static final class ReadonlyAndRequiredPropertiesBuilderImpl extends ReadonlyAndRequiredPropertiesBuilder { + + @Override + protected ReadonlyAndRequiredPropertiesBuilderImpl self() { + return this; + } + + @Override + public ReadonlyAndRequiredProperties build() { + return new ReadonlyAndRequiredProperties(this); + } + } + + public static abstract class ReadonlyAndRequiredPropertiesBuilder> { + private String requiredYesReadonlyYes; + private String requiredYesReadonlyNo; + private String requiredNoReadonlyYes; + private String requiredNoReadonlyNo; + protected abstract B self(); + + public abstract C build(); + + public B requiredYesReadonlyYes(String requiredYesReadonlyYes) { + this.requiredYesReadonlyYes = requiredYesReadonlyYes; + return self(); + } + public B requiredYesReadonlyNo(String requiredYesReadonlyNo) { + this.requiredYesReadonlyNo = requiredYesReadonlyNo; + return self(); + } + public B requiredNoReadonlyYes(String requiredNoReadonlyYes) { + this.requiredNoReadonlyYes = requiredNoReadonlyYes; + return self(); + } + public B requiredNoReadonlyNo(String requiredNoReadonlyNo) { + this.requiredNoReadonlyNo = requiredNoReadonlyNo; + return self(); + } + } +} + diff --git a/samples/server/petstore/jaxrs-spec-required-and-readonly-property/src/main/openapi/openapi.yaml b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/src/main/openapi/openapi.yaml new file mode 100644 index 00000000000..e3afb081034 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-required-and-readonly-property/src/main/openapi/openapi.yaml @@ -0,0 +1,41 @@ +openapi: 3.0.3 +info: + description: Title + title: Title + version: 1.0.0 +servers: +- url: https +paths: + /user: + get: + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ReadonlyAndRequiredProperties' + description: success + x-accepts: application/json +components: + schemas: + ReadonlyAndRequiredProperties: + example: + requiredYesReadonlyYes: requiredYesReadonlyYes + requiredNoReadonlyYes: requiredNoReadonlyYes + requiredYesReadonlyNo: requiredYesReadonlyNo + requiredNoReadonlyNo: requiredNoReadonlyNo + properties: + requiredYesReadonlyYes: + readOnly: true + type: string + requiredYesReadonlyNo: + type: string + requiredNoReadonlyYes: + readOnly: true + type: string + requiredNoReadonlyNo: + type: string + required: + - requiredYesReadonlyNo + - requiredYesReadonlyYes + type: object