From b8e6b708070c9a27e4b53c0ffafe9054dde01bd1 Mon Sep 17 00:00:00 2001 From: Ignacio Vidal Date: Sun, 6 Sep 2026 13:50:36 +0100 Subject: [PATCH] [Java][jaxrs-spec] add JSpecify support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit jaxrs-spec had no jspecify support at all — no `useJspecify` option, and the generator emitted no nullability annotations for any library. This adds it generator-wide rather than per-library, because the model and parameter templates are shared by all six libraries (default, quarkus, thorntail, openliberty, helidon, kumuluzee); only quarkus overrides formParams. - JavaJAXRSSpecServerCodegen: `useJspecify` cliOption, applyJspecify() and the Nullable imports for models and operations. - Shared pojo.mustache (field, getter, setter, fluent setter) and the six parameter partials, plus the quarkus formParams override. - New JavaJaxRS/spec copies of modelPackageInfo/apiPackageInfo and the nullable* partials: unlike the java client libraries, which all share one `Java` template dir, jaxrs-spec has its own with no fallback. - Base pom swaps jsr305 for jspecify under the flag, matching how the java client libraries handle it; the quarkus pom adds jspecify. applyJspecify() must run *after* the `supportingFiles.clear()` in processOpts(), which would otherwise drop the @NullMarked package-info files. jaxrs-cxf-cdi extends this generator but uses its own cxf-cdi template directory, so it would have advertised the option without honouring it — the exact defect this change fixes elsewhere. It calls removeOption, alongside the existing removeOption(GENERATE_JSON_CREATOR). Every template change is guarded by {{#useJspecify}}: flag-off output is byte-identical to before, verified by diffing against a jar built from the parent branch for both the default and quarkus libraries. Adds a sample (jaxrs-spec-quarkus-jspecify, registered in samples-jdk17 since the quarkus library targets Java 17) and two tests covering the annotations, type-use placement on qualified types, and that the flag off still emits jsr305 and no annotations. --- .github/workflows/samples-jdk17.yaml | 3 + bin/configs/jaxrs-spec-quarkus-jspecify.yaml | 16 + docs/generators/jaxrs-spec.md | 1 + .../JavaJAXRSCXFCDIServerCodegen.java | 10 + .../languages/JavaJAXRSSpecServerCodegen.java | 22 + .../JavaJaxRS/spec/apiPackageInfo.mustache | 2 + .../JavaJaxRS/spec/bodyParams.mustache | 2 +- .../JavaJaxRS/spec/cookieParams.mustache | 2 +- .../JavaJaxRS/spec/formParams.mustache | 2 +- .../JavaJaxRS/spec/headerParams.mustache | 2 +- .../libraries/quarkus/formParams.mustache | 2 +- .../spec/libraries/quarkus/pom.mustache | 7 + .../JavaJaxRS/spec/modelPackageInfo.mustache | 2 + .../JavaJaxRS/spec/nullableDataType.mustache | 1 + .../spec/nullableDatatypeWithEnum.mustache | 1 + .../spec/nullable_var_annotations.mustache | 1 + .../JavaJaxRS/spec/pathParams.mustache | 2 +- .../resources/JavaJaxRS/spec/pojo.mustache | 8 +- .../resources/JavaJaxRS/spec/pom.mustache | 9 + .../JavaJaxRS/spec/queryParams.mustache | 2 +- .../jaxrs/JavaJAXRSSpecServerCodegenTest.java | 117 ++++++ .../jaxrs-spec-quarkus-jspecify/.dockerignore | 4 + .../.openapi-generator-ignore | 23 + .../.openapi-generator/FILES | 19 + .../.openapi-generator/VERSION | 1 + .../jaxrs-spec-quarkus-jspecify/README.md | 15 + .../jaxrs-spec-quarkus-jspecify/pom.xml | 164 ++++++++ .../java/org/openapitools/api/FileApi.java | 27 ++ .../gen/java/org/openapitools/api/FooApi.java | 28 ++ .../api/RequiredAndNullableApi.java | 27 ++ .../org/openapitools/api/RestApplication.java | 9 + .../openapitools/api/RestResourceRoot.java | 5 + .../java/org/openapitools/api/UploadApi.java | 30 ++ .../org/openapitools/api/UploadFilesApi.java | 30 ++ .../org/openapitools/api/package-info.java | 2 + .../org/openapitools/model/FileContent.java | 174 ++++++++ .../gen/java/org/openapitools/model/Foo.java | 393 ++++++++++++++++++ .../model/RequiredAndNullable.java | 197 +++++++++ .../org/openapitools/model/package-info.java | 2 + .../src/main/docker/Dockerfile.jvm | 34 ++ .../src/main/docker/Dockerfile.native | 22 + .../src/main/resources/META-INF/openapi.yaml | 294 +++++++++++++ .../src/main/resources/application.properties | 5 + 43 files changed, 1708 insertions(+), 11 deletions(-) create mode 100644 bin/configs/jaxrs-spec-quarkus-jspecify.yaml create mode 100644 modules/openapi-generator/src/main/resources/JavaJaxRS/spec/apiPackageInfo.mustache create mode 100644 modules/openapi-generator/src/main/resources/JavaJaxRS/spec/modelPackageInfo.mustache create mode 100644 modules/openapi-generator/src/main/resources/JavaJaxRS/spec/nullableDataType.mustache create mode 100644 modules/openapi-generator/src/main/resources/JavaJaxRS/spec/nullableDatatypeWithEnum.mustache create mode 100644 modules/openapi-generator/src/main/resources/JavaJaxRS/spec/nullable_var_annotations.mustache create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/.dockerignore create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/.openapi-generator-ignore create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/.openapi-generator/FILES create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/.openapi-generator/VERSION create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/README.md create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/pom.xml create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/FileApi.java create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/FooApi.java create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/RequiredAndNullableApi.java create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/RestApplication.java create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/RestResourceRoot.java create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/UploadApi.java create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/UploadFilesApi.java create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/package-info.java create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/FileContent.java create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/Foo.java create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/RequiredAndNullable.java create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/package-info.java create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/docker/Dockerfile.jvm create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/docker/Dockerfile.native create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/resources/META-INF/openapi.yaml create mode 100644 samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/resources/application.properties diff --git a/.github/workflows/samples-jdk17.yaml b/.github/workflows/samples-jdk17.yaml index dbe0fb2580ac..210db8aae521 100644 --- a/.github/workflows/samples-jdk17.yaml +++ b/.github/workflows/samples-jdk17.yaml @@ -24,6 +24,7 @@ on: - samples/server/petstore/java-helidon-server/v3/se/** - samples/server/petstore/jaxrs-spec-sealed/** - samples/server/petstore/jaxrs-spec/quarkus-security/** + - samples/server/petstore/jaxrs-spec-quarkus-jspecify/** pull_request: paths: # clients @@ -48,6 +49,7 @@ on: - samples/server/petstore/java-helidon-server/v3/se/** - samples/server/petstore/jaxrs-spec-sealed/** - samples/server/petstore/jaxrs-spec/quarkus-security/** + - samples/server/petstore/jaxrs-spec-quarkus-jspecify/** jobs: build: name: Build with JDK17 @@ -78,6 +80,7 @@ jobs: - samples/server/petstore/java-helidon-server/v3/se - samples/server/petstore/jaxrs-spec-sealed - samples/server/petstore/jaxrs-spec/quarkus-security + - samples/server/petstore/jaxrs-spec-quarkus-jspecify steps: - uses: actions/checkout@v7 - uses: actions/setup-java@v6.0.1 diff --git a/bin/configs/jaxrs-spec-quarkus-jspecify.yaml b/bin/configs/jaxrs-spec-quarkus-jspecify.yaml new file mode 100644 index 000000000000..53485bd4fa13 --- /dev/null +++ b/bin/configs/jaxrs-spec-quarkus-jspecify.yaml @@ -0,0 +1,16 @@ +generatorName: jaxrs-spec +outputDir: samples/server/petstore/jaxrs-spec-quarkus-jspecify +inputSpec: modules/openapi-generator/src/test/resources/3_0/java/jspecify.yaml +templateDir: modules/openapi-generator/src/main/resources/JavaJaxRS/spec +validateSpec: false +additionalProperties: + artifactId: jaxrs-spec-quarkus-jspecify + library: "quarkus" + hideGenerationTimestamp: "true" + interfaceOnly: "true" + useJakartaEe: "true" + useJspecify: "true" + openApiNullable: "false" + dateLibrary: "java8" +typeMappings: + BigDecimal: java.math.BigDecimal diff --git a/docs/generators/jaxrs-spec.md b/docs/generators/jaxrs-spec.md index d6251797095b..4affdbb922a3 100644 --- a/docs/generators/jaxrs-spec.md +++ b/docs/generators/jaxrs-spec.md @@ -86,6 +86,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |useEnumCaseInsensitive|Use `equalsIgnoreCase` when String for enum comparison| |false| |useJakartaEe|whether to use Jakarta EE namespace instead of javax| |false| |useJakartaSecurityAnnotations|Whether to generate Jakarta security annotations (@RolesAllowed, @PermitAll). Requires useJakartaEe=true. Currently only supported when library is set to quarkus.| |false| +|useJspecify|Use JSpecify for null checks: @NullMarked package-info and @Nullable on optional properties and parameters.| |false| |useMicroProfileOpenAPIAnnotations|Whether to generate Microprofile OpenAPI annotations. Only valid when library is set to quarkus.| |false| |useMutiny|Whether to use Smallrye Mutiny instead of CompletionStage for asynchronous computation. Only valid when library is set to quarkus.| |false| |useOneOfInterfaces|whether to use a java interface to describe a set of oneOf options, where each option is a class that implements the interface| |false| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaJAXRSCXFCDIServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaJAXRSCXFCDIServerCodegen.java index d4ba2a8dc3bd..b64e836ae598 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaJAXRSCXFCDIServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaJAXRSCXFCDIServerCodegen.java @@ -64,6 +64,9 @@ public JavaJAXRSCXFCDIServerCodegen() { embeddedTemplateDir = templateDir = JAXRS_TEMPLATE_DIRECTORY_NAME + File.separator + "cxf-cdi"; removeOption(JavaJAXRSSpecServerCodegen.GENERATE_JSON_CREATOR); + // jspecify support lives in the JavaJaxRS/spec templates; this generator uses its own + // cxf-cdi template directory, so the option would be advertised but have no effect. + removeOption(USE_JSPECIFY); } @Override @@ -73,6 +76,13 @@ public String getName() { @Override public void processOpts() { + // Force jspecify off before super.processOpts(), which is where applyJspecify() runs. + // removeOption() only hides the option from the advertised CLI list; the flag can still + // arrive through additionalProperties (a config file, or -p useJspecify=true) and would + // then emit imports and package-info for templates this generator does not have. + additionalProperties.remove(USE_JSPECIFY); + setUseJspecify(false); + super.processOpts(); supportingFiles.clear(); // Don't need extra files provided by AbstractJAX-RS & Java Codegen diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaJAXRSSpecServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaJAXRSSpecServerCodegen.java index 9f6a73e53b85..e7a8468f4fab 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaJAXRSSpecServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaJAXRSSpecServerCodegen.java @@ -171,6 +171,7 @@ public JavaJAXRSSpecServerCodegen() { cliOptions.add(CliOption.newBoolean(GENERATE_JSON_CREATOR, "Whether to generate @JsonCreator constructor for required properties.", generateJsonCreator)); cliOptions.add(CliOption.newBoolean(USE_ENUM_CASE_INSENSITIVE, "Use `equalsIgnoreCase` when String for enum comparison", useEnumCaseInsensitive)); cliOptions.add(CliOption.newBoolean(USE_SEALED, "Whether to generate sealed model interfaces and classes.", useSealed)); + cliOptions.add(CliOption.newBoolean(USE_JSPECIFY, "Use JSpecify for null checks: @NullMarked package-info and @Nullable on optional properties and parameters.", useJspecify)); } @Override @@ -255,6 +256,11 @@ public void processOpts() { supportingFiles.add(new SupportingFile("README.mustache", "", "README.md") .doNotOverwrite()); + // after the clear() above, which would otherwise drop the @NullMarked package-info files + if (useJspecify) { + applyJspecify(); + } + if ((!interfaceOnly) || generateRootResources) { supportingFiles.add(new SupportingFile("RestResourceRoot.mustache", (sourceFolder + '/' + invokerPackage).replace(".", "/"), "RestResourceRoot.java") @@ -348,6 +354,10 @@ public CodegenModel fromModel(String name, Schema model) { codegenModel.imports.remove("JsonProperty"); codegenModel.imports.remove("JsonTypeName"); } + if (useJspecify) { + codegenModel.imports.add("Nullable"); + } + return codegenModel; } @@ -481,6 +491,18 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation if (QUARKUS_LIBRARY.equals(getLibrary()) && useJakartaSecurityAnnotations) { jakartaSecurityAnnotationProcessor.applyTo(op, operation, openAPI); } + if (useJspecify) { + addNullableImportForOperation(op); + } return op; } + + @Override + protected void applyJspecify() { + super.applyJspecify(); + // nullable_var_annotations.mustache emits @{{javaxPackage}}.annotation.Nullable; the lambda + // finds that and re-injects a bare @Nullable in type-use position, so tell it what to look for. + jSpecifyNullableLambda.setNullableAnnotation("@" + additionalProperties.get(JAVAX_PACKAGE) + ".annotation.Nullable"); + } + } diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/apiPackageInfo.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/apiPackageInfo.mustache new file mode 100644 index 000000000000..315e9eae09cb --- /dev/null +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/apiPackageInfo.mustache @@ -0,0 +1,2 @@ +@org.jspecify.annotations.NullMarked +package {{apiPackage}}; diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/bodyParams.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/bodyParams.mustache index ad24da52660b..13da3564d8fb 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/bodyParams.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/bodyParams.mustache @@ -1 +1 @@ -{{#isBodyParam}}{{#isDeprecated}}@Deprecated {{/isDeprecated}}{{#useBeanValidation}}@Valid {{#required}}{{^isNullable}}@NotNull {{/isNullable}}{{/required}}{{/useBeanValidation}}{{{dataType}}} {{paramName}}{{/isBodyParam}} \ No newline at end of file +{{#isBodyParam}}{{#isDeprecated}}@Deprecated {{/isDeprecated}}{{#useBeanValidation}}@Valid {{#required}}{{^isNullable}}@NotNull {{/isNullable}}{{/required}}{{/useBeanValidation}}{{#useJspecify}}{{>nullable_var_annotations}}{{/useJspecify}}{{>nullableDataType}} {{paramName}}{{/isBodyParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/cookieParams.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/cookieParams.mustache index 0f8d12e85bb5..87a63b8631fd 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/cookieParams.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/cookieParams.mustache @@ -1 +1 @@ -{{#isCookieParam}}{{#isDeprecated}}@Deprecated {{/isDeprecated}}@CookieParam("{{baseName}}"){{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{^isContainer}}{{#defaultValue}} @DefaultValue("{{{.}}}"){{/defaultValue}}{{/isContainer}} {{#useSwaggerAnnotations}}{{#description}} @ApiParam("{{.}}"){{/description}}{{/useSwaggerAnnotations}}{{#useMicroProfileOpenAPIAnnotations}}{{#description}} @org.eclipse.microprofile.openapi.annotations.parameters.Parameter(description="{{.}}"){{/description}}{{/useMicroProfileOpenAPIAnnotations}} {{{dataType}}} {{paramName}}{{/isCookieParam}} \ No newline at end of file +{{#isCookieParam}}{{#isDeprecated}}@Deprecated {{/isDeprecated}}@CookieParam("{{baseName}}"){{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{^isContainer}}{{#defaultValue}} @DefaultValue("{{{.}}}"){{/defaultValue}}{{/isContainer}} {{#useSwaggerAnnotations}}{{#description}} @ApiParam("{{.}}"){{/description}}{{/useSwaggerAnnotations}}{{#useMicroProfileOpenAPIAnnotations}}{{#description}} @org.eclipse.microprofile.openapi.annotations.parameters.Parameter(description="{{.}}"){{/description}}{{/useMicroProfileOpenAPIAnnotations}} {{#useJspecify}}{{>nullable_var_annotations}}{{/useJspecify}}{{>nullableDataType}} {{paramName}}{{/isCookieParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/formParams.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/formParams.mustache index e67c9fda95f3..a3c45f23cb41 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/formParams.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/formParams.mustache @@ -1,2 +1,2 @@ {{#isFormParam}} -{{#isDeprecated}}@Deprecated {{/isDeprecated}}{{^isFile}}@FormParam(value = "{{baseName}}") {{{dataType}}} {{paramName}}{{/isFile}}{{#isFile}}@FormParam(value = "{{baseName}}") InputStream {{paramName}}InputStream{{/isFile}}{{/isFormParam}} \ No newline at end of file +{{#isDeprecated}}@Deprecated {{/isDeprecated}}{{^isFile}}@FormParam(value = "{{baseName}}") {{#useJspecify}}{{>nullable_var_annotations}}{{/useJspecify}}{{>nullableDataType}} {{paramName}}{{/isFile}}{{#isFile}}@FormParam(value = "{{baseName}}") InputStream {{paramName}}InputStream{{/isFile}}{{/isFormParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/headerParams.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/headerParams.mustache index 80e256e18153..176eb9524623 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/headerParams.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/headerParams.mustache @@ -1 +1 @@ -{{#isHeaderParam}}{{#isDeprecated}}@Deprecated {{/isDeprecated}}@HeaderParam("{{baseName}}"){{#useBeanValidation}}{{>beanValidationHeaderParams}}{{/useBeanValidation}} {{#defaultValue}} @DefaultValue("{{{.}}}"){{/defaultValue}} {{#useSwaggerAnnotations}}{{#description}} @ApiParam("{{.}}"){{/description}}{{/useSwaggerAnnotations}}{{#useMicroProfileOpenAPIAnnotations}}{{#description}} @org.eclipse.microprofile.openapi.annotations.parameters.Parameter(description="{{.}}"){{/description}}{{/useMicroProfileOpenAPIAnnotations}} {{{dataType}}} {{paramName}}{{/isHeaderParam}} \ No newline at end of file +{{#isHeaderParam}}{{#isDeprecated}}@Deprecated {{/isDeprecated}}@HeaderParam("{{baseName}}"){{#useBeanValidation}}{{>beanValidationHeaderParams}}{{/useBeanValidation}} {{#defaultValue}} @DefaultValue("{{{.}}}"){{/defaultValue}} {{#useSwaggerAnnotations}}{{#description}} @ApiParam("{{.}}"){{/description}}{{/useSwaggerAnnotations}}{{#useMicroProfileOpenAPIAnnotations}}{{#description}} @org.eclipse.microprofile.openapi.annotations.parameters.Parameter(description="{{.}}"){{/description}}{{/useMicroProfileOpenAPIAnnotations}} {{#useJspecify}}{{>nullable_var_annotations}}{{/useJspecify}}{{>nullableDataType}} {{paramName}}{{/isHeaderParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/libraries/quarkus/formParams.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/libraries/quarkus/formParams.mustache index f0bd273244da..efa6f6dc539b 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/libraries/quarkus/formParams.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/libraries/quarkus/formParams.mustache @@ -1,2 +1,2 @@ {{#isFormParam}} -{{#isDeprecated}}@Deprecated {{/isDeprecated}}{{^isFile}}@FormParam(value = "{{baseName}}") {{{dataType}}} {{paramName}}{{/isFile}}{{#isFile}}{{#useJakartaEe}}{{^isArray}}@RestForm(value = "{{baseName}}") FileUpload {{paramName}}{{/isArray}}{{#isArray}}@RestForm(value = "{{baseName}}") List {{paramName}}{{/isArray}}{{/useJakartaEe}}{{^useJakartaEe}}@FormParam(value = "{{baseName}}") InputStream {{paramName}}InputStream{{/useJakartaEe}}{{/isFile}}{{/isFormParam}} \ No newline at end of file +{{#isDeprecated}}@Deprecated {{/isDeprecated}}{{^isFile}}@FormParam(value = "{{baseName}}") {{#useJspecify}}{{>nullable_var_annotations}}{{/useJspecify}}{{>nullableDataType}} {{paramName}}{{/isFile}}{{#isFile}}{{#useJakartaEe}}{{^isArray}}@RestForm(value = "{{baseName}}") FileUpload {{paramName}}{{/isArray}}{{#isArray}}@RestForm(value = "{{baseName}}") List {{paramName}}{{/isArray}}{{/useJakartaEe}}{{^useJakartaEe}}@FormParam(value = "{{baseName}}") InputStream {{paramName}}InputStream{{/useJakartaEe}}{{/isFile}}{{/isFormParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/libraries/quarkus/pom.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/libraries/quarkus/pom.mustache index 779d8e495932..c50633fb56de 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/libraries/quarkus/pom.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/libraries/quarkus/pom.mustache @@ -179,6 +179,13 @@ ${jackson-databind-nullable-version} {{/openApiNullable}} + {{#useJspecify}} + + org.jspecify + jspecify + 1.0.0 + + {{/useJspecify}} diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/modelPackageInfo.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/modelPackageInfo.mustache new file mode 100644 index 000000000000..9de75276e352 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/modelPackageInfo.mustache @@ -0,0 +1,2 @@ +@org.jspecify.annotations.NullMarked +package {{modelPackage}}; diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/nullableDataType.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/nullableDataType.mustache new file mode 100644 index 000000000000..aa95491ce5ab --- /dev/null +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/nullableDataType.mustache @@ -0,0 +1 @@ +{{#lambda.jSpecifyDatatype}}{{{dataType}}}{{/lambda.jSpecifyDatatype}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/nullableDatatypeWithEnum.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/nullableDatatypeWithEnum.mustache new file mode 100644 index 000000000000..8fbd3969b872 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/nullableDatatypeWithEnum.mustache @@ -0,0 +1 @@ +{{#lambda.jSpecifyDatatype}}{{{datatypeWithEnum}}}{{/lambda.jSpecifyDatatype}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/nullable_var_annotations.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/nullable_var_annotations.mustache new file mode 100644 index 000000000000..7b13c89bca91 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/nullable_var_annotations.mustache @@ -0,0 +1 @@ +{{#lambda.jSpecifyNullable}}{{^useJspecify}}{{#required}}{{#isNullable}}@{{javaxPackage}}.annotation.Nullable{{/isNullable}}{{^isNullable}}@{{javaxPackage}}.annotation.Nonnull{{/isNullable}}{{/required}}{{^required}}@{{javaxPackage}}.annotation.Nullable{{/required}}{{/useJspecify}}{{#useJspecify}}{{#required}}{{#isNullable}}@{{javaxPackage}}.annotation.Nullable {{/isNullable}}{{/required}}{{^required}}@{{javaxPackage}}.annotation.Nullable {{/required}}{{/useJspecify}}{{/lambda.jSpecifyNullable}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pathParams.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pathParams.mustache index fa317ee181bf..1ebdb70afa06 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pathParams.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pathParams.mustache @@ -1 +1 @@ -{{#isPathParam}}{{#isDeprecated}}@Deprecated {{/isDeprecated}}@PathParam("{{baseName}}"){{#useBeanValidation}}{{>beanValidationPathParams}}{{/useBeanValidation}}{{#useSwaggerAnnotations}}{{#description}} @ApiParam("{{.}}"){{/description}}{{/useSwaggerAnnotations}}{{#useMicroProfileOpenAPIAnnotations}}{{#description}} @org.eclipse.microprofile.openapi.annotations.parameters.Parameter(description="{{.}}"){{/description}}{{/useMicroProfileOpenAPIAnnotations}} {{{dataType}}} {{paramName}}{{/isPathParam}} \ No newline at end of file +{{#isPathParam}}{{#isDeprecated}}@Deprecated {{/isDeprecated}}@PathParam("{{baseName}}"){{#useBeanValidation}}{{>beanValidationPathParams}}{{/useBeanValidation}}{{#useSwaggerAnnotations}}{{#description}} @ApiParam("{{.}}"){{/description}}{{/useSwaggerAnnotations}}{{#useMicroProfileOpenAPIAnnotations}}{{#description}} @org.eclipse.microprofile.openapi.annotations.parameters.Parameter(description="{{.}}"){{/description}}{{/useMicroProfileOpenAPIAnnotations}} {{#useJspecify}}{{>nullable_var_annotations}}{{/useJspecify}}{{>nullableDataType}} {{paramName}}{{/isPathParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pojo.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pojo.mustache index 2e6f060545e4..17023dd822ff 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pojo.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pojo.mustache @@ -80,7 +80,7 @@ public {{>sealed}}class {{classname}} {{#parent}}extends {{{.}}}{{/parent}} {{#v {{/isContainer}} {{/vendorExtensions.x-is-jackson-optional-nullable}} {{^vendorExtensions.x-is-jackson-optional-nullable}} - private {{#isContainer}}{{#useBeanValidation}}@Valid {{/useBeanValidation}}{{/isContainer}}{{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}; + private {{#isContainer}}{{#useBeanValidation}}@Valid {{/useBeanValidation}}{{/isContainer}}{{#useJspecify}}{{>nullable_var_annotations}}{{/useJspecify}}{{>nullableDatatypeWithEnum}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}; {{/vendorExtensions.x-is-jackson-optional-nullable}} {{/vars}} @@ -144,7 +144,7 @@ public {{>sealed}}class {{classname}} {{#parent}}extends {{{.}}}{{/parent}} {{#v {{#deprecated}} @Deprecated {{/deprecated}} - public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) { + public {{classname}} {{name}}({{#useJspecify}}{{>nullable_var_annotations}}{{/useJspecify}}{{>nullableDatatypeWithEnum}} {{name}}) { {{#vendorExtensions.x-is-jackson-optional-nullable}} this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{name}}); {{/vendorExtensions.x-is-jackson-optional-nullable}} @@ -174,7 +174,7 @@ public {{>sealed}}class {{classname}} {{#parent}}extends {{{.}}}{{/parent}} {{#v } {{/vendorExtensions.x-is-jackson-optional-nullable}} {{^vendorExtensions.x-is-jackson-optional-nullable}} - {{#useBeanValidation}}{{>beanValidation}}{{/useBeanValidation}}public {{>beanValidatedType}} {{getter}}() { + {{#useBeanValidation}}{{>beanValidation}}{{/useBeanValidation}}public {{#useJspecify}}{{>nullable_var_annotations}}{{/useJspecify}}{{#lambda.jSpecifyDatatype}}{{>beanValidatedType}}{{/lambda.jSpecifyDatatype}} {{getter}}() { return {{name}}; } {{/vendorExtensions.x-is-jackson-optional-nullable}} @@ -189,7 +189,7 @@ public {{>sealed}}class {{classname}} {{#parent}}extends {{{.}}}{{/parent}} {{#v @JsonProperty({{#required}}required = {{required}}, value = {{/required}}"{{baseName}}") {{/jackson}} {{#vendorExtensions.x-setter-extra-annotation}}{{{vendorExtensions.x-setter-extra-annotation}}} - {{/vendorExtensions.x-setter-extra-annotation}}public void {{setter}}({{{datatypeWithEnum}}} {{name}}) { + {{/vendorExtensions.x-setter-extra-annotation}}public void {{setter}}({{#useJspecify}}{{>nullable_var_annotations}}{{/useJspecify}}{{>nullableDatatypeWithEnum}} {{name}}) { {{#vendorExtensions.x-is-jackson-optional-nullable}} this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{name}}); {{/vendorExtensions.x-is-jackson-optional-nullable}} diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pom.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pom.mustache index 8640e276e9ee..200561be0643 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pom.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pom.mustache @@ -133,11 +133,20 @@ {{/useSwaggerV3Annotations}} + {{#useJspecify}} + + org.jspecify + jspecify + 1.0.0 + + {{/useJspecify}} + {{^useJspecify}} com.google.code.findbugs jsr305 3.0.2 + {{/useJspecify}} org.junit.jupiter junit-jupiter-engine diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/queryParams.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/queryParams.mustache index 3f8c985a4a71..015995bce97b 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/queryParams.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/queryParams.mustache @@ -1 +1 @@ -{{#isQueryParam}}{{#isDeprecated}}@Deprecated {{/isDeprecated}}@QueryParam("{{baseName}}"){{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{^isContainer}}{{#defaultValue}} @DefaultValue("{{{.}}}"){{/defaultValue}}{{/isContainer}} {{#useSwaggerAnnotations}}{{#description}} @ApiParam("{{.}}"){{/description}}{{/useSwaggerAnnotations}}{{#useMicroProfileOpenAPIAnnotations}}{{#description}} @org.eclipse.microprofile.openapi.annotations.parameters.Parameter(description="{{.}}"){{/description}}{{/useMicroProfileOpenAPIAnnotations}} {{{dataType}}} {{paramName}}{{/isQueryParam}} \ No newline at end of file +{{#isQueryParam}}{{#isDeprecated}}@Deprecated {{/isDeprecated}}@QueryParam("{{baseName}}"){{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{^isContainer}}{{#defaultValue}} @DefaultValue("{{{.}}}"){{/defaultValue}}{{/isContainer}} {{#useSwaggerAnnotations}}{{#description}} @ApiParam("{{.}}"){{/description}}{{/useSwaggerAnnotations}}{{#useMicroProfileOpenAPIAnnotations}}{{#description}} @org.eclipse.microprofile.openapi.annotations.parameters.Parameter(description="{{.}}"){{/description}}{{/useMicroProfileOpenAPIAnnotations}} {{#useJspecify}}{{>nullable_var_annotations}}{{/useJspecify}}{{>nullableDataType}} {{paramName}}{{/isQueryParam}} \ 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 64929cece8a9..167522e967f8 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 @@ -34,6 +34,7 @@ import static org.openapitools.codegen.languages.AbstractJavaCodegen.DISABLE_DISCRIMINATOR_JSON_IGNORE_PROPERTIES; import static org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen.*; import static org.openapitools.codegen.languages.features.GzipFeatures.USE_GZIP_FEATURE; +import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertTrue; /** @@ -2586,4 +2587,120 @@ public void testQuarkusJavaxKeepsResteasyClassicFileBinding() throws IOException "List"); assertFileContains(Paths.get(outputPath + "/pom.xml"), "quarkus-resteasy"); } + + /** + * useJspecify is generator-wide (the model/param templates are shared by every jaxrs-spec + * library), so the annotations must appear whichever library is selected. + */ + @Test + public void testUseJspecify() throws Exception { + Map properties = new HashMap<>(); + properties.put(USE_JSPECIFY, true); + properties.put(AbstractJavaJAXRSServerCodegen.USE_JAKARTA_EE, true); + properties.put(INTERFACE_ONLY, true); + properties.put(CodegenConstants.OPENAPI_NULLABLE, false); + + File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("jaxrs-spec") + .setLibrary("quarkus") + .setAdditionalProperties(properties) + .addTypeMapping("BigDecimal", "java.math.BigDecimal") + .setValidateSpec(false) + .setInputSpec("src/test/resources/3_0/java/jspecify.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + new DefaultGenerator().opts(configurator.toClientOptInput()).generate(); + + final Path model = Paths.get(output.toPath() + "/src/gen/java/org/openapitools/model/Foo.java"); + // optional properties are annotated, required ones are not + assertFileContains(model, + "import org.jspecify.annotations.Nullable;", + "private @Nullable Date dt;", + "private Date requiredDt;"); + // a qualified type keeps the annotation in type-use position + assertFileContains(model, "private java.math.@Nullable BigDecimal number;"); + + // the getter's return type must be annotated too. It renders via beanValidatedType, + // which emits the type directly, so the outer type has to be routed through the + // jSpecifyDatatype lambda or the annotation stashed by nullable_var_annotations is + // dropped and the getter falsely promises non-null under @NullMarked. + assertFileContains(model, + "public @Nullable Date getDt() {", + "public java.math.@Nullable BigDecimal getNumber() {"); + assertFileNotContains(model, "public Date getDt() {"); + + // @NullMarked is what makes the unannotated types mean non-null + assertFileContains(Paths.get(output.toPath() + "/src/gen/java/org/openapitools/model/package-info.java"), + "@org.jspecify.annotations.NullMarked"); + assertFileContains(Paths.get(output.toPath() + "/src/gen/java/org/openapitools/api/package-info.java"), + "@org.jspecify.annotations.NullMarked"); + + // operation parameters, alongside the JAX-RS annotations + final Path api = Paths.get(output.toPath() + "/src/gen/java/org/openapitools/api/FooApi.java"); + assertFileContains(api, "import org.jspecify.annotations.Nullable;", "@Nullable Date dtParam"); + } + + /** + * With the flag off, no nullability annotations are emitted and the pom keeps jsr305. + */ + @Test + public void testUseJspecifyDisabledByDefault() throws Exception { + Map properties = new HashMap<>(); + properties.put(AbstractJavaJAXRSServerCodegen.USE_JAKARTA_EE, true); + properties.put(INTERFACE_ONLY, true); + + File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("jaxrs-spec") + .setAdditionalProperties(properties) + .setValidateSpec(false) + .setInputSpec("src/test/resources/3_0/java/jspecify.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + new DefaultGenerator().opts(configurator.toClientOptInput()).generate(); + + assertFileNotContains(Paths.get(output.toPath() + "/src/gen/java/org/openapitools/model/Foo.java"), + "org.jspecify", "@Nullable"); + assertFileContains(Paths.get(output.toPath() + "/pom.xml"), "jsr305"); + assertFileNotContains(Paths.get(output.toPath() + "/pom.xml"), "jspecify"); + } + + + /** + * jaxrs-cxf-cdi extends this generator but uses its own template directory, which has no + * jspecify support. removeOption() only hides the option from the CLI listing, so the flag + * must also be forced off — otherwise a config file setting useJspecify=true emits the + * jspecify import into files that have no annotations and no @NullMarked package-info. + */ + @Test + public void testUseJspecifyIsDisabledForCxfCdi() throws Exception { + Map properties = new HashMap<>(); + properties.put(USE_JSPECIFY, true); + properties.put(AbstractJavaJAXRSServerCodegen.USE_JAKARTA_EE, true); + + File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("jaxrs-cxf-cdi") + .setAdditionalProperties(properties) + .setValidateSpec(false) + .setInputSpec("src/test/resources/3_0/java/jspecify.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + List files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate(); + + for (File file : files) { + if (file.getName().endsWith(".java")) { + assertFileNotContains(file.toPath(), "org.jspecify"); + } + assertNotEquals(file.getName(), "package-info.java"); + } + } + } diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/.dockerignore b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/.dockerignore new file mode 100644 index 000000000000..b86c7ac34057 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/.dockerignore @@ -0,0 +1,4 @@ +* +!target/*-runner +!target/*-runner.jar +!target/lib/* \ No newline at end of file diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/.openapi-generator-ignore b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/.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-quarkus-jspecify/.openapi-generator/FILES b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/.openapi-generator/FILES new file mode 100644 index 000000000000..032be962844e --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/.openapi-generator/FILES @@ -0,0 +1,19 @@ +.dockerignore +README.md +pom.xml +src/gen/java/org/openapitools/api/FileApi.java +src/gen/java/org/openapitools/api/FooApi.java +src/gen/java/org/openapitools/api/RequiredAndNullableApi.java +src/gen/java/org/openapitools/api/RestApplication.java +src/gen/java/org/openapitools/api/RestResourceRoot.java +src/gen/java/org/openapitools/api/UploadApi.java +src/gen/java/org/openapitools/api/UploadFilesApi.java +src/gen/java/org/openapitools/api/package-info.java +src/gen/java/org/openapitools/model/FileContent.java +src/gen/java/org/openapitools/model/Foo.java +src/gen/java/org/openapitools/model/RequiredAndNullable.java +src/gen/java/org/openapitools/model/package-info.java +src/main/docker/Dockerfile.jvm +src/main/docker/Dockerfile.native +src/main/resources/META-INF/openapi.yaml +src/main/resources/application.properties diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/.openapi-generator/VERSION b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/.openapi-generator/VERSION new file mode 100644 index 000000000000..32a8cfaceeb9 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.26.0-SNAPSHOT diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/README.md b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/README.md new file mode 100644 index 000000000000..578b4ebc0f3a --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/README.md @@ -0,0 +1,15 @@ +# JAX-RS server with OpenAPI using Quarkus + +## 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 and +the [Eclipse-MicroProfile-OpenAPI](https://github.com/eclipse/microprofile-open-api) addition. + +The pom file is configured to use [Quarkus](https://quarkus.io/) as application server. + +This project produces a jar that defines some interfaces. +The jar can be used in combination with another project providing the implementation. + diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/pom.xml b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/pom.xml new file mode 100644 index 000000000000..5b6e26a06eb3 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/pom.xml @@ -0,0 +1,164 @@ + + + 4.0.0 + org.openapitools + jaxrs-spec-quarkus-jspecify + jaxrs-spec-quarkus-jspecify + 1.0.0 + + + + 3.8.1 + true + 17 + 17 + UTF-8 + UTF-8 + 3.27.4.1 + 3.27.4.1 + quarkus-bom + io.quarkus + 2.22.1 + 3.1.0 + 2.1.1 + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + io.quarkus + quarkus-rest + + + + io.quarkus + quarkus-hibernate-validator + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-smallrye-openapi + + + io.quarkus.resteasy.reactive + resteasy-reactive + + + jakarta.ws.rs + jakarta.ws.rs-api + ${jakarta.ws.rs-version} + provided + + + jakarta.annotation + jakarta.annotation-api + ${jakarta.annotation-api-version} + + + org.jspecify + jspecify + 1.0.0 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.9.1 + + + add-source + generate-sources + + add-source + + + + src/gen/java + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus-plugin.version} + + + + build + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + native + + + + diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/FileApi.java b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/FileApi.java new file mode 100644 index 000000000000..2904eff181cc --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/FileApi.java @@ -0,0 +1,27 @@ +package org.openapitools.api; + +import org.openapitools.model.FileContent; + +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Response; +import org.jboss.resteasy.reactive.ResponseStatus; + + + +import java.io.InputStream; +import java.util.Map; +import java.util.List; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + + +@Path("/file/{id}") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", comments = "Generator version: 7.26.0-SNAPSHOT") +public interface FileApi { + + @GET + @Produces({ "application/json" }) + @ResponseStatus(200) + FileContent fileIdGet(@PathParam("id") String id); + +} diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/FooApi.java b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/FooApi.java new file mode 100644 index 000000000000..b05b941dbe41 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/FooApi.java @@ -0,0 +1,28 @@ +package org.openapitools.api; + +import org.openapitools.model.Foo; +import org.jspecify.annotations.Nullable; +import java.time.OffsetDateTime; + +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Response; +import org.jboss.resteasy.reactive.ResponseStatus; + + + +import java.io.InputStream; +import java.util.Map; +import java.util.List; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + + +@Path("/foo/{dtParam}") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", comments = "Generator version: 7.26.0-SNAPSHOT") +public interface FooApi { + + @GET + @Produces({ "application/json" }) + Foo fooDtParamGet(@PathParam("dtParam") @Nullable OffsetDateTime dtParam,@QueryParam("dtQuery") @Nullable OffsetDateTime dtQuery,@CookieParam("dtCookie") @Nullable OffsetDateTime dtCookie,@QueryParam("color") @DefaultValue("red") @Nullable String color); + +} diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/RequiredAndNullableApi.java b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/RequiredAndNullableApi.java new file mode 100644 index 000000000000..14141c348109 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/RequiredAndNullableApi.java @@ -0,0 +1,27 @@ +package org.openapitools.api; + +import org.openapitools.model.RequiredAndNullable; + +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Response; +import org.jboss.resteasy.reactive.ResponseStatus; + + + +import java.io.InputStream; +import java.util.Map; +import java.util.List; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + + +@Path("/requiredAndNullable") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", comments = "Generator version: 7.26.0-SNAPSHOT") +public interface RequiredAndNullableApi { + + @POST + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + RequiredAndNullable requiredAndNullablePost(@Valid @NotNull RequiredAndNullable requiredAndNullable); + +} diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/RestApplication.java b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/RestApplication.java new file mode 100644 index 000000000000..f6d53b4501db --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/RestApplication.java @@ -0,0 +1,9 @@ +package org.openapitools.api; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; + +@ApplicationPath(RestResourceRoot.APPLICATION_PATH) +public class RestApplication extends Application { + +} diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/RestResourceRoot.java b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/RestResourceRoot.java new file mode 100644 index 000000000000..727f0dfe3fb3 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/RestResourceRoot.java @@ -0,0 +1,5 @@ +package org.openapitools.api; + +public class RestResourceRoot { + public static final String APPLICATION_PATH = ""; +} diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/UploadApi.java b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/UploadApi.java new file mode 100644 index 000000000000..7d4b9d650bcb --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/UploadApi.java @@ -0,0 +1,30 @@ +package org.openapitools.api; + +import java.io.File; +import org.jspecify.annotations.Nullable; + +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Response; +import org.jboss.resteasy.reactive.ResponseStatus; + + + +import org.jboss.resteasy.reactive.RestForm; +import org.jboss.resteasy.reactive.multipart.FileUpload; + +import java.io.InputStream; +import java.util.Map; +import java.util.List; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + + +@Path("/upload") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", comments = "Generator version: 7.26.0-SNAPSHOT") +public interface UploadApi { + + @POST + @Consumes({ "multipart/form-data" }) + void uploadPost(@RestForm(value = "file") FileUpload _file); + +} diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/UploadFilesApi.java b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/UploadFilesApi.java new file mode 100644 index 000000000000..168a9c3c6ad3 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/UploadFilesApi.java @@ -0,0 +1,30 @@ +package org.openapitools.api; + +import java.io.File; +import org.jspecify.annotations.Nullable; + +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Response; +import org.jboss.resteasy.reactive.ResponseStatus; + + + +import org.jboss.resteasy.reactive.RestForm; +import org.jboss.resteasy.reactive.multipart.FileUpload; + +import java.io.InputStream; +import java.util.Map; +import java.util.List; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + + +@Path("/uploadFiles") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", comments = "Generator version: 7.26.0-SNAPSHOT") +public interface UploadFilesApi { + + @POST + @Consumes({ "multipart/form-data" }) + void uploadFilesPost(@RestForm(value = "file") List _file); + +} diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/package-info.java b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/package-info.java new file mode 100644 index 000000000000..46e4608c520d --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/api/package-info.java @@ -0,0 +1,2 @@ +@org.jspecify.annotations.NullMarked +package org.openapitools.api; diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/FileContent.java b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/FileContent.java new file mode 100644 index 000000000000..cee583aab484 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/FileContent.java @@ -0,0 +1,174 @@ +package org.openapitools.model; + +import org.jspecify.annotations.Nullable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +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("FileContent") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", comments = "Generator version: 7.26.0-SNAPSHOT") +public class FileContent { + private String name; + private @Nullable Integer size; + public enum VirusScanEnum { + + CLEAN(String.valueOf("clean")), DETECTED(String.valueOf("detected")); + + + private String value; + + VirusScanEnum (String v) { + value = v; + } + + public String value() { + return value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + /** + * Convert a String into String, as specified in the + * See JAX RS 2.0 Specification, section 3.2, p. 12 + */ + public static VirusScanEnum fromString(String s) { + for (VirusScanEnum b : VirusScanEnum.values()) { + // using Objects.toString() to be safe if value type non-object type + // because types like 'int' etc. will be auto-boxed + if (java.util.Objects.toString(b.value).equals(s)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected string value '" + s + "'"); + } + + @JsonCreator + public static VirusScanEnum fromValue(String value) { + for (VirusScanEnum b : VirusScanEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } +} + + private @Nullable VirusScanEnum virusScan; + + public FileContent() { + } + + @JsonCreator + public FileContent( + @JsonProperty(required = true, value = "name") String name + ) { + this.name = name; + } + + /** + **/ + public FileContent name(String name) { + this.name = name; + return this; + } + + + @JsonProperty(required = true, value = "name") + public String getName() { + return name; + } + + @JsonProperty(required = true, value = "name") + public void setName(String name) { + this.name = name; + } + + /** + **/ + public FileContent size(@Nullable Integer size) { + this.size = size; + return this; + } + + + @JsonProperty("size") + public @Nullable Integer getSize() { + return size; + } + + @JsonProperty("size") + public void setSize(@Nullable Integer size) { + this.size = size; + } + + /** + **/ + public FileContent virusScan(@Nullable VirusScanEnum virusScan) { + this.virusScan = virusScan; + return this; + } + + + @JsonProperty("virusScan") + public @Nullable VirusScanEnum getVirusScan() { + return virusScan; + } + + @JsonProperty("virusScan") + public void setVirusScan(@Nullable VirusScanEnum virusScan) { + this.virusScan = virusScan; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FileContent fileContent = (FileContent) o; + return Objects.equals(this.name, fileContent.name) && + Objects.equals(this.size, fileContent.size) && + Objects.equals(this.virusScan, fileContent.virusScan); + } + + @Override + public int hashCode() { + return Objects.hash(name, size, virusScan); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FileContent {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" size: ").append(toIndentedString(size)).append("\n"); + sb.append(" virusScan: ").append(toIndentedString(virusScan)).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) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + +} diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/Foo.java b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/Foo.java new file mode 100644 index 000000000000..da95803f0181 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/Foo.java @@ -0,0 +1,393 @@ +package org.openapitools.model; + +import java.io.File; +import java.math.BigDecimal; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.jspecify.annotations.Nullable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +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("Foo") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", comments = "Generator version: 7.26.0-SNAPSHOT") +public class Foo { + private @Nullable OffsetDateTime dt; + private @Nullable OffsetDateTime nullableDt; + private @Nullable File binary; + private @Nullable File nullableBinary; + private @Valid @Nullable List listOfDt = new ArrayList<>(); + private @Valid @Nullable List listMinIntems = new ArrayList<>(); + private @Valid @Nullable List nullableListMinIntems; + private OffsetDateTime requiredDt; + private java.math.@Nullable BigDecimal number; + private java.math.@Nullable BigDecimal nullableNumber; + private @Nullable String color = "red"; + private String requiredColor = "red"; + private @Nullable String nullableColor = "red"; + + public Foo() { + } + + @JsonCreator + public Foo( + @JsonProperty(required = true, value = "requiredDt") OffsetDateTime requiredDt, + @JsonProperty(required = true, value = "requiredColor") String requiredColor + ) { + this.requiredDt = requiredDt; + this.requiredColor = requiredColor; + } + + /** + **/ + public Foo dt(@Nullable OffsetDateTime dt) { + this.dt = dt; + return this; + } + + + @JsonProperty("dt") + public @Nullable OffsetDateTime getDt() { + return dt; + } + + @JsonProperty("dt") + public void setDt(@Nullable OffsetDateTime dt) { + this.dt = dt; + } + + /** + **/ + public Foo nullableDt(@Nullable OffsetDateTime nullableDt) { + this.nullableDt = nullableDt; + return this; + } + + + @JsonProperty("nullableDt") + public @Nullable OffsetDateTime getNullableDt() { + return nullableDt; + } + + @JsonProperty("nullableDt") + public void setNullableDt(@Nullable OffsetDateTime nullableDt) { + this.nullableDt = nullableDt; + } + + /** + **/ + public Foo binary(@Nullable File binary) { + this.binary = binary; + return this; + } + + + @JsonProperty("binary") + public @Nullable File getBinary() { + return binary; + } + + @JsonProperty("binary") + public void setBinary(@Nullable File binary) { + this.binary = binary; + } + + /** + **/ + public Foo nullableBinary(@Nullable File nullableBinary) { + this.nullableBinary = nullableBinary; + return this; + } + + + @JsonProperty("nullableBinary") + public @Nullable File getNullableBinary() { + return nullableBinary; + } + + @JsonProperty("nullableBinary") + public void setNullableBinary(@Nullable File nullableBinary) { + this.nullableBinary = nullableBinary; + } + + /** + **/ + public Foo listOfDt(@Nullable List listOfDt) { + this.listOfDt = listOfDt; + return this; + } + + + @JsonProperty("listOfDt") + public @Nullable List getListOfDt() { + return listOfDt; + } + + @JsonProperty("listOfDt") + public void setListOfDt(@Nullable List listOfDt) { + this.listOfDt = listOfDt; + } + + public Foo addListOfDtItem(OffsetDateTime listOfDtItem) { + if (this.listOfDt == null) { + this.listOfDt = new ArrayList<>(); + } + + this.listOfDt.add(listOfDtItem); + return this; + } + + public Foo removeListOfDtItem(OffsetDateTime listOfDtItem) { + if (listOfDtItem != null && this.listOfDt != null) { + this.listOfDt.remove(listOfDtItem); + } + + return this; + } + /** + **/ + public Foo listMinIntems(@Nullable List listMinIntems) { + this.listMinIntems = listMinIntems; + return this; + } + + + @JsonProperty("listMinIntems") + @Size(min=1)public @Nullable List getListMinIntems() { + return listMinIntems; + } + + @JsonProperty("listMinIntems") + public void setListMinIntems(@Nullable List listMinIntems) { + this.listMinIntems = listMinIntems; + } + + public Foo addListMinIntemsItem(OffsetDateTime listMinIntemsItem) { + if (this.listMinIntems == null) { + this.listMinIntems = new ArrayList<>(); + } + + this.listMinIntems.add(listMinIntemsItem); + return this; + } + + public Foo removeListMinIntemsItem(OffsetDateTime listMinIntemsItem) { + if (listMinIntemsItem != null && this.listMinIntems != null) { + this.listMinIntems.remove(listMinIntemsItem); + } + + return this; + } + /** + **/ + public Foo nullableListMinIntems(@Nullable List nullableListMinIntems) { + this.nullableListMinIntems = nullableListMinIntems; + return this; + } + + + @JsonProperty("nullableListMinIntems") + @Size(min=1)public @Nullable List getNullableListMinIntems() { + return nullableListMinIntems; + } + + @JsonProperty("nullableListMinIntems") + public void setNullableListMinIntems(@Nullable List nullableListMinIntems) { + this.nullableListMinIntems = nullableListMinIntems; + } + + public Foo addNullableListMinIntemsItem(OffsetDateTime nullableListMinIntemsItem) { + if (this.nullableListMinIntems == null) { + this.nullableListMinIntems = new ArrayList<>(); + } + + this.nullableListMinIntems.add(nullableListMinIntemsItem); + return this; + } + + public Foo removeNullableListMinIntemsItem(OffsetDateTime nullableListMinIntemsItem) { + if (nullableListMinIntemsItem != null && this.nullableListMinIntems != null) { + this.nullableListMinIntems.remove(nullableListMinIntemsItem); + } + + return this; + } + /** + **/ + public Foo requiredDt(OffsetDateTime requiredDt) { + this.requiredDt = requiredDt; + return this; + } + + + @JsonProperty(required = true, value = "requiredDt") + @NotNull public OffsetDateTime getRequiredDt() { + return requiredDt; + } + + @JsonProperty(required = true, value = "requiredDt") + public void setRequiredDt(OffsetDateTime requiredDt) { + this.requiredDt = requiredDt; + } + + /** + **/ + public Foo number(java.math.@Nullable BigDecimal number) { + this.number = number; + return this; + } + + + @JsonProperty("number") + @Valid public java.math.@Nullable BigDecimal getNumber() { + return number; + } + + @JsonProperty("number") + public void setNumber(java.math.@Nullable BigDecimal number) { + this.number = number; + } + + /** + **/ + public Foo nullableNumber(java.math.@Nullable BigDecimal nullableNumber) { + this.nullableNumber = nullableNumber; + return this; + } + + + @JsonProperty("nullableNumber") + @Valid public java.math.@Nullable BigDecimal getNullableNumber() { + return nullableNumber; + } + + @JsonProperty("nullableNumber") + public void setNullableNumber(java.math.@Nullable BigDecimal nullableNumber) { + this.nullableNumber = nullableNumber; + } + + /** + **/ + public Foo color(@Nullable String color) { + this.color = color; + return this; + } + + + @JsonProperty("color") + public @Nullable String getColor() { + return color; + } + + @JsonProperty("color") + public void setColor(@Nullable String color) { + this.color = color; + } + + /** + **/ + public Foo requiredColor(String requiredColor) { + this.requiredColor = requiredColor; + return this; + } + + + @JsonProperty(required = true, value = "requiredColor") + @NotNull public String getRequiredColor() { + return requiredColor; + } + + @JsonProperty(required = true, value = "requiredColor") + public void setRequiredColor(String requiredColor) { + this.requiredColor = requiredColor; + } + + /** + **/ + public Foo nullableColor(@Nullable String nullableColor) { + this.nullableColor = nullableColor; + return this; + } + + + @JsonProperty("nullableColor") + public @Nullable String getNullableColor() { + return nullableColor; + } + + @JsonProperty("nullableColor") + public void setNullableColor(@Nullable String nullableColor) { + this.nullableColor = nullableColor; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Foo foo = (Foo) o; + return Objects.equals(this.dt, foo.dt) && + Objects.equals(this.nullableDt, foo.nullableDt) && + Objects.equals(this.binary, foo.binary) && + Objects.equals(this.nullableBinary, foo.nullableBinary) && + Objects.equals(this.listOfDt, foo.listOfDt) && + Objects.equals(this.listMinIntems, foo.listMinIntems) && + Objects.equals(this.nullableListMinIntems, foo.nullableListMinIntems) && + Objects.equals(this.requiredDt, foo.requiredDt) && + Objects.equals(this.number, foo.number) && + Objects.equals(this.nullableNumber, foo.nullableNumber) && + Objects.equals(this.color, foo.color) && + Objects.equals(this.requiredColor, foo.requiredColor) && + Objects.equals(this.nullableColor, foo.nullableColor); + } + + @Override + public int hashCode() { + return Objects.hash(dt, nullableDt, binary, nullableBinary, listOfDt, listMinIntems, nullableListMinIntems, requiredDt, number, nullableNumber, color, requiredColor, nullableColor); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Foo {\n"); + + sb.append(" dt: ").append(toIndentedString(dt)).append("\n"); + sb.append(" nullableDt: ").append(toIndentedString(nullableDt)).append("\n"); + sb.append(" binary: ").append(toIndentedString(binary)).append("\n"); + sb.append(" nullableBinary: ").append(toIndentedString(nullableBinary)).append("\n"); + sb.append(" listOfDt: ").append(toIndentedString(listOfDt)).append("\n"); + sb.append(" listMinIntems: ").append(toIndentedString(listMinIntems)).append("\n"); + sb.append(" nullableListMinIntems: ").append(toIndentedString(nullableListMinIntems)).append("\n"); + sb.append(" requiredDt: ").append(toIndentedString(requiredDt)).append("\n"); + sb.append(" number: ").append(toIndentedString(number)).append("\n"); + sb.append(" nullableNumber: ").append(toIndentedString(nullableNumber)).append("\n"); + sb.append(" color: ").append(toIndentedString(color)).append("\n"); + sb.append(" requiredColor: ").append(toIndentedString(requiredColor)).append("\n"); + sb.append(" nullableColor: ").append(toIndentedString(nullableColor)).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) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + +} diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/RequiredAndNullable.java b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/RequiredAndNullable.java new file mode 100644 index 000000000000..7166c89b2d51 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/RequiredAndNullable.java @@ -0,0 +1,197 @@ +package org.openapitools.model; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.jspecify.annotations.Nullable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +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("RequiredAndNullable") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", comments = "Generator version: 7.26.0-SNAPSHOT") +public class RequiredAndNullable { + private @Nullable String str; + private @Nullable File _file; + private @Nullable String color = "red"; + private String onlyRequired; + private @Valid @Nullable List _list; + + public RequiredAndNullable() { + } + + @JsonCreator + public RequiredAndNullable( + @JsonProperty(required = true, value = "str") String str, + @JsonProperty(required = true, value = "file") File _file, + @JsonProperty(required = true, value = "color") String color, + @JsonProperty(required = true, value = "onlyRequired") String onlyRequired, + @JsonProperty(required = true, value = "list") List _list + ) { + this.str = str; + this._file = _file; + this.color = color; + this.onlyRequired = onlyRequired; + this._list = _list; + } + + /** + **/ + public RequiredAndNullable str(@Nullable String str) { + this.str = str; + return this; + } + + + @JsonProperty(required = true, value = "str") + @NotNull public @Nullable String getStr() { + return str; + } + + @JsonProperty(required = true, value = "str") + public void setStr(@Nullable String str) { + this.str = str; + } + + /** + **/ + public RequiredAndNullable _file(@Nullable File _file) { + this._file = _file; + return this; + } + + + @JsonProperty(required = true, value = "file") + @NotNull public @Nullable File getFile() { + return _file; + } + + @JsonProperty(required = true, value = "file") + public void setFile(@Nullable File _file) { + this._file = _file; + } + + /** + **/ + public RequiredAndNullable color(@Nullable String color) { + this.color = color; + return this; + } + + + @JsonProperty(required = true, value = "color") + @NotNull public @Nullable String getColor() { + return color; + } + + @JsonProperty(required = true, value = "color") + public void setColor(@Nullable String color) { + this.color = color; + } + + /** + **/ + public RequiredAndNullable onlyRequired(String onlyRequired) { + this.onlyRequired = onlyRequired; + return this; + } + + + @JsonProperty(required = true, value = "onlyRequired") + @NotNull public String getOnlyRequired() { + return onlyRequired; + } + + @JsonProperty(required = true, value = "onlyRequired") + public void setOnlyRequired(String onlyRequired) { + this.onlyRequired = onlyRequired; + } + + /** + **/ + public RequiredAndNullable _list(@Nullable List _list) { + this._list = _list; + return this; + } + + + @JsonProperty(required = true, value = "list") + @NotNull public @Nullable List getList() { + return _list; + } + + @JsonProperty(required = true, value = "list") + public void setList(@Nullable List _list) { + this._list = _list; + } + + public RequiredAndNullable addListItem(String _listItem) { + if (this._list == null) { + this._list = new ArrayList<>(); + } + + this._list.add(_listItem); + return this; + } + + public RequiredAndNullable removeListItem(String _listItem) { + if (_listItem != null && this._list != null) { + this._list.remove(_listItem); + } + + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RequiredAndNullable requiredAndNullable = (RequiredAndNullable) o; + return Objects.equals(this.str, requiredAndNullable.str) && + Objects.equals(this._file, requiredAndNullable._file) && + Objects.equals(this.color, requiredAndNullable.color) && + Objects.equals(this.onlyRequired, requiredAndNullable.onlyRequired) && + Objects.equals(this._list, requiredAndNullable._list); + } + + @Override + public int hashCode() { + return Objects.hash(str, _file, color, onlyRequired, _list); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class RequiredAndNullable {\n"); + + sb.append(" str: ").append(toIndentedString(str)).append("\n"); + sb.append(" _file: ").append(toIndentedString(_file)).append("\n"); + sb.append(" color: ").append(toIndentedString(color)).append("\n"); + sb.append(" onlyRequired: ").append(toIndentedString(onlyRequired)).append("\n"); + sb.append(" _list: ").append(toIndentedString(_list)).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) { + return o == null ? "null" : o.toString().replace("\n", "\n "); + } + + +} diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/package-info.java b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/package-info.java new file mode 100644 index 000000000000..d53d015a0286 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/gen/java/org/openapitools/model/package-info.java @@ -0,0 +1,2 @@ +@org.jspecify.annotations.NullMarked +package org.openapitools.model; diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/docker/Dockerfile.jvm b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/docker/Dockerfile.jvm new file mode 100644 index 000000000000..425165a2282b --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/docker/Dockerfile.jvm @@ -0,0 +1,34 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the docker image run: +# +# mvn package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/jaxrs-spec-quarkus-jspecify-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/jaxrs-spec-quarkus-jspecify-jvm +# +### +FROM fabric8/java-alpine-openjdk8-jre:1.6.5 +ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV AB_ENABLED=jmx_exporter + +# Be prepared for running in OpenShift too +RUN adduser -G root --no-create-home --disabled-password 1001 \ + && chown -R 1001 /deployments \ + && chmod -R "g+rwX" /deployments \ + && chown -R 1001:root /deployments + +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/app.jar +EXPOSE 8080 + +# run with user 1001 +USER 1001 + +ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/docker/Dockerfile.native b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/docker/Dockerfile.native new file mode 100644 index 000000000000..e89a329c735a --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/docker/Dockerfile.native @@ -0,0 +1,22 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode +# +# Before building the docker image run: +# +# mvn package -Pnative -Dquarkus.native.container-build=true +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/jaxrs-spec-quarkus-jspecify . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/jaxrs-spec-quarkus-jspecify +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal +WORKDIR /work/ +COPY target/*-runner /work/application +RUN chmod 775 /work +EXPOSE 8080 +CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/resources/META-INF/openapi.yaml b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/resources/META-INF/openapi.yaml new file mode 100644 index 000000000000..237bd96e1415 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/resources/META-INF/openapi.yaml @@ -0,0 +1,294 @@ +openapi: 3.0.0 +info: + description: test fully qualified name and jspecify + title: jspecify + version: 1.0.0 +servers: +- url: / +tags: +- description: requiredAndNullable + name: requiredAndNullable +- description: foo + name: foo +- description: upload + name: upload +- description: file + name: file +paths: + /foo/{dtParam}: + get: + parameters: + - explode: false + in: path + name: dtParam + required: false + schema: + format: date-time + type: string + style: simple + - explode: true + in: query + name: dtQuery + required: false + schema: + format: date-time + type: string + style: form + - explode: true + in: cookie + name: dtCookie + required: false + schema: + format: date-time + type: string + style: form + - explode: true + in: query + name: color + required: false + schema: + default: red + type: string + style: form + responses: + default: + content: + application/json: + schema: + $ref: "#/components/schemas/Foo" + description: response + tags: + - foo + x-accepts: + - application/json + x-tags: + - tag: foo + /requiredAndNullable: + post: + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/RequiredAndNullable" + description: bodyWithRequiredAndNullableAttributes + required: true + responses: + default: + content: + application/json: + schema: + $ref: "#/components/schemas/RequiredAndNullable" + description: response + tags: + - requiredAndNullable + x-content-type: application/json + x-accepts: + - application/json + x-tags: + - tag: requiredAndNullable + /upload: + post: + requestBody: + content: + multipart/form-data: + schema: + $ref: "#/components/schemas/_upload_post_request" + description: file + responses: + default: + description: ok + tags: + - upload + x-content-type: multipart/form-data + x-accepts: + - application/json + x-tags: + - tag: upload + /uploadFiles: + post: + requestBody: + content: + multipart/form-data: + schema: + $ref: "#/components/schemas/_uploadFiles_post_request" + description: file + responses: + default: + description: ok + tags: + - upload + x-content-type: multipart/form-data + x-accepts: + - application/json + x-tags: + - tag: upload + /file/{id}: + get: + parameters: + - explode: false + in: path + name: id + required: true + schema: + type: string + style: simple + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/FileContent" + description: ok + tags: + - file + x-accepts: + - application/json + x-tags: + - tag: file +components: + schemas: + Foo: + example: + dt: 2000-01-23T04:56:07.000+00:00 + nullableDt: 2000-01-23T04:56:07.000+00:00 + binary: "" + nullableBinary: "" + listOfDt: + - 2000-01-23T04:56:07.000+00:00 + - 2000-01-23T04:56:07.000+00:00 + listMinIntems: + - 2000-01-23T04:56:07.000+00:00 + - 2000-01-23T04:56:07.000+00:00 + nullableListMinIntems: + - 2000-01-23T04:56:07.000+00:00 + - 2000-01-23T04:56:07.000+00:00 + requiredDt: 2000-01-23T04:56:07.000+00:00 + number: 0.8008281904610115 + nullableNumber: 6.027456183070403 + color: red + requiredColor: red + nullableColor: red + properties: + dt: + format: date-time + type: string + nullableDt: + format: date-time + nullable: true + type: string + binary: + format: binary + type: string + nullableBinary: + format: binary + nullable: true + type: string + listOfDt: + items: + format: date-time + type: string + type: array + listMinIntems: + items: + format: date-time + type: string + minItems: 1 + type: array + nullableListMinIntems: + items: + format: date-time + type: string + minItems: 1 + nullable: true + type: array + requiredDt: + format: date-time + type: string + number: + type: number + nullableNumber: + nullable: true + type: number + color: + default: red + type: string + requiredColor: + default: red + type: string + nullableColor: + default: red + nullable: true + type: string + required: + - requiredColor + - requiredDt + RequiredAndNullable: + example: + str: str + file: "" + color: red + onlyRequired: onlyRequired + list: + - list + - list + properties: + str: + nullable: true + type: string + file: + format: binary + nullable: true + type: string + color: + default: red + nullable: true + type: string + onlyRequired: + type: string + list: + items: + type: string + nullable: true + type: array + required: + - color + - file + - list + - onlyRequired + - str + type: object + FileContent: + example: + name: name + size: 0 + virusScan: clean + properties: + name: + readOnly: true + type: string + size: + readOnly: true + type: integer + virusScan: + enum: + - clean + - detected + readOnly: true + type: string + required: + - name + type: object + _upload_post_request: + properties: + file: + format: binary + type: string + type: object + _uploadFiles_post_request: + properties: + file: + items: + format: binary + type: string + type: array + type: object diff --git a/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/resources/application.properties b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/resources/application.properties new file mode 100644 index 000000000000..83b16e96d391 --- /dev/null +++ b/samples/server/petstore/jaxrs-spec-quarkus-jspecify/src/main/resources/application.properties @@ -0,0 +1,5 @@ +# Configuration file +# key = value + +mp.openapi.scan.disable=true +