diff --git a/docs/generators/jaxrs-cxf-client.md b/docs/generators/jaxrs-cxf-client.md index 29a7923078b0..53c3ccbef0c6 100644 --- a/docs/generators/jaxrs-cxf-client.md +++ b/docs/generators/jaxrs-cxf-client.md @@ -54,6 +54,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |licenseName|The name of the license| |Unlicense| |licenseUrl|The URL of the license| |http://unlicense.org| |modelPackage|package for generated models| |org.openapitools.model| +|oas3|Generate Swagger annotations in version 3 instead of version 2.| |false| |openApiNullable|Enable OpenAPI Jackson Nullable library. Not supported by `microprofile` library.| |true| |parentArtifactId|parent artifactId in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null| |parentGroupId|parent groupId in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaCXFClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaCXFClientCodegen.java index c96233046c55..51733fd423f8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaCXFClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaCXFClientCodegen.java @@ -47,6 +47,8 @@ public class JavaCXFClientCodegen extends AbstractJavaCodegen public static final String USE_ABSTRACTION_FOR_FILES = "useAbstractionForFiles"; + public static final String OAS3 = "oas3"; + @Getter protected boolean useGenericResponse = false; @Getter protected boolean useGzipFeatureForTests = false; @@ -54,6 +56,8 @@ public class JavaCXFClientCodegen extends AbstractJavaCodegen @Getter protected boolean useLoggingFeatureForTests = false; @Setter protected boolean useAbstractionForFiles = false; + + @Setter protected boolean oas3 = false; public JavaCXFClientCodegen() { super(); @@ -93,6 +97,7 @@ public JavaCXFClientCodegen() { 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(USE_ABSTRACTION_FOR_FILES, "Use alternative types instead of java.io.File to allow passing bytes without a file on disk.")); + cliOptions.add(CliOption.newBoolean(OAS3, "Generate Swagger annotations in version 3 instead of version 2.")); } @Override @@ -103,6 +108,7 @@ public void processOpts() { convertPropertyToBooleanAndWriteBack(USE_LOGGING_FEATURE_FOR_TESTS, this::setUseLoggingFeatureForTests); convertPropertyToBooleanAndWriteBack(JACKSON, this::setJackson); convertPropertyToBooleanAndWriteBack(USE_ABSTRACTION_FOR_FILES, this::setUseAbstractionForFiles); + convertPropertyToBooleanAndWriteBack(OAS3, this::setOas3); supportingFiles.clear(); // Don't need extra files provided by AbstractJAX-RS & Java Codegen @@ -132,6 +138,13 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert super.postProcessModelProperty(model, property); model.imports.remove("ApiModelProperty"); model.imports.remove("ApiModel"); + + if (oas3) { + apiTemplateFiles.remove("api.mustache"); + importMapping.remove("ApiModelProperty"); + importMapping.remove("ApiModel"); + apiTemplateFiles.put("api_oas3.mustache", ".java"); + } if (jackson) { //Add jackson imports when model has inner enum diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/cxf/api_oas3.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/cxf/api_oas3.mustache new file mode 100644 index 000000000000..14a820f569c6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/cxf/api_oas3.mustache @@ -0,0 +1,79 @@ +package {{package}}; + +{{#imports}}import {{import}}; +{{/imports}} + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; +import {{javaxPackage}}.ws.rs.*; +import {{javaxPackage}}.ws.rs.core.Response; +import {{javaxPackage}}.ws.rs.core.MediaType; +import org.apache.cxf.jaxrs.ext.multipart.*; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +{{#useBeanValidation}} +import {{javaxPackage}}.validation.constraints.*; +import {{javaxPackage}}.validation.Valid; +{{/useBeanValidation}} + +{{#appName}} +/** + * {{{appName}}} + * + {{#appDescription}} + *

