Skip to content

Commit 5f989c9

Browse files
committed
Generate jspecify test
1 parent a9d0883 commit 5f989c9

29 files changed

Lines changed: 3645 additions & 25 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
generatorName: spring
2+
outputDir: samples/openapi3/server/petstore/springboot-jspecify
3+
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
4+
templateDir: modules/openapi-generator/src/main/resources/JavaSpring
5+
additionalProperties:
6+
groupId: org.openapitools.openapi.jspecify
7+
documentationProvider: springdoc
8+
artifactId: springboot
9+
snapshotVersion: "true"
10+
useSpringBoot3: true
11+
useBeanValidation: true
12+
hideGenerationTimestamp: "true"
13+
generateConstructorWithAllArgs: true
14+
additionalModelTypeAnnotations: '@org.jspecify.annotations.NullMarked'
15+
importMappings:
16+
Nullable: org.jspecify.annotations.Nullable

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,7 @@ private Set<String> reformatProvideArgsParams(Operation operation) {
11531153
@RequiredArgsConstructor
11541154
static class ConstructorSpecs {
11551155

1156+
static final String NULL_UNMARKED = "@org.jspecify.annotations.NullUnmarked";
11561157
static final String JSON_CREATOR = "@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)";
11571158
private final String constructorAnnotation;
11581159
private final boolean useJsonProperty;
@@ -1167,7 +1168,7 @@ static ConstructorSpecs useJsonProperty(boolean useJsonProperty) {
11671168
*
11681169
* TODO: make it configurable
11691170
*/
1170-
private boolean isUseJspecify() {
1171+
public boolean isUseJspecify() {
11711172
return importMapping.getOrDefault("Nullable", "")
11721173
.contains("jspecify");
11731174
}
@@ -1178,41 +1179,34 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
11781179

11791180
Map<String, CodegenModel> allModels = getAllModels(objs);
11801181
// conditionally force the generation of no args constructor
1182+
11811183
for (CodegenModel cm : allModels.values()) {
11821184
boolean hasLombokNoArgsConstructor = lombokAnnotations != null && lombokAnnotations.containsKey("NoArgsConstructor");
11831185
boolean hasAllArgsConstructor = cm.vendorExtensions.containsKey("x-java-all-args-constructor");
11841186

11851187
if (!hasLombokNoArgsConstructor
11861188
&& (cm.hasRequired || hasAllArgsConstructor)) {
11871189

1188-
String annotation = (cm.hasRequired && isUseJspecify())? "@org.jspecify.annotations.NullUnmarked": null;
1190+
String annotation = (cm.hasRequired && isUseJspecify())? ConstructorSpecs.NULL_UNMARKED: null;
11891191

11901192
ConstructorSpecs noArgSpec = new ConstructorSpecs(annotation, false);
11911193
cm.vendorExtensions.put("x-java-no-args-constructor", noArgSpec);
11921194
}
11931195

1196+
// improve the generation of required and all arguments constructor
1197+
boolean jsonPropertyAllowed = !this.withXml && jackson;
11941198
boolean requiredJsonProperty = false;
1195-
// add @JsonCreator on constructor
1196-
if (!this.withXml && jackson) {
1197-
if (hasAllArgsConstructor) {
1198-
// add @JsonCreator and @JsonProperty on the all argument constructor
1199-
cm.vendorExtensions.put("x-java-all-args-constructor", ConstructorSpecs.useJsonProperty(true));
1200-
// List<CodegenProperty> properties = (List<CodegenProperty>)cm.vendorExtensions.get("x-java-all-args-constructor-vars");
1201-
// properties.forEach(p -> p.vendorExtensions.put("x-java-all-args-jsonProperty", p.baseName));
1202-
} else if (this.generatedConstructorWithRequiredArgs && cm.hasRequired && cm.requiredVars.size() == cm.allVars.size()) {
1203-
/* add @JsonCreator and @JsonProperty on the required argument constructor
1204-
* all fields are initialzed in the required constructor
1205-
* no all arg constructor
1206-
*/
1207-
requiredJsonProperty = true;
1208-
// cm.vendorExtensions.put("x-java-required-constructor-jsoncreator", true);
1209-
// cm.requiredVars.forEach(p -> p.vendorExtensions.put("x-java-required-args-jsonProperty", p.baseName));
1210-
}
1211-
if (generatedConstructorWithRequiredArgs && cm.hasRequired) {
1212-
cm.vendorExtensions.put("x-java-required-args-constructor", ConstructorSpecs.useJsonProperty(requiredJsonProperty));
1213-
}
1199+
if (hasAllArgsConstructor) {
1200+
// add @JsonCreator and @JsonProperty on the all arguments constructor
1201+
cm.vendorExtensions.put("x-java-all-args-constructor", ConstructorSpecs.useJsonProperty(jsonPropertyAllowed));
1202+
} else if (this.generatedConstructorWithRequiredArgs && cm.hasRequired && cm.requiredVars.size() == cm.allVars.size()) {
1203+
/* add @JsonCreator and @JsonProperty on the required argument constructor
1204+
*/
1205+
requiredJsonProperty = jsonPropertyAllowed;
1206+
}
1207+
if (generatedConstructorWithRequiredArgs && cm.hasRequired) {
1208+
cm.vendorExtensions.put("x-java-required-args-constructor", ConstructorSpecs.useJsonProperty(requiredJsonProperty));
12141209
}
1215-
12161210
}
12171211
return objs;
12181212
}

modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-boot/pom-sb3.mustache

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,13 @@
250250
<groupId>com.fasterxml.jackson.core</groupId>
251251
<artifactId>jackson-databind</artifactId>
252252
</dependency>
253+
{{#useJspecify}}
254+
<dependency>
255+
<groupId>org.jspecify</groupId>
256+
<artifactId>jspecify</artifactId>
257+
<version>1.0.0</version>
258+
</dependency>
259+
{{/useJspecify}}
253260
<dependency>
254261
<groupId>org.springframework.boot</groupId>
255262
<artifactId>spring-boot-starter-test</artifactId>

modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
9191
{{#vendorExtensions.x-java-no-args-constructor}}
9292

9393
{{#constructorAnnotation}}
94-
{{.}}
94+
{{{.}}}
9595
{{/constructorAnnotation}}
9696
public {{classname}}() {
9797
super();
@@ -105,7 +105,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
105105
* Constructor with only required parameters{{#generateConstructorWithAllArgs}}{{^vendorExtensions.x-java-all-args-constructor}} and all parameters{{/vendorExtensions.x-java-all-args-constructor}}{{/generateConstructorWithAllArgs}}
106106
*/
107107
{{#constructorAnnotation}}
108-
{{.}}
108+
{{{.}}}
109109
{{/constructorAnnotation}}
110110
public {{classname}}({{#requiredVars}}{{#useJsonProperty}}@JsonProperty("{{baseName}}") {{/useJsonProperty}}{{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}{{/requiredVars}}) {
111111
{{#parent}}
@@ -130,7 +130,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
130130
* Constructor with all args parameters
131131
*/
132132
{{#constructorAnnotation}}
133-
{{.}}
133+
{{{.}}}
134134
{{/constructorAnnotation}}
135135
public {{classname}}({{#vendorExtensions.x-java-all-args-constructor-vars}}{{#useJsonProperty}}@JsonProperty("{{baseName}}") {{/useJsonProperty}}{{>nullableAnnotation}}{{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-java-all-args-constructor-vars}}) {
136136
{{#parent}}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# OpenAPI Generator Ignore
2+
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
3+
4+
# Use this file to prevent files from being overwritten by the generator.
5+
# The patterns follow closely to .gitignore or .dockerignore.
6+
7+
# As an example, the C# client generator defines ApiClient.cs.
8+
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
9+
#ApiClient.cs
10+
11+
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
12+
#foo/*/qux
13+
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
14+
15+
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
16+
#foo/**/qux
17+
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
18+
19+
# You can also negate patterns with an exclamation (!).
20+
# For example, you can ignore all files in a docs folder with the file extension .md:
21+
#docs/*.md
22+
# Then explicitly reverse the ignore rule for a single file:
23+
#!docs/README.md
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
README.md
2+
pom.xml
3+
src/main/java/org/openapitools/OpenApiGeneratorApplication.java
4+
src/main/java/org/openapitools/RFC3339DateFormat.java
5+
src/main/java/org/openapitools/api/ApiUtil.java
6+
src/main/java/org/openapitools/api/PetApi.java
7+
src/main/java/org/openapitools/api/StoreApi.java
8+
src/main/java/org/openapitools/api/UserApi.java
9+
src/main/java/org/openapitools/configuration/HomeController.java
10+
src/main/java/org/openapitools/configuration/SpringDocConfiguration.java
11+
src/main/java/org/openapitools/model/Category.java
12+
src/main/java/org/openapitools/model/ModelApiResponse.java
13+
src/main/java/org/openapitools/model/Order.java
14+
src/main/java/org/openapitools/model/Pet.java
15+
src/main/java/org/openapitools/model/Tag.java
16+
src/main/java/org/openapitools/model/User.java
17+
src/main/resources/application.properties
18+
src/main/resources/openapi.yaml
19+
src/test/java/org/openapitools/OpenApiGeneratorApplicationTests.java
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
7.20.0-SNAPSHOT
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# OpenAPI generated server
2+
3+
Spring Boot Server
4+
5+
## Overview
6+
This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project.
7+
By using the [OpenAPI-Spec](https://openapis.org), you can easily generate a server stub.
8+
This is an example of building a OpenAPI-enabled server in Java using the SpringBoot framework.
9+
10+
11+
The underlying library integrating OpenAPI to Spring Boot is [springdoc](https://springdoc.org).
12+
Springdoc will generate an OpenAPI v3 specification based on the generated Controller and Model classes.
13+
The specification is available to download using the following url:
14+
http://localhost:8080/v3/api-docs/
15+
16+
Start your server as a simple java application
17+
18+
You can view the api documentation in swagger-ui by pointing to
19+
http://localhost:8080/swagger-ui.html
20+
21+
Change default port value in application.properties
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
2+
<modelVersion>4.0.0</modelVersion>
3+
<groupId>org.openapitools.openapi.jspecify</groupId>
4+
<artifactId>springboot</artifactId>
5+
<packaging>jar</packaging>
6+
<name>springboot</name>
7+
<version>1.0.0-SNAPSHOT</version>
8+
<properties>
9+
<java.version>17</java.version>
10+
<maven.compiler.source>${java.version}</maven.compiler.source>
11+
<maven.compiler.target>${java.version}</maven.compiler.target>
12+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13+
<springdoc.version>2.6.0</springdoc.version>
14+
<swagger-ui.version>5.17.14</swagger-ui.version>
15+
</properties>
16+
<parent>
17+
<groupId>org.springframework.boot</groupId>
18+
<artifactId>spring-boot-starter-parent</artifactId>
19+
<version>3.1.3</version>
20+
<relativePath/> <!-- lookup parent from repository -->
21+
</parent>
22+
23+
<repositories>
24+
<repository>
25+
<id>repository.spring.milestone</id>
26+
<name>Spring Milestone Repository</name>
27+
<url>https://repo.spring.io/milestone</url>
28+
</repository>
29+
</repositories>
30+
<pluginRepositories>
31+
<pluginRepository>
32+
<id>spring-milestones</id>
33+
<url>https://repo.spring.io/milestone</url>
34+
</pluginRepository>
35+
</pluginRepositories>
36+
37+
<build>
38+
<sourceDirectory>src/main/java</sourceDirectory>
39+
<plugins>
40+
<plugin>
41+
<groupId>org.springframework.boot</groupId>
42+
<artifactId>spring-boot-maven-plugin</artifactId>
43+
<configuration>
44+
</configuration>
45+
</plugin>
46+
</plugins>
47+
</build>
48+
<dependencies>
49+
<dependency>
50+
<groupId>org.springframework.boot</groupId>
51+
<artifactId>spring-boot-starter-web</artifactId>
52+
</dependency>
53+
<dependency>
54+
<groupId>org.springframework.data</groupId>
55+
<artifactId>spring-data-commons</artifactId>
56+
</dependency>
57+
<!--SpringDoc dependencies -->
58+
<dependency>
59+
<groupId>org.springdoc</groupId>
60+
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
61+
<version>${springdoc.version}</version>
62+
</dependency>
63+
<!-- @Nullable annotation -->
64+
<dependency>
65+
<groupId>com.google.code.findbugs</groupId>
66+
<artifactId>jsr305</artifactId>
67+
<version>3.0.2</version>
68+
</dependency>
69+
<dependency>
70+
<groupId>com.fasterxml.jackson.dataformat</groupId>
71+
<artifactId>jackson-dataformat-yaml</artifactId>
72+
</dependency>
73+
<dependency>
74+
<groupId>com.fasterxml.jackson.datatype</groupId>
75+
<artifactId>jackson-datatype-jsr310</artifactId>
76+
</dependency>
77+
<dependency>
78+
<groupId>org.openapitools</groupId>
79+
<artifactId>jackson-databind-nullable</artifactId>
80+
<version>0.2.8</version>
81+
</dependency>
82+
<!-- Bean Validation API support -->
83+
<dependency>
84+
<groupId>org.springframework.boot</groupId>
85+
<artifactId>spring-boot-starter-validation</artifactId>
86+
</dependency>
87+
<dependency>
88+
<groupId>com.fasterxml.jackson.core</groupId>
89+
<artifactId>jackson-databind</artifactId>
90+
</dependency>
91+
<dependency>
92+
<groupId>org.jspecify</groupId>
93+
<artifactId>jspecify</artifactId>
94+
<version>1.0.0</version>
95+
</dependency>
96+
<dependency>
97+
<groupId>org.springframework.boot</groupId>
98+
<artifactId>spring-boot-starter-test</artifactId>
99+
<scope>test</scope>
100+
</dependency>
101+
</dependencies>
102+
</project>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.openapitools;
2+
3+
import com.fasterxml.jackson.databind.Module;
4+
import org.openapitools.jackson.nullable.JsonNullableModule;
5+
import org.springframework.boot.SpringApplication;
6+
import org.springframework.boot.autoconfigure.SpringBootApplication;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.ComponentScan;
9+
import org.springframework.context.annotation.FilterType;
10+
import org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator;
11+
12+
@SpringBootApplication(
13+
nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class
14+
)
15+
@ComponentScan(
16+
basePackages = {"org.openapitools", "org.openapitools.api" , "org.openapitools.configuration"},
17+
nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class
18+
)
19+
public class OpenApiGeneratorApplication {
20+
21+
public static void main(String[] args) {
22+
SpringApplication.run(OpenApiGeneratorApplication.class, args);
23+
}
24+
25+
@Bean(name = "org.openapitools.OpenApiGeneratorApplication.jsonNullableModule")
26+
public Module jsonNullableModule() {
27+
return new JsonNullableModule();
28+
}
29+
30+
}

0 commit comments

Comments
 (0)