Skip to content

Commit b54299f

Browse files
Spring request mapping mode (#13838)
* Introduce RequestMappingMode option * generate docs * Add test case using interfaceOnly * Generate Samples * Add requestMappingMode: iface to bin/configs/spring-boot-oas3.yaml * Restore #12250: Move Feign Client url parameter under condition. * Rename iface to api_interface.
1 parent fe5601a commit b54299f

File tree

285 files changed

+215
-171
lines changed

Some content is hidden

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

285 files changed

+215
-171
lines changed

bin/configs/spring-boot-oas3.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ additionalProperties:
88
artifactId: springboot
99
snapshotVersion: "true"
1010
hideGenerationTimestamp: "true"
11+
requestMappingMode: api_interface

docs/generators/java-camel.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
7474
|performBeanValidation|Use Bean Validation Impl. to perform BeanValidation| |false|
7575
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
7676
|reactive|wrap responses in Mono/Flux Reactor types (spring-boot only)| |false|
77+
|requestMappingMode|Where to generate the class level @RequestMapping annotation.|<dl><dt>**api_interface**</dt><dd>Generate the @RequestMapping annotation on the generated Api Interface.</dd><dt>**controller**</dt><dd>Generate the @RequestMapping annotation on the generated Api Controller Implementation.</dd><dt>**none**</dt><dd>Do not add a class level @RequestMapping annotation.</dd></dl>|controller|
7778
|responseWrapper|wrap the responses in given type (Future, Callable, CompletableFuture,ListenableFuture, DeferredResult, RxObservable, RxSingle or fully qualified type)| |null|
7879
|returnSuccessCode|Generated server returns 2xx code| |false|
7980
|scmConnection|SCM connection in generated pom.xml| |scm:git:git@github.com:openapitools/openapi-generator.git|

docs/generators/spring.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
6767
|performBeanValidation|Use Bean Validation Impl. to perform BeanValidation| |false|
6868
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
6969
|reactive|wrap responses in Mono/Flux Reactor types (spring-boot only)| |false|
70+
|requestMappingMode|Where to generate the class level @RequestMapping annotation.|<dl><dt>**api_interface**</dt><dd>Generate the @RequestMapping annotation on the generated Api Interface.</dd><dt>**controller**</dt><dd>Generate the @RequestMapping annotation on the generated Api Controller Implementation.</dd><dt>**none**</dt><dd>Do not add a class level @RequestMapping annotation.</dd></dl>|controller|
7071
|responseWrapper|wrap the responses in given type (Future, Callable, CompletableFuture,ListenableFuture, DeferredResult, RxObservable, RxSingle or fully qualified type)| |null|
7172
|returnSuccessCode|Generated server returns 2xx code| |false|
7273
|scmConnection|SCM connection in generated pom.xml| |scm:git:git@github.com:openapitools/openapi-generator.git|

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

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import java.util.Set;
4444
import java.util.regex.Matcher;
4545
import java.util.stream.Collectors;
46-
import java.util.stream.Stream;
4746

4847
import org.apache.commons.lang3.tuple.Pair;
4948
import org.openapitools.codegen.CliOption;
@@ -107,6 +106,25 @@ public class SpringCodegen extends AbstractJavaCodegen
107106
public static final String UNHANDLED_EXCEPTION_HANDLING = "unhandledException";
108107
public static final String USE_SPRING_BOOT3 = "useSpringBoot3";
109108
public static final String USE_JAKARTA_EE = "useJakartaEe";
109+
public static final String REQUEST_MAPPING_OPTION = "requestMappingMode";
110+
public static final String USE_REQUEST_MAPPING_ON_CONTROLLER = "useRequestMappingOnController";
111+
public static final String USE_REQUEST_MAPPING_ON_INTERFACE = "useRequestMappingOnInterface";
112+
113+
public enum RequestMappingMode {
114+
api_interface("Generate the @RequestMapping annotation on the generated Api Interface."),
115+
controller("Generate the @RequestMapping annotation on the generated Api Controller Implementation."),
116+
none("Do not add a class level @RequestMapping annotation.");
117+
118+
public String getDescription() {
119+
return description;
120+
}
121+
122+
private String description;
123+
124+
RequestMappingMode(String description) {
125+
this.description = description;
126+
}
127+
}
110128

111129
public static final String OPEN_BRACE = "{";
112130
public static final String CLOSE_BRACE = "}";
@@ -116,7 +134,6 @@ public class SpringCodegen extends AbstractJavaCodegen
116134
protected String basePackage = "org.openapitools";
117135
protected boolean interfaceOnly = false;
118136
protected boolean useFeignClientUrl = true;
119-
protected boolean useFeignClient = false;
120137
protected boolean delegatePattern = false;
121138
protected boolean delegateMethod = false;
122139
protected boolean singleContentTypes = false;
@@ -136,6 +153,7 @@ public class SpringCodegen extends AbstractJavaCodegen
136153
protected boolean useSpringController = false;
137154
protected boolean useSwaggerUI = true;
138155
protected boolean useSpringBoot3 = false;
156+
protected RequestMappingMode requestMappingMode = RequestMappingMode.controller;
139157

140158
public SpringCodegen() {
141159
super();
@@ -208,6 +226,15 @@ public SpringCodegen() {
208226
cliOptions
209227
.add(CliOption.newBoolean(RETURN_SUCCESS_CODE, "Generated server returns 2xx code", returnSuccessCode));
210228
cliOptions.add(CliOption.newBoolean(SPRING_CONTROLLER, "Annotate the generated API as a Spring Controller", useSpringController));
229+
230+
CliOption requestMappingOpt = new CliOption(REQUEST_MAPPING_OPTION,
231+
"Where to generate the class level @RequestMapping annotation.")
232+
.defaultValue(requestMappingMode.name());
233+
for (RequestMappingMode mode: RequestMappingMode.values()) {
234+
requestMappingOpt.addEnum(mode.name(), mode.getDescription());
235+
}
236+
cliOptions.add(requestMappingOpt);
237+
211238
cliOptions.add(CliOption.newBoolean(UNHANDLED_EXCEPTION_HANDLING,
212239
"Declare operation methods to throw a generic exception and allow unhandled exceptions (useful for Spring `@ControllerAdvice` directives).",
213240
unhandledException));
@@ -304,6 +331,13 @@ public void processOpts() {
304331
LOGGER.info("Set base package to invoker package ({})", basePackage);
305332
}
306333

334+
if (additionalProperties.containsKey(REQUEST_MAPPING_OPTION)) {
335+
RequestMappingMode optValue = RequestMappingMode.valueOf(
336+
String.valueOf(additionalProperties.get(REQUEST_MAPPING_OPTION)));
337+
setRequestMappingMode(optValue);
338+
additionalProperties.remove(REQUEST_MAPPING_OPTION);
339+
}
340+
307341
useOneOfInterfaces = true;
308342
legacyDiscriminatorBehavior = false;
309343

@@ -498,8 +532,9 @@ public void processOpts() {
498532
additionalProperties.put(SINGLE_CONTENT_TYPES, "true");
499533
this.setSingleContentTypes(true);
500534
}
535+
// @RequestMapping not supported with spring cloud openfeign.
536+
setRequestMappingMode(RequestMappingMode.none);
501537
additionalProperties.put(USE_FEIGN_CLIENT, "true");
502-
this.setUseFeignClient(true);
503538
} else {
504539
apiTemplateFiles.put("apiController.mustache", "Controller.java");
505540
if (containsEnums()) {
@@ -582,6 +617,19 @@ public void processOpts() {
582617
}
583618
}
584619

620+
switch (getRequestMappingMode()) {
621+
case api_interface:
622+
additionalProperties.put(USE_REQUEST_MAPPING_ON_INTERFACE, true);
623+
break;
624+
case controller:
625+
additionalProperties.put(USE_REQUEST_MAPPING_ON_CONTROLLER, true);
626+
break;
627+
case none:
628+
additionalProperties.put(USE_REQUEST_MAPPING_ON_INTERFACE, false);
629+
additionalProperties.put(USE_REQUEST_MAPPING_ON_CONTROLLER, false);
630+
break;
631+
}
632+
585633
// add lambda for mustache templates
586634
additionalProperties.put("lambdaRemoveDoubleQuote", (Mustache.Lambda) (fragment, writer) -> writer
587635
.write(fragment.execute().replaceAll("\"", Matcher.quoteReplacement(""))));
@@ -873,10 +921,6 @@ public void setSingleContentTypes(boolean singleContentTypes) {
873921
this.singleContentTypes = singleContentTypes;
874922
}
875923

876-
public void setUseFeignClient( boolean useFeignClient ) {
877-
this.useFeignClient = useFeignClient;
878-
}
879-
880924
public void setSkipDefaultInterface(boolean skipDefaultInterface) {
881925
this.skipDefaultInterface = skipDefaultInterface;
882926
}
@@ -1121,4 +1165,12 @@ public boolean isUseSpringBoot3() {
11211165
public void setUseSpringBoot3(boolean useSpringBoot3) {
11221166
this.useSpringBoot3 = useSpringBoot3;
11231167
}
1168+
1169+
public RequestMappingMode getRequestMappingMode() {
1170+
return requestMappingMode;
1171+
}
1172+
1173+
public void setRequestMappingMode(RequestMappingMode requestMappingMode) {
1174+
this.requestMappingMode = requestMappingMode;
1175+
}
11241176
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,11 @@ import javax.annotation.Generated;
9696
{{#virtualService}}
9797
@VirtualService
9898
{{/virtualService}}
99-
{{^useFeignClient}}
99+
{{#useRequestMappingOnInterface}}
100100
{{=<% %>=}}
101101
@RequestMapping("${openapi.<%title%>.base-path:<%>defaultBasePath%>}")
102102
<%={{ }}=%>
103-
{{/useFeignClient}}
103+
{{/useRequestMappingOnInterface}}
104104
public interface {{classname}} {
105105
{{#jdk8-default-interface}}
106106
{{^isDelegate}}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ import javax.annotation.Generated;
5858

5959
{{>generatedAnnotation}}
6060
@Controller
61+
{{#useRequestMappingOnController}}
62+
{{=<% %>=}}
63+
@RequestMapping("${openapi.<%title%>.base-path:<%>defaultBasePath%>}")
64+
<%={{ }}=%>
65+
{{/useRequestMappingOnController}}
6166
{{#operations}}
6267
public class {{classname}}Controller implements {{classname}} {
6368
{{#isDelegate}}

modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-cloud/apiClient.mustache

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package {{package}};
33
import org.springframework.cloud.openfeign.FeignClient;
44
import {{configPackage}}.ClientConfiguration;
55

6-
{{=<% %>=}}
7-
@FeignClient(name="${<%classVarName%>.name:<%classVarName%>}", url="${<%classVarName%>.url:<%basePath%>}", configuration = ClientConfiguration.class)
8-
<%={{ }}=%>
6+
@FeignClient(name="${{openbrace}}{{classVarName}}.name:{{classVarName}}{{closebrace}}", {{#useFeignClientUrl}}url="${{openbrace}}{{classVarName}}.url:{{basePath}}{{closebrace}}", {{/useFeignClientUrl}}configuration = ClientConfiguration.class)
97
public interface {{classname}}Client extends {{classname}} {
108
}

modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import static org.assertj.core.api.Assertions.assertThat;
2222
import static org.openapitools.codegen.TestUtils.assertFileContains;
2323
import static org.openapitools.codegen.TestUtils.assertFileNotContains;
24+
import static org.openapitools.codegen.languages.SpringCodegen.INTERFACE_ONLY;
25+
import static org.openapitools.codegen.languages.SpringCodegen.REQUEST_MAPPING_OPTION;
2426
import static org.openapitools.codegen.languages.SpringCodegen.RESPONSE_WRAPPER;
2527
import static org.openapitools.codegen.languages.SpringCodegen.SPRING_BOOT;
2628
import static org.openapitools.codegen.languages.features.DocumentationProviderFeatures.DOCUMENTATION_PROVIDER;
@@ -44,7 +46,6 @@
4446
import java.util.function.Function;
4547
import java.util.stream.Collectors;
4648

47-
import org.assertj.core.api.Assertions;
4849
import org.openapitools.codegen.java.assertions.JavaFileAssert;
4950
import org.openapitools.codegen.CliOption;
5051
import org.openapitools.codegen.ClientOptInput;
@@ -107,10 +108,9 @@ public void doAnnotateDatesOnModelParameters() throws IOException {
107108

108109
JavaFileAssert.assertThat(Paths.get(outputPath + "/src/main/java/org/openapitools/api/ZebrasApi.java"))
109110
.assertTypeAnnotations()
110-
.hasSize(4)
111+
.hasSize(3)
111112
.containsWithName("Validated")
112113
.containsWithName("Generated")
113-
.containsWithName("RequestMapping")
114114
.containsWithNameAndAttributes("Generated", ImmutableMap.of(
115115
"value", "\"org.openapitools.codegen.languages.SpringCodegen\""
116116
))
@@ -661,6 +661,7 @@ public void testMultipartCloud() throws IOException {
661661
public void testRequestMappingAnnotation() throws IOException {
662662
final SpringCodegen codegen = new SpringCodegen();
663663
codegen.setLibrary("spring-boot");
664+
codegen.additionalProperties().put(REQUEST_MAPPING_OPTION, SpringCodegen.RequestMappingMode.api_interface);
664665

665666
final Map<String, File> files = generateFiles(codegen, "src/test/resources/2_0/petstore.yaml");
666667

@@ -674,7 +675,7 @@ public void testRequestMappingAnnotation() throws IOException {
674675
}
675676

676677
@Test
677-
public void testNoRequestMappingAnnotation() throws IOException {
678+
public void testNoRequestMappingAnnotation_spring_cloud_default() throws IOException {
678679
final SpringCodegen codegen = new SpringCodegen();
679680
codegen.setLibrary( "spring-cloud" );
680681

@@ -687,6 +688,21 @@ public void testNoRequestMappingAnnotation() throws IOException {
687688

688689
}
689690

691+
@Test
692+
public void testNoRequestMappingAnnotation() throws IOException {
693+
final SpringCodegen codegen = new SpringCodegen();
694+
codegen.setLibrary( "spring-cloud" );
695+
codegen.additionalProperties().put(INTERFACE_ONLY, "true");
696+
codegen.additionalProperties().put(REQUEST_MAPPING_OPTION, SpringCodegen.RequestMappingMode.none);
697+
698+
final Map<String, File> files = generateFiles( codegen, "src/test/resources/2_0/petstore.yaml" );
699+
700+
// Check that the @RequestMapping annotation is not generated in the Api file
701+
final File petApiFile = files.get( "PetApi.java" );
702+
JavaFileAssert.assertThat( petApiFile ).assertTypeAnnotations().hasSize( 3 ).containsWithName( "Validated" )
703+
.containsWithName( "Generated" ).containsWithName( "Tag" );
704+
}
705+
690706
@Test
691707
public void testSettersForConfigValues() throws Exception {
692708
final SpringCodegen codegen = new SpringCodegen();

samples/client/petstore/spring-cloud-date-time/src/main/java/org/openapitools/api/DefaultApi.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
2828
@Validated
2929
@Api(value = "Default", description = "the Default API")
30-
@RequestMapping("${openapi.apiDocumentation.base-path:}")
3130
public interface DefaultApi {
3231

3332
/**

samples/client/petstore/spring-cloud-feign-without-url/src/main/java/org/openapitools/api/PetApiClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
import org.springframework.cloud.openfeign.FeignClient;
44
import org.openapitools.configuration.ClientConfiguration;
55

6-
@FeignClient(name="${pet.name:pet}", url="${pet.url:http://petstore.swagger.io/v2}", configuration = ClientConfiguration.class)
6+
@FeignClient(name="${pet.name:pet}", configuration = ClientConfiguration.class)
77
public interface PetApiClient extends PetApi {
88
}

0 commit comments

Comments
 (0)