{{{.}}} + * + {{/appDescription}} + */ +{{/appName}} +@Path("{{#useAnnotatedBasePath}}{{contextPath}}{{/useAnnotatedBasePath}}{{commonPath}}") +{{#addConsumesProducesJson}} +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +{{/addConsumesProducesJson}} +public interface {{classname}} { +{{#operations}} +{{#operation}} + + {{#summary}} + /** + * {{summary}} + * + {{#notes}} + * {{.}} + * + {{/notes}} + */ + {{/summary}} + @{{httpMethod}} + {{#subresourceOperation}}@Path("{{{path}}}"){{/subresourceOperation}} +{{#hasConsumes}} + @Consumes({ {{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}} }) +{{/hasConsumes}} +{{#hasProduces}} + @Produces({ {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}} }) +{{/hasProduces}} + @Operation(summary = "{{{summary}}}", tags={ {{#vendorExtensions.x-tags}}"{{tag}}"{{^-last}}, {{/-last}}{{/vendorExtensions.x-tags}} }) + {{#implicitHeadersParams.0}} + @Parameters({ + {{#implicitHeadersParams}} + @Parameter(name = "{{{baseName}}}", description = "{{{description}}}", {{#required}}required = true,{{/required}} in = ParameterIn.HEADER, content = @Content(schema = @Schema(type = "{{{dataType}}}"))){{^-last}},{{/-last}} + {{/implicitHeadersParams}} + }) + {{/implicitHeadersParams.0}} + @ApiResponses(value = { {{#responses}} + @ApiResponse(responseCode = "{{{code}}}", description = "{{{message}}}", content = @Content(mediaType = {{#content}}{{#entrySet}}"{{{key}}}"{{/entrySet}}{{/content}}{{^content}}"*/*"{{/content}}, {{#containerType}}array = @ArraySchema({{/containerType}}schema = @Schema(implementation = {{#isFile}}{{#useAbstractionForFiles}}InputStream{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{baseType}}}{{/useAbstractionForFiles}}{{/isFile}}{{^isFile}}{{{baseType}}}{{/isFile}}.class){{#containerType}}){{/containerType}})){{^-last}},{{/-last}}{{/responses}} }) + public {{>returnTypes}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{/allParams}}); +{{/operation}} +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/cxf/pojo.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/cxf/pojo.mustache index 6fb7cc552873..67bc45f509cb 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/cxf/pojo.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/cxf/pojo.mustache @@ -1,4 +1,9 @@ +{{#oas3}} +import io.swagger.v3.oas.annotations.media.Schema; +{{/oas3}} +{{^oas3}} import io.swagger.annotations.ApiModelProperty; +{{/oas3}} import java.util.Objects; {{#withXml}} import {{javaxPackage}}.xml.bind.annotation.XmlElement; @@ -13,9 +18,9 @@ import com.fasterxml.jackson.annotation.JsonProperty; {{#description}} /** - * {{{description}}} + * {{{.}}} **/ -@ApiModel(description="{{{description}}}") +{{^oas3}}@ApiModel(description="{{{.}}}"){{/oas3}} {{/description}} {{>additionalModelTypeAnnotations}}{{>xmlPojoAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}} {{#vendorExtensions.x-class-extra-annotation}} @@ -28,7 +33,7 @@ public class {{classname}} {{#parent}}extends {{{.}}}{{/parent}}{{#vendorExtensi {{#withXml}} @XmlElement(name="{{baseName}}"{{#required}}, required = {{required}}{{/required}}) {{/withXml}} - @ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}value = "{{{description}}}"){{^isPrimitiveType}}{{^isDate}}{{^isDateTime}}{{^isString}}{{^isFile}}{{#useBeanValidation}} +{{#oas3}} @Schema({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}description = "{{{description}}}"){{/oas3}}{{^oas3}} @ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}value = "{{{description}}}"){{/oas3}}{{^isPrimitiveType}}{{^isDate}}{{^isDateTime}}{{^isString}}{{^isFile}}{{#useBeanValidation}} @Valid{{/useBeanValidation}}{{/isFile}}{{/isString}}{{/isDateTime}}{{/isDate}}{{/isPrimitiveType}} {{#description}} /** diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaCXFClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaCXFClientCodegenTest.java index e1e9dd7285c6..c957555b470d 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaCXFClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaCXFClientCodegenTest.java @@ -336,4 +336,59 @@ public void testUseAbstractionForFiles() throws Exception { "public FilesUploadPost200Response filesUploadPost(InputStream body);" ); } + + @Test + public void testUseOas3Imports() throws Exception { + + Map properties = new HashMap<>(); + properties.put(CodegenConstants.API_PACKAGE, "xyz.abcdef.api"); + properties.put(CodegenConstants.MODEL_PACKAGE, "xyz.abcdef.api"); + + properties.put(JavaCXFClientCodegen.OAS3, true); + + File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("jaxrs-cxf-client") + .setAdditionalProperties(properties) + .setInputSpec("src/test/resources/3_1/java/issue_19907.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + + DefaultGenerator generator = new DefaultGenerator(); + List files = generator.opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + + validateJavaSourceFiles(files); + + Path petApi = Paths.get(output + "/src/gen/java/xyz/abcdef/api/PetApi.java"); + Path petModel = Paths.get(output + "/src/gen/java/xyz/abcdef/api/Pet.java"); + + TestUtils.assertFileContains(petApi, + // imports + "import io.swagger.v3.oas.annotations.responses.ApiResponses;", + "import io.swagger.v3.oas.annotations.responses.ApiResponse;", + "import io.swagger.v3.oas.annotations.media.ArraySchema;", + "import io.swagger.v3.oas.annotations.media.Content;", + + "@Path(\"/pet/{petId}\")", + "@Produces({ \"application/json\" })", + "@Operation(summary = \"Find pet by ID\", tags={ })", + "@ApiResponse(responseCode = \"200\", description = \"successful operation\", content = @Content(mediaType = \"application/json\", schema = @Schema(implementation = Pet.class))),", + "@ApiResponse(responseCode = \"400\", description = \"Invalid ID supplied\", content = @Content(mediaType = \"*/*\", schema = @Schema(implementation = Void.class))),", + "@ApiResponse(responseCode = \"404\", description = \"Pet not found\", content = @Content(mediaType = \"*/*\", schema = @Schema(implementation = Void.class))) })", + "public Pet getPetById(@PathParam(\"petId\") Long petId);" + + ); + + TestUtils.assertFileNotContains(petModel, + "import io.swagger.annotations.ApiModel;", + "@ApiModel(description=\"A pet for sale in the pet store\")" + ); + + TestUtils.assertFileContains(petModel, + "@Schema(description = \"\")" + ); + } } diff --git a/modules/openapi-generator/src/test/resources/3_1/java/issue_19907.yaml b/modules/openapi-generator/src/test/resources/3_1/java/issue_19907.yaml new file mode 100644 index 000000000000..2859af19dcd4 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_1/java/issue_19907.yaml @@ -0,0 +1,112 @@ +openapi: 3.1.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + This is a sample server Petstore server. For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: OpenAPI Petstore + license: + name: Apache-2.0 + url: 'https://www.apache.org/licenses/LICENSE-2.0.html' +paths: + '/pet/{petId}': + get: + tags: + - pet + summary: Find pet by ID + description: Returns a single pet + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found +components: + schemas: + Tag: + title: Pet Tag + description: A tag for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + Pet: + title: a Pet + description: A pet for sale in the pet store + type: object + properties: + tags: + type: array + items: + $ref: '#/components/schemas/Tag' + tagsDefaultList: + type: array + default: [] + items: + $ref: '#/components/schemas/Tag' + tagsUnique: + type: array + uniqueItems: true + items: + $ref: '#/components/schemas/Tag' + tagsDefaultSet: + type: array + default: [ ] + uniqueItems: true + items: + $ref: '#/components/schemas/Tag' + stringList: + type: array + items: + type: string + stringDefaultList: + type: array + default: + - A + - B + items: + type: string + stringEmptyDefaultList: + type: array + default: [] + items: + type: string + stringSet: + type: array + uniqueItems: true + items: + type: string + stringDefaultSet: + type: array + uniqueItems: true + default: + - A + - B + items: + type: string + stringEmptyDefaultSet: + type: array + uniqueItems: true + default: [] + items: + type: string +