Skip to content

Commit 4e27041

Browse files
miguelborges99Nuno Borges
andauthored
Add microprofile OpenApi annotations to JavaRxSpec (quarkus library). Add OpenID support (core) (#15407)
* Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec * Issue #795: Add microprofile OpenAPI annotations for quarkus library in JaxRsSpec --------- Co-authored-by: Nuno Borges <Nuno.Borges@ctw.bmwgroup.com>
1 parent c251202 commit 4e27041

89 files changed

Lines changed: 14032 additions & 31 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/samples-jaxrs.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ jobs:
3838
- samples/server/petstore/jaxrs-cxf-annotated-base-path
3939
- samples/server/petstore/jaxrs-cxf-cdi
4040
- samples/server/petstore/jaxrs-cxf-non-spring-app
41+
- samples/server/petstore/jaxrs-spec-microprofile-openapi-annotations
4142
steps:
4243
- uses: actions/checkout@v3
4344
- uses: actions/setup-java@v3
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
generatorName: jaxrs-spec
2+
outputDir: samples/server/petstore/jaxrs-spec-microprofile-openapi-annotations
3+
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml
4+
templateDir: modules/openapi-generator/src/main/resources/JavaJaxRS/spec
5+
additionalProperties:
6+
artifactId: jaxrs-spec-petstore-server
7+
serializableModel: "true"
8+
hideGenerationTimestamp: "true"
9+
implicitHeadersRegex: (api_key|enum_header_string)
10+
generateBuilders: "true"
11+
useMicroProfileOpenAPIAnnotations: "true"
12+
library: "quarkus"
13+
dateLibrary: "java8-localdatetime"

docs/generators/jaxrs-cxf-cdi.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
7878
|title|a title describing the application| |OpenAPI Server|
7979
|useBeanValidation|Use BeanValidation API annotations| |true|
8080
|useJakartaEe|whether to use Jakarta EE namespace instead of javax| |false|
81+
|useMicroProfileOpenAPIAnnotations|Whether to generate Microprofile OpenAPI annotations. Only valid when library is set to quarkus.| |false|
8182
|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|
8283
|useSwaggerAnnotations|Whether to generate Swagger annotations.| |true|
8384
|useTags|use tags for creating interface and controller classnames| |false|

docs/generators/jaxrs-spec.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
7878
|title|a title describing the application| |OpenAPI Server|
7979
|useBeanValidation|Use BeanValidation API annotations| |true|
8080
|useJakartaEe|whether to use Jakarta EE namespace instead of javax| |false|
81+
|useMicroProfileOpenAPIAnnotations|Whether to generate Microprofile OpenAPI annotations. Only valid when library is set to quarkus.| |false|
8182
|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|
8283
|useSwaggerAnnotations|Whether to generate Swagger annotations.| |true|
8384
|useTags|use tags for creating interface and controller classnames| |false|

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ public void setIsBooleanSchemaFalse(boolean isBooleanSchemaFalse) {
259259
this.isBooleanSchemaFalse = isBooleanSchemaFalse;
260260
}
261261

262+
public String getOpenApiType() { return openApiType; }
263+
262264
public String getBaseName() {
263265
return baseName;
264266
}

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@
2525

2626
public class CodegenSecurity {
2727
public String name;
28+
public String description;
2829
public String type;
2930
public String scheme;
30-
public Boolean isBasic, isOAuth, isApiKey;
31+
public Boolean isBasic, isOAuth, isApiKey, isOpenId;
3132
// is Basic is true for all http authentication type.
3233
// Those are to differentiate basic and bearer authentication
3334
// isHttpSignature is to support HTTP signature authorization scheme.
@@ -42,12 +43,15 @@ public class CodegenSecurity {
4243
public String flow, authorizationUrl, tokenUrl, refreshUrl;
4344
public List<Map<String, Object>> scopes;
4445
public Boolean isCode, isPassword, isApplication, isImplicit;
46+
// OpenId specific
47+
public String openIdConnectUrl;
4548

4649
// Return a copy of the security object, filtering out any scopes from the passed-in list.
4750
public CodegenSecurity filterByScopeNames(List<String> filterScopes) {
4851
CodegenSecurity filteredSecurity = new CodegenSecurity();
4952
// Copy all fields except the scopes.
5053
filteredSecurity.name = name;
54+
filteredSecurity.description = description;
5155
filteredSecurity.type = type;
5256
filteredSecurity.isBasic = isBasic;
5357
filteredSecurity.isBasicBasic = isBasicBasic;
@@ -67,6 +71,7 @@ public CodegenSecurity filterByScopeNames(List<String> filterScopes) {
6771
filteredSecurity.tokenUrl = tokenUrl;
6872
filteredSecurity.authorizationUrl = authorizationUrl;
6973
filteredSecurity.refreshUrl = refreshUrl;
74+
filteredSecurity.openIdConnectUrl = openIdConnectUrl;
7075
// It is not possible to deep copy the extensions, as we have no idea what types they are.
7176
// So the filtered method *will* refer to the original extensions, if any.
7277
filteredSecurity.vendorExtensions = new HashMap<String, Object>(vendorExtensions);
@@ -93,6 +98,7 @@ public boolean equals(Object o) {
9398
if (o == null || getClass() != o.getClass()) return false;
9499
CodegenSecurity that = (CodegenSecurity) o;
95100
return Objects.equals(name, that.name) &&
101+
Objects.equals(description, that.description) &&
96102
Objects.equals(type, that.type) &&
97103
Objects.equals(scheme, that.scheme) &&
98104
Objects.equals(isBasic, that.isBasic) &&
@@ -115,22 +121,25 @@ public boolean equals(Object o) {
115121
Objects.equals(isCode, that.isCode) &&
116122
Objects.equals(isPassword, that.isPassword) &&
117123
Objects.equals(isApplication, that.isApplication) &&
118-
Objects.equals(isImplicit, that.isImplicit);
124+
Objects.equals(isImplicit, that.isImplicit) &&
125+
Objects.equals(openIdConnectUrl, that.openIdConnectUrl);
119126
}
120127

121128
@Override
122129
public int hashCode() {
123130

124-
return Objects.hash(name, type, scheme, isBasic, isOAuth, isApiKey,
131+
return Objects.hash(name, description, type, scheme, isBasic, isOAuth, isApiKey,
125132
isBasicBasic, isHttpSignature, isBasicBearer, bearerFormat, vendorExtensions,
126133
keyParamName, isKeyInQuery, isKeyInHeader, isKeyInCookie, flow,
127-
authorizationUrl, tokenUrl, refreshUrl, scopes, isCode, isPassword, isApplication, isImplicit);
134+
authorizationUrl, tokenUrl, refreshUrl, scopes, isCode, isPassword, isApplication, isImplicit,
135+
openIdConnectUrl);
128136
}
129137

130138
@Override
131139
public String toString() {
132140
final StringBuffer sb = new StringBuffer("CodegenSecurity{");
133141
sb.append("name='").append(name).append('\'');
142+
sb.append("description='").append(description).append('\'');
134143
sb.append(", type='").append(type).append('\'');
135144
sb.append(", scheme='").append(scheme).append('\'');
136145
sb.append(", isBasic=").append(isBasic);
@@ -154,6 +163,7 @@ public String toString() {
154163
sb.append(", isPassword=").append(isPassword);
155164
sb.append(", isApplication=").append(isApplication);
156165
sb.append(", isImplicit=").append(isImplicit);
166+
sb.append(", openIdConnectUrl=").append(openIdConnectUrl);
157167
sb.append('}');
158168
return sb.toString();
159169
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5321,6 +5321,11 @@ public List<CodegenSecurity> fromSecurity(Map<String, SecurityScheme> securitySc
53215321
once(LOGGER).warn("Unknown scheme `{}` found in the HTTP security definition.", securityScheme.getScheme());
53225322
}
53235323
codegenSecurities.add(cs);
5324+
} else if (SecurityScheme.Type.OPENIDCONNECT.equals(securityScheme.getType())) {
5325+
final CodegenSecurity cs = defaultCodegenSecurity(key, securityScheme);
5326+
cs.isOpenId = true;
5327+
cs.openIdConnectUrl = securityScheme.getOpenIdConnectUrl();
5328+
codegenSecurities.add(cs);
53245329
} else if (SecurityScheme.Type.OAUTH2.equals(securityScheme.getType())) {
53255330
final OAuthFlows flows = securityScheme.getFlows();
53265331
boolean isFlowEmpty = true;
@@ -5374,8 +5379,9 @@ public List<CodegenSecurity> fromSecurity(Map<String, SecurityScheme> securitySc
53745379
private CodegenSecurity defaultCodegenSecurity(String key, SecurityScheme securityScheme) {
53755380
final CodegenSecurity cs = CodegenModelFactory.newInstance(CodegenModelType.SECURITY);
53765381
cs.name = key;
5382+
cs.description = securityScheme.getDescription();
53775383
cs.type = securityScheme.getType().toString();
5378-
cs.isCode = cs.isPassword = cs.isApplication = cs.isImplicit = false;
5384+
cs.isCode = cs.isPassword = cs.isApplication = cs.isImplicit = cs.isOpenId = false;
53795385
cs.isHttpSignature = false;
53805386
cs.isBasicBasic = cs.isBasicBearer = false;
53815387
cs.scheme = securityScheme.getScheme();

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,20 @@ void generateApis(List<File> files, List<OperationsMap> allOperations, List<Mode
608608
operation.put("basePathWithoutHost", removeTrailingSlash(config.encodePath(url.getPath())));
609609
operation.put("contextPath", contextPath);
610610
operation.put("baseName", tag);
611+
Optional.ofNullable(openAPI.getTags()).orElseGet(Collections::emptyList).stream()
612+
.map(Tag::getName)
613+
.filter(Objects::nonNull)
614+
.filter(tag::equalsIgnoreCase)
615+
.findFirst()
616+
.ifPresent(tagName -> operation.put("operationTagName", config.escapeText(tagName)));
617+
operation.put("operationTagDescription", "");
618+
Optional.ofNullable(openAPI.getTags()).orElseGet(Collections::emptyList).stream()
619+
.filter(t -> tag.equalsIgnoreCase(t.getName()))
620+
.map(Tag::getDescription)
621+
.filter(Objects::nonNull)
622+
.findFirst()
623+
.ifPresent(description -> operation.put("operationTagDescription", config.escapeText(description)));
624+
Optional.ofNullable(config.additionalProperties().get("appVersion")).ifPresent(version -> operation.put("version", version));
611625
operation.put("apiPackage", config.apiPackage());
612626
operation.put("modelPackage", config.modelPackage());
613627
operation.putAll(config.additionalProperties());
@@ -616,6 +630,8 @@ void generateApis(List<File> files, List<OperationsMap> allOperations, List<Mode
616630
operation.put("importPath", config.toApiImport(tag));
617631
operation.put("classFilename", config.toApiFilename(tag));
618632
operation.put("strictSpecBehavior", config.isStrictSpecBehavior());
633+
Optional.ofNullable(openAPI.getInfo()).map(Info::getLicense).ifPresent(license -> operation.put("license", license));
634+
Optional.ofNullable(openAPI.getInfo()).map(Info::getContact).ifPresent(contact -> operation.put("contact", contact));
619635

620636
if (allModels == null || allModels.isEmpty()) {
621637
operation.put("hasModel", false);

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,22 @@ public abstract class AbstractJavaJAXRSServerCodegen extends AbstractJavaCodegen
4444
* Mustache template for the JAX-RS Codegen.
4545
*/
4646
protected static final String JAXRS_TEMPLATE_DIRECTORY_NAME = "JavaJaxRS";
47+
protected static final String X_MICROPROFILE_OPEN_API_RETURN_SCHEMA_CONTAINER = "x-microprofile-open-api-return-schema-container";
48+
protected static final String X_MICROPROFILE_OPEN_API_RETURN_UNIQUE_ITEMS = "x-microprofile-open-api-return-unique-items";
49+
protected static final String X_MICROPROFILE_OPEN_API_SCHEMA_TYPE = "x-microprofile-open-api-schema-type";
50+
protected static final String SCHEMA_TYPE_ARRAY = "org.eclipse.microprofile.openapi.annotations.enums.SchemaType.ARRAY";
51+
protected static final Map<String,String> ARRAY_OF_MICROPROFILE_OPEN_API_SCHEMA_TYPES;
52+
static {
53+
final Map<String, String> schemaTypes = new HashMap<>();
54+
schemaTypes.put("integer", "org.eclipse.microprofile.openapi.annotations.enums.SchemaType.INTEGER");
55+
schemaTypes.put("number", "org.eclipse.microprofile.openapi.annotations.enums.SchemaType.NUMBER");
56+
schemaTypes.put("boolean", "org.eclipse.microprofile.openapi.annotations.enums.SchemaType.BOOLEAN");
57+
schemaTypes.put("string", "org.eclipse.microprofile.openapi.annotations.enums.SchemaType.STRING");
58+
schemaTypes.put("object", "org.eclipse.microprofile.openapi.annotations.enums.SchemaType.OBJECT");
59+
schemaTypes.put("array", "org.eclipse.microprofile.openapi.annotations.enums.SchemaType.ARRAY");
60+
ARRAY_OF_MICROPROFILE_OPEN_API_SCHEMA_TYPES = Collections.unmodifiableMap(schemaTypes);
61+
}
62+
4763
protected String implFolder = "src/main/java";
4864
protected String testResourcesFolder = "src/test/resources";
4965
protected String title = "OpenAPI Server";
@@ -233,11 +249,18 @@ static OperationsMap jaxrsPostProcessOperations(OperationsMap objs) {
233249

234250
if ("array".equals(resp.containerType)) {
235251
resp.containerType = "List";
252+
resp.vendorExtensions.put(X_MICROPROFILE_OPEN_API_RETURN_SCHEMA_CONTAINER, SCHEMA_TYPE_ARRAY);
236253
} else if ("set".equals(resp.containerType)) {
237254
resp.containerType = "Set";
255+
resp.vendorExtensions.put(X_MICROPROFILE_OPEN_API_RETURN_SCHEMA_CONTAINER, SCHEMA_TYPE_ARRAY);
256+
resp.vendorExtensions.put(X_MICROPROFILE_OPEN_API_RETURN_UNIQUE_ITEMS, true);
238257
} else if ("map".equals(resp.containerType)) {
239258
resp.containerType = "Map";
240259
}
260+
261+
if (resp.getResponseHeaders() != null) {
262+
handleHeaders(resp.getResponseHeaders());
263+
}
241264
}
242265
}
243266

@@ -271,6 +294,17 @@ static OperationsMap jaxrsPostProcessOperations(OperationsMap objs) {
271294
return objs;
272295
}
273296

297+
private static void handleHeaders(List<CodegenParameter> headers) {
298+
for (CodegenParameter header : headers) {
299+
if (header.getSchema() != null && header.getSchema().getOpenApiType() != null) {
300+
final String schemaType = ARRAY_OF_MICROPROFILE_OPEN_API_SCHEMA_TYPES.get(header.getSchema().getOpenApiType());
301+
if (schemaType != null) {
302+
header.vendorExtensions.put(X_MICROPROFILE_OPEN_API_SCHEMA_TYPE, schemaType);
303+
}
304+
}
305+
}
306+
}
307+
274308
@Override
275309
public String toApiName(final String name) {
276310
String computed = name;

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public class JavaJAXRSSpecServerCodegen extends AbstractJavaJAXRSServerCodegen {
3333
public static final String RETURN_RESPONSE = "returnResponse";
3434
public static final String GENERATE_POM = "generatePom";
3535
public static final String USE_SWAGGER_ANNOTATIONS = "useSwaggerAnnotations";
36+
public static final String USE_MICROPROFILE_OPENAPI_ANNOTATIONS = "useMicroProfileOpenAPIAnnotations";
3637
public static final String OPEN_API_SPEC_FILE_LOCATION = "openApiSpecFileLocation";
3738
public static final String GENERATE_BUILDERS = "generateBuilders";
3839

@@ -47,6 +48,7 @@ public class JavaJAXRSSpecServerCodegen extends AbstractJavaJAXRSServerCodegen {
4748
private boolean generatePom = true;
4849
private boolean generateBuilders = false;
4950
private boolean useSwaggerAnnotations = true;
51+
private boolean useMicroProfileOpenAPIAnnotations = false;
5052

5153
protected boolean useGzipFeature = false;
5254
private boolean useJackson = false;
@@ -105,6 +107,7 @@ public JavaJAXRSSpecServerCodegen() {
105107
cliOptions.add(CliOption.newBoolean(INTERFACE_ONLY, "Whether to generate only API interface stubs without the server files.").defaultValue(String.valueOf(interfaceOnly)));
106108
cliOptions.add(CliOption.newBoolean(RETURN_RESPONSE, "Whether generate API interface should return javax.ws.rs.core.Response instead of a deserialized entity. Only useful if interfaceOnly is true.").defaultValue(String.valueOf(returnResponse)));
107109
cliOptions.add(CliOption.newBoolean(USE_SWAGGER_ANNOTATIONS, "Whether to generate Swagger annotations.", useSwaggerAnnotations));
110+
cliOptions.add(CliOption.newBoolean(USE_MICROPROFILE_OPENAPI_ANNOTATIONS, "Whether to generate Microprofile OpenAPI annotations. Only valid when library is set to quarkus.", useMicroProfileOpenAPIAnnotations));
108111
cliOptions.add(CliOption.newString(OPEN_API_SPEC_FILE_LOCATION, "Location where the file containing the spec will be generated in the output folder. No file generated when set to null or empty string."));
109112
cliOptions.add(CliOption.newBoolean(SUPPORT_ASYNC, "Wrap responses in CompletionStage type, allowing asynchronous computation (requires JAX-RS 2.1).", supportAsync));
110113
}
@@ -147,6 +150,14 @@ public void processOpts() {
147150
}
148151
writePropertyBack(USE_SWAGGER_ANNOTATIONS, useSwaggerAnnotations);
149152

153+
if (QUARKUS_LIBRARY.equals(library)) {
154+
if (additionalProperties.containsKey(USE_MICROPROFILE_OPENAPI_ANNOTATIONS)) {
155+
useMicroProfileOpenAPIAnnotations = Boolean.parseBoolean(additionalProperties.get(USE_MICROPROFILE_OPENAPI_ANNOTATIONS).toString());
156+
}
157+
writePropertyBack(USE_MICROPROFILE_OPENAPI_ANNOTATIONS, useMicroProfileOpenAPIAnnotations);
158+
}
159+
160+
150161
if (additionalProperties.containsKey(GENERATE_BUILDERS)) {
151162
generateBuilders = Boolean.parseBoolean(additionalProperties.get(GENERATE_BUILDERS).toString());
152163
}

0 commit comments

Comments
 (0)