Skip to content

Commit e5d40f4

Browse files
authored
[java-spring] - add 'includeHttpRequestContext' additional property defaulting to "true" for reactive and "false" for blocking to include ServerWebExchange/HttpServletRequest (#22910)
* add includeHttpRequestContext additional property defaulting to "true" for reactive and "false" for blocking. Implement tests and add samples to check compilation success. * generate sample files * fix template * fix template * fix template * update docs * fix imports * fix codegen * fix codegen and update samples * improve unit tests * generate also delegate * update samples * update samples * updated files
1 parent e438e15 commit e5d40f4

135 files changed

Lines changed: 18715 additions & 438 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-jdk17.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ on:
1414
- samples/client/petstore/java/microprofile-rest-client-outer-enum/**
1515
# servers
1616
- samples/openapi3/server/petstore/springboot-3/**
17+
- samples/openapi3/server/petstore/springboot-3-include-http-request-context/**
1718
- samples/server/petstore/springboot-x-implements-skip/**
1819
- samples/server/petstore/java-camel/**
1920
- samples/server/petstore/java-helidon-server/v3/mp/**
@@ -32,6 +33,7 @@ on:
3233
- samples/client/petstore/java/microprofile-rest-client-outer-enum/**
3334
# servers
3435
- samples/openapi3/server/petstore/springboot-3/**
36+
- samples/openapi3/server/petstore/springboot-3-include-http-request-context/**
3537
- samples/server/petstore/springboot-x-implements-skip/**
3638
- samples/server/petstore/java-camel/**
3739
- samples/server/petstore/java-helidon-server/v3/mp/**
@@ -56,6 +58,7 @@ jobs:
5658
- samples/client/petstore/java/microprofile-rest-client-outer-enum
5759
# servers
5860
- samples/openapi3/server/petstore/springboot-3
61+
- samples/openapi3/server/petstore/springboot-3-include-http-request-context
5962
- samples/server/petstore/springboot-x-implements-skip
6063
- samples/server/petstore/java-camel/
6164
- samples/server/petstore/java-helidon-server/v3/mp/

.github/workflows/samples-spring.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
- samples/server/petstore/spring-boot-nullable-set
4545
- samples/server/petstore/spring-boot-defaultInterface-unhandledExcp
4646
- samples/server/petstore/springboot
47+
- samples/server/petstore/springboot-include-http-request-context
4748
- samples/server/petstore/springboot-beanvalidation
4849
- samples/server/petstore/springboot-builtin-validation
4950
- samples/server/petstore/springboot-delegate
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
generatorName: spring
2+
outputDir: samples/openapi3/server/petstore/springboot-3-include-http-request-context
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.openapi3
7+
documentationProvider: springdoc
8+
artifactId: springboot
9+
snapshotVersion: "true"
10+
useSpringBoot3: true
11+
useBeanValidation: true
12+
withXml: true
13+
hideGenerationTimestamp: "true"
14+
generateConstructorWithAllArgs: true
15+
generateBuilders: true
16+
includeHttpRequestContext: "true"
17+
delegatePattern: true
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
generatorName: spring
2+
outputDir: samples/server/petstore/springboot-include-http-request-context
3+
inputSpec: modules/openapi-generator/src/test/resources/3_0/spring/petstore-with-fake-endpoints-models-for-testing.yaml
4+
templateDir: modules/openapi-generator/src/main/resources/JavaSpring
5+
additionalProperties:
6+
documentationProvider: springfox
7+
artifactId: springboot
8+
snapshotVersion: "true"
9+
hideGenerationTimestamp: "true"
10+
camelCaseDollarSign: "true"
11+
modelNameSuffix: 'Dto'
12+
includeHttpRequestContext: "true"

bin/configs/spring-boot-reactive-noResponseEntity.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ additionalProperties:
1010
hideGenerationTimestamp: "true"
1111
delegatePattern: "true"
1212
useResponseEntity: "false"
13+
includeHttpRequestContext: "false"

docs/generators/java-camel.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
6868
|ignoreAnyOfInEnum|Ignore anyOf keyword in enum| |false|
6969
|implicitHeaders|Skip header parameters in the generated API methods using @ApiImplicitParams annotation.| |false|
7070
|implicitHeadersRegex|Skip header parameters that matches given regex in the generated API methods using @ApiImplicitParams annotation. Note: this parameter is ignored when implicitHeaders=true| |null|
71+
|includeHttpRequestContext|Whether to include HttpServletRequest (blocking) or ServerWebExchange (reactive) as additional parameter in generated methods. Defaults to 'true' for reactive and 'false' for blocking.| |true (reactive) / false (blocking)|
7172
|interfaceOnly|Whether to generate only API interface stubs without the server files.| |false|
7273
|invokerPackage|root package for generated code| |org.openapitools.api|
7374
|legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C# have this enabled by default).|<dl><dt>**true**</dt><dd>The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.</dd><dt>**false**</dt><dd>The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.</dd></dl>|false|

docs/generators/spring.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
6161
|ignoreAnyOfInEnum|Ignore anyOf keyword in enum| |false|
6262
|implicitHeaders|Skip header parameters in the generated API methods using @ApiImplicitParams annotation.| |false|
6363
|implicitHeadersRegex|Skip header parameters that matches given regex in the generated API methods using @ApiImplicitParams annotation. Note: this parameter is ignored when implicitHeaders=true| |null|
64+
|includeHttpRequestContext|Whether to include HttpServletRequest (blocking) or ServerWebExchange (reactive) as additional parameter in generated methods. Defaults to 'true' for reactive and 'false' for blocking.| |true (reactive) / false (blocking)|
6465
|interfaceOnly|Whether to generate only API interface stubs without the server files.| |false|
6566
|invokerPackage|root package for generated code| |org.openapitools.api|
6667
|legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C# have this enabled by default).|<dl><dt>**true**</dt><dd>The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.</dd><dt>**false**</dt><dd>The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.</dd></dl>|false|

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

Lines changed: 62 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import io.swagger.v3.oas.models.media.Schema;
2626
import io.swagger.v3.oas.models.servers.Server;
2727
import io.swagger.v3.oas.models.tags.Tag;
28+
import io.swagger.v3.parser.util.SchemaTypeUtil;
2829
import lombok.Getter;
2930
import lombok.Setter;
3031
import org.apache.commons.lang3.StringUtils;
@@ -94,6 +95,7 @@ public class SpringCodegen extends AbstractJavaCodegen
9495
public static final String USE_ENUM_CASE_INSENSITIVE = "useEnumCaseInsensitive";
9596
public static final String USE_SPRING_BOOT3 = "useSpringBoot3";
9697
public static final String USE_SPRING_BOOT4 = "useSpringBoot4";
98+
public static final String INCLUDE_HTTP_REQUEST_CONTEXT = "includeHttpRequestContext";
9799
public static final String REQUEST_MAPPING_OPTION = "requestMappingMode";
98100
public static final String USE_REQUEST_MAPPING_ON_CONTROLLER = "useRequestMappingOnController";
99101
public static final String USE_REQUEST_MAPPING_ON_INTERFACE = "useRequestMappingOnInterface";
@@ -161,6 +163,12 @@ public enum RequestMappingMode {
161163
protected boolean useSpringBoot3 = false;
162164
@Getter @Setter
163165
protected boolean useSpringBoot4 = false;
166+
@Getter @Setter
167+
private Boolean includeHttpRequestContext = null;
168+
@Getter
169+
private final boolean defaultIncludeHttpRequestContextForReactive = true;
170+
@Getter
171+
private final boolean defaultIncludeHttpRequestContextForBlocking = false;
164172
protected boolean generatedConstructorWithRequiredArgs = true;
165173
@Getter @Setter
166174
protected RequestMappingMode requestMappingMode = RequestMappingMode.controller;
@@ -294,6 +302,9 @@ public SpringCodegen() {
294302
"Generate code and provide dependencies for use with Spring Boot 4.x. (Use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.",
295303
useSpringBoot4));
296304
cliOptions.add(CliOption.newBoolean(USE_JACKSON_3, "Set it in order to use jackson 3 dependencies (only allowed when `" + USE_SPRING_BOOT4 + "` is set and incompatible with `"+OPENAPI_NULLABLE+"`).", useJackson3));
305+
cliOptions.add(new CliOption(INCLUDE_HTTP_REQUEST_CONTEXT,
306+
"Whether to include HttpServletRequest (blocking) or ServerWebExchange (reactive) as additional parameter in generated methods. Defaults to 'true' for reactive and 'false' for blocking.",
307+
SchemaTypeUtil.BOOLEAN_TYPE).defaultValue("true (reactive) / false (blocking)"));
297308
cliOptions.add(CliOption.newBoolean(GENERATE_CONSTRUCTOR_WITH_REQUIRED_ARGS,
298309
"Whether to generate constructors with required args for models",
299310
generatedConstructorWithRequiredArgs));
@@ -445,6 +456,22 @@ public void processOpts() {
445456
convertPropertyToBooleanAndWriteBack(REACTIVE, this::setReactive);
446457
convertPropertyToBooleanAndWriteBack(SSE, this::setSse);
447458
}
459+
if (additionalProperties.containsKey(INCLUDE_HTTP_REQUEST_CONTEXT)) {
460+
convertPropertyToBooleanAndWriteBack(INCLUDE_HTTP_REQUEST_CONTEXT, this::setIncludeHttpRequestContext);
461+
}
462+
//set default value for includeHttpRequestContext based on reactive/blocking
463+
if (includeHttpRequestContext == null) {
464+
if (this.reactive) {
465+
//default to true for reactive
466+
this.setIncludeHttpRequestContext(this.isDefaultIncludeHttpRequestContextForReactive());
467+
LOGGER.info("Defaulting {} to '{}' for reactive", INCLUDE_HTTP_REQUEST_CONTEXT, this.isDefaultIncludeHttpRequestContextForReactive());
468+
} else {
469+
//default to false for blocking
470+
this.setIncludeHttpRequestContext(this.isDefaultIncludeHttpRequestContextForBlocking());
471+
LOGGER.info("Defaulting {} to '{}' for blocking", INCLUDE_HTTP_REQUEST_CONTEXT, this.isDefaultIncludeHttpRequestContextForBlocking());
472+
}
473+
additionalProperties.put(INCLUDE_HTTP_REQUEST_CONTEXT, this.getIncludeHttpRequestContext());
474+
}
448475

449476
convertPropertyToStringAndWriteBack(RESPONSE_WRAPPER, this::setResponseWrapper);
450477
convertPropertyToBooleanAndWriteBack(USE_TAGS, this::setUseTags);
@@ -1074,7 +1101,6 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
10741101
// add org.springframework.format.annotation.DateTimeFormat when needed
10751102
codegenOperation.allParams.stream().filter(p -> p.isDate || p.isDateTime).findFirst()
10761103
.ifPresent(p -> codegenOperation.imports.add("DateTimeFormat"));
1077-
10781104
// add org.springframework.data.domain.Pageable import when needed
10791105
if (codegenOperation.vendorExtensions.containsKey("x-spring-paginated")) {
10801106
codegenOperation.imports.add("Pageable");
@@ -1100,13 +1126,12 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
11001126

11011127
addSpringNullableImportForOperation(codegenOperation);
11021128

1103-
if (reactive) {
1104-
if (DocumentationProvider.SPRINGFOX.equals(getDocumentationProvider())) {
1105-
codegenOperation.imports.add("ApiIgnore");
1106-
}
1107-
if (sse) {
1108-
var MEDIA_EVENT_STREAM = "text/event-stream";
1109-
// inspecting used streaming media types
1129+
if (DocumentationProvider.SPRINGFOX.equals(getDocumentationProvider()) && includeHttpRequestContext != null && includeHttpRequestContext) {
1130+
codegenOperation.imports.add("ApiIgnore");
1131+
}
1132+
if (reactive && sse) {
1133+
var MEDIA_EVENT_STREAM = "text/event-stream";
1134+
// inspecting used streaming media types
11101135
/*
11111136
expected definition:
11121137
content:
@@ -1118,36 +1143,35 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
11181143
type: <type> or
11191144
$ref: <typeRef>
11201145
*/
1121-
Map<String, List<Schema>> schemaTypes = operation.getResponses().entrySet().stream()
1122-
.map(e -> Pair.of(e.getValue(), fromResponse(e.getKey(), e.getValue())))
1123-
.filter(p -> p.getRight().is2xx) // consider only success
1124-
.map(p -> p.getLeft().getContent().get(MEDIA_EVENT_STREAM))
1125-
.map(MediaType::getSchema)
1126-
.collect(Collectors.toList()).stream()
1127-
.collect(Collectors.groupingBy(Schema::getType));
1128-
if (schemaTypes.containsKey("array")) {
1129-
// we have a match with SSE pattern
1130-
// double check potential conflicting, multiple specs
1131-
if (schemaTypes.keySet().size() > 1) {
1132-
throw new RuntimeException("only 1 response media type supported, when SSE is detected");
1133-
}
1134-
// double check schema format
1135-
List<Schema> eventTypes = schemaTypes.get("array");
1136-
if (eventTypes.stream().anyMatch(schema -> !"event-stream".equalsIgnoreCase(schema.getFormat()))) {
1137-
throw new RuntimeException("schema format 'event-stream' is required, when SSE is detected");
1138-
}
1139-
// double check item types
1140-
Set<String> itemTypes = eventTypes.stream()
1141-
.map(schema -> schema.getItems().getType() != null
1142-
? schema.getItems().getType()
1143-
: schema.getItems().get$ref())
1144-
.collect(Collectors.toSet());
1145-
if (itemTypes.size() > 1) {
1146-
throw new RuntimeException("only single item type is supported, when SSE is detected");
1147-
}
1148-
codegenOperation.vendorExtensions.put("x-sse", true);
1149-
} // Not an SSE compliant definition
1150-
}
1146+
Map<String, List<Schema>> schemaTypes = operation.getResponses().entrySet().stream()
1147+
.map(e -> Pair.of(e.getValue(), fromResponse(e.getKey(), e.getValue())))
1148+
.filter(p -> p.getRight().is2xx) // consider only success
1149+
.map(p -> p.getLeft().getContent().get(MEDIA_EVENT_STREAM))
1150+
.map(MediaType::getSchema)
1151+
.collect(Collectors.toList()).stream()
1152+
.collect(Collectors.groupingBy(Schema::getType));
1153+
if (schemaTypes.containsKey("array")) {
1154+
// we have a match with SSE pattern
1155+
// double check potential conflicting, multiple specs
1156+
if (schemaTypes.keySet().size() > 1) {
1157+
throw new RuntimeException("only 1 response media type supported, when SSE is detected");
1158+
}
1159+
// double check schema format
1160+
List<Schema> eventTypes = schemaTypes.get("array");
1161+
if (eventTypes.stream().anyMatch(schema -> !"event-stream".equalsIgnoreCase(schema.getFormat()))) {
1162+
throw new RuntimeException("schema format 'event-stream' is required, when SSE is detected");
1163+
}
1164+
// double check item types
1165+
Set<String> itemTypes = eventTypes.stream()
1166+
.map(schema -> schema.getItems().getType() != null
1167+
? schema.getItems().getType()
1168+
: schema.getItems().get$ref())
1169+
.collect(Collectors.toSet());
1170+
if (itemTypes.size() > 1) {
1171+
throw new RuntimeException("only single item type is supported, when SSE is detected");
1172+
}
1173+
codegenOperation.vendorExtensions.put("x-sse", true);
1174+
} // Not an SSE compliant definition
11511175
}
11521176
return codegenOperation;
11531177
}

0 commit comments

Comments
 (0)