Skip to content

Commit 9b572a4

Browse files
authored
Merge pull request swagger-api#5411 from wing328/elizabetht-issue-5310
[Java][Spring] Add examples defined in the spec to Spring MVC server generator
2 parents c3fbb17 + ec2eafb commit 9b572a4

File tree

100 files changed

+587
-5135
lines changed

Some content is hidden

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

100 files changed

+587
-5135
lines changed

modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SpringCodegen.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
package io.swagger.codegen.languages;
22

3+
import com.samskivert.mustache.Mustache;
4+
import com.samskivert.mustache.Template;
35
import io.swagger.codegen.*;
46
import io.swagger.codegen.languages.features.BeanValidationFeatures;
57
import io.swagger.models.Operation;
68
import io.swagger.models.Path;
79
import io.swagger.models.Swagger;
810

911
import java.io.File;
12+
import java.io.IOException;
13+
import java.io.Writer;
1014
import java.util.*;
15+
import java.util.regex.Matcher;
16+
import java.util.regex.Pattern;
17+
1118

1219
public class SpringCodegen extends AbstractJavaCodegen implements BeanValidationFeatures {
1320
public static final String DEFAULT_LIBRARY = "spring-boot";
@@ -268,6 +275,19 @@ public void processOpts() {
268275
break;
269276
}
270277

278+
// add lamda for mustache templates
279+
additionalProperties.put("lamdaEscapeDoubleQuote", new Mustache.Lambda() {
280+
@Override
281+
public void execute(Template.Fragment fragment, Writer writer) throws IOException {
282+
writer.write(fragment.execute().replaceAll("\"", Matcher.quoteReplacement("\\\"")));
283+
}
284+
});
285+
additionalProperties.put("lamdaRemoveLineBreak", new Mustache.Lambda() {
286+
@Override
287+
public void execute(Template.Fragment fragment, Writer writer) throws IOException {
288+
writer.write(fragment.execute().replaceAll("\\r|\\n", ""));
289+
}
290+
});
271291
}
272292

273293
@Override

modules/swagger-codegen/src/main/resources/JavaSpring/api.mustache

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
1616
import org.springframework.web.bind.annotation.RequestParam;
1717
import org.springframework.web.bind.annotation.RequestPart;
1818
import org.springframework.web.multipart.MultipartFile;
19+
import java.io.IOException;
1920

2021
import java.util.List;
2122
{{#async}}
@@ -40,16 +41,18 @@ public interface {{classname}} {
4041
}{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}",{{/vendorExtensions.x-tags}} })
4142
@ApiResponses(value = { {{#responses}}
4243
@ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{returnType}}}.class){{#hasMore}},{{/hasMore}}{{/responses}} })
43-
{{#implicitHeaders}}@ApiImplicitParams({
44+
{{#implicitHeaders}}
45+
@ApiImplicitParams({
4446
{{#headerParams}}{{>implicitHeader}}{{/headerParams}}
45-
}){{/implicitHeaders}}
47+
})
48+
{{/implicitHeaders}}
4649
@RequestMapping(value = "{{{path}}}",{{#singleContentTypes}}
4750
produces = "{{{vendorExtensions.x-accepts}}}",
4851
consumes = "{{{vendorExtensions.x-contentType}}}",{{/singleContentTypes}}{{^singleContentTypes}}{{#hasProduces}}
4952
produces = { {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }, {{/hasProduces}}{{#hasConsumes}}
5053
consumes = { {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} },{{/hasConsumes}}{{/singleContentTypes}}
5154
method = RequestMethod.{{httpMethod}})
52-
{{#jdk8}}default {{/jdk8}}{{#responseWrapper}}{{.}}<{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}} {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}}){{^jdk8}};{{/jdk8}}{{#jdk8}} {
55+
{{#jdk8}}default {{/jdk8}}{{#responseWrapper}}{{.}}<{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}} {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}},{{/allParams}} @RequestHeader("Accept") String accept){{#examples}}{{#-first}} throws IOException{{/-first}}{{/examples}}{{^jdk8}};{{/jdk8}}{{#jdk8}} {
5356
// do some magic!
5457
return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK){{#async}}){{/async}};
5558
}{{/jdk8}}

modules/swagger-codegen/src/main/resources/JavaSpring/apiController.mustache

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import org.springframework.web.bind.annotation.RequestHeader;
1717
import org.springframework.web.bind.annotation.RequestParam;
1818
import org.springframework.web.bind.annotation.RequestPart;
1919
import org.springframework.web.multipart.MultipartFile;
20+
import com.fasterxml.jackson.databind.ObjectMapper;
21+
import java.io.IOException;
2022

2123
import java.util.List;
2224
{{#async}}
@@ -36,21 +38,44 @@ public class {{classname}}Controller implements {{classname}} {
3638
@org.springframework.beans.factory.annotation.Autowired
3739
public {{classname}}Controller({{classname}}Delegate delegate) {
3840
this.delegate = delegate;
39-
}{{/isDelegate}}
41+
}
42+
43+
{{/isDelegate}}
44+
{{^jdk8-no-delegate}}
45+
{{#operation}}
46+
public {{#async}}Callable<{{/async}}ResponseEntity<{{>returnTypes}}>{{#async}}>{{/async}} {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}},
47+
{{/allParams}}@RequestHeader("Accept") String accept){{#examples}}{{#-first}} throws IOException{{/-first}}{{/examples}} {
48+
// do some magic!
49+
{{^isDelegate}}
50+
{{^async}}
51+
{{#examples}}
52+
{{#-first}}
53+
54+
ObjectMapper objectMapper = new ObjectMapper();
4055

41-
{{^jdk8-no-delegate}}{{#operation}}
42-
public {{#async}}Callable<{{/async}}ResponseEntity<{{>returnTypes}}>{{#async}}>{{/async}} {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},
43-
{{/hasMore}}{{/allParams}}) {
44-
// do some magic!{{^isDelegate}}{{^async}}
45-
return new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK);{{/async}}{{#async}}
56+
{{/-first}}
57+
if (accept != null && accept.contains("{{{contentType}}}")) {
58+
return new ResponseEntity<{{>returnTypes}}>(objectMapper.readValue("{{#lamdaRemoveLineBreak}}{{#lamdaEscapeDoubleQuote}}{{{example}}}{{/lamdaEscapeDoubleQuote}}{{/lamdaRemoveLineBreak}}", {{>exampleReturnTypes}}.class), HttpStatus.OK);
59+
}
60+
61+
{{/examples}}
62+
return new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK);
63+
{{/async}}
64+
{{#async}}
4665
return new Callable<ResponseEntity<{{>returnTypes}}>>() {
4766
@Override
4867
public ResponseEntity<{{>returnTypes}}> call() throws Exception {
4968
return new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK);
5069
}
51-
};{{/async}}{{/isDelegate}}{{#isDelegate}}
52-
return delegate.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{/isDelegate}}
70+
};
71+
{{/async}}
72+
{{/isDelegate}}
73+
{{#isDelegate}}
74+
return delegate.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
75+
{{/isDelegate}}
5376
}
54-
{{/operation}}{{/jdk8-no-delegate}}
77+
78+
{{/operation}}
79+
{{/jdk8-no-delegate}}
5580
}
5681
{{/operations}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{#returnContainer}}{{#isMapContainer}}Map{{/isMapContainer}}{{#isListContainer}}List{{/isListContainer}}{{/returnContainer}}{{^returnContainer}}{{{returnType}}}{{/returnContainer}}

samples/client/petstore/spring-cloud/src/main/java/io/swagger/api/PetApi.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.springframework.web.bind.annotation.RequestParam;
1515
import org.springframework.web.bind.annotation.RequestPart;
1616
import org.springframework.web.multipart.MultipartFile;
17+
import java.io.IOException;
1718

1819
import java.util.List;
1920
import javax.validation.constraints.*;
@@ -68,7 +69,7 @@ public interface PetApi {
6869
produces = "application/json",
6970
consumes = "application/json",
7071
method = RequestMethod.GET)
71-
com.netflix.hystrix.HystrixCommand<ResponseEntity<List<Pet>>> findPetsByStatus( @NotNull @ApiParam(value = "Status values that need to be considered for filter", required = true, allowableValues = "available, pending, sold") @RequestParam(value = "status", required = true) List<String> status);
72+
com.netflix.hystrix.HystrixCommand<ResponseEntity<List<Pet>>> findPetsByStatus( @NotNull @ApiParam(value = "Status values that need to be considered for filter", required = true, allowableValues = "available, pending, sold") @RequestParam(value = "status", required = true) List<String> status) throws IOException;
7273

7374

7475
@ApiOperation(value = "Finds Pets by tags", notes = "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", response = Pet.class, responseContainer = "List", authorizations = {
@@ -85,7 +86,7 @@ public interface PetApi {
8586
produces = "application/json",
8687
consumes = "application/json",
8788
method = RequestMethod.GET)
88-
com.netflix.hystrix.HystrixCommand<ResponseEntity<List<Pet>>> findPetsByTags( @NotNull @ApiParam(value = "Tags to filter by", required = true) @RequestParam(value = "tags", required = true) List<String> tags);
89+
com.netflix.hystrix.HystrixCommand<ResponseEntity<List<Pet>>> findPetsByTags( @NotNull @ApiParam(value = "Tags to filter by", required = true) @RequestParam(value = "tags", required = true) List<String> tags) throws IOException;
8990

9091

9192
@ApiOperation(value = "Find pet by ID", notes = "Returns a single pet", response = Pet.class, authorizations = {
@@ -100,7 +101,7 @@ public interface PetApi {
100101
produces = "application/json",
101102
consumes = "application/json",
102103
method = RequestMethod.GET)
103-
com.netflix.hystrix.HystrixCommand<ResponseEntity<Pet>> getPetById(@ApiParam(value = "ID of pet to return",required=true ) @PathVariable("petId") Long petId);
104+
com.netflix.hystrix.HystrixCommand<ResponseEntity<Pet>> getPetById(@ApiParam(value = "ID of pet to return",required=true ) @PathVariable("petId") Long petId) throws IOException;
104105

105106

106107
@ApiOperation(value = "Update an existing pet", notes = "", response = Void.class, authorizations = {
@@ -150,6 +151,6 @@ public interface PetApi {
150151
produces = "application/json",
151152
consumes = "multipart/form-data",
152153
method = RequestMethod.POST)
153-
com.netflix.hystrix.HystrixCommand<ResponseEntity<ModelApiResponse>> uploadFile(@ApiParam(value = "ID of pet to update",required=true ) @PathVariable("petId") Long petId,@ApiParam(value = "Additional data to pass to server" ) @RequestParam(value="additionalMetadata", required=false) String additionalMetadata,@ApiParam(value = "file detail") @RequestParam("file") MultipartFile file);
154+
com.netflix.hystrix.HystrixCommand<ResponseEntity<ModelApiResponse>> uploadFile(@ApiParam(value = "ID of pet to update",required=true ) @PathVariable("petId") Long petId,@ApiParam(value = "Additional data to pass to server" ) @RequestParam(value="additionalMetadata", required=false) String additionalMetadata,@ApiParam(value = "file detail") @RequestParam("file") MultipartFile file) throws IOException;
154155

155156
}

samples/client/petstore/spring-cloud/src/main/java/io/swagger/api/StoreApi.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.springframework.web.bind.annotation.RequestParam;
1414
import org.springframework.web.bind.annotation.RequestPart;
1515
import org.springframework.web.multipart.MultipartFile;
16+
import java.io.IOException;
1617

1718
import java.util.List;
1819
import javax.validation.constraints.*;
@@ -43,7 +44,7 @@ public interface StoreApi {
4344
produces = "application/json",
4445
consumes = "application/json",
4546
method = RequestMethod.GET)
46-
com.netflix.hystrix.HystrixCommand<ResponseEntity<Map<String, Integer>>> getInventory();
47+
com.netflix.hystrix.HystrixCommand<ResponseEntity<Map<String, Integer>>> getInventory() throws IOException;
4748

4849

4950
@ApiOperation(value = "Find purchase order by ID", notes = "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", response = Order.class, tags={ "store", })
@@ -56,7 +57,7 @@ public interface StoreApi {
5657
produces = "application/json",
5758
consumes = "application/json",
5859
method = RequestMethod.GET)
59-
com.netflix.hystrix.HystrixCommand<ResponseEntity<Order>> getOrderById( @Min(1) @Max(5)@ApiParam(value = "ID of pet that needs to be fetched",required=true ) @PathVariable("orderId") Long orderId);
60+
com.netflix.hystrix.HystrixCommand<ResponseEntity<Order>> getOrderById( @Min(1) @Max(5)@ApiParam(value = "ID of pet that needs to be fetched",required=true ) @PathVariable("orderId") Long orderId) throws IOException;
6061

6162

6263
@ApiOperation(value = "Place an order for a pet", notes = "", response = Order.class, tags={ "store", })
@@ -68,6 +69,6 @@ public interface StoreApi {
6869
produces = "application/json",
6970
consumes = "application/json",
7071
method = RequestMethod.POST)
71-
com.netflix.hystrix.HystrixCommand<ResponseEntity<Order>> placeOrder(@ApiParam(value = "order placed for purchasing the pet" ,required=true ) @Valid @RequestBody Order body);
72+
com.netflix.hystrix.HystrixCommand<ResponseEntity<Order>> placeOrder(@ApiParam(value = "order placed for purchasing the pet" ,required=true ) @Valid @RequestBody Order body) throws IOException;
7273

7374
}

samples/client/petstore/spring-cloud/src/main/java/io/swagger/api/UserApi.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.springframework.web.bind.annotation.RequestParam;
1414
import org.springframework.web.bind.annotation.RequestPart;
1515
import org.springframework.web.multipart.MultipartFile;
16+
import java.io.IOException;
1617

1718
import java.util.List;
1819
import javax.validation.constraints.*;
@@ -76,7 +77,7 @@ public interface UserApi {
7677
produces = "application/json",
7778
consumes = "application/json",
7879
method = RequestMethod.GET)
79-
com.netflix.hystrix.HystrixCommand<ResponseEntity<User>> getUserByName(@ApiParam(value = "The name that needs to be fetched. Use user1 for testing. ",required=true ) @PathVariable("username") String username);
80+
com.netflix.hystrix.HystrixCommand<ResponseEntity<User>> getUserByName(@ApiParam(value = "The name that needs to be fetched. Use user1 for testing. ",required=true ) @PathVariable("username") String username) throws IOException;
8081

8182

8283
@ApiOperation(value = "Logs user into the system", notes = "", response = String.class, tags={ "user", })
@@ -88,7 +89,7 @@ public interface UserApi {
8889
produces = "application/json",
8990
consumes = "application/json",
9091
method = RequestMethod.GET)
91-
com.netflix.hystrix.HystrixCommand<ResponseEntity<String>> loginUser( @NotNull @ApiParam(value = "The user name for login", required = true) @RequestParam(value = "username", required = true) String username, @NotNull @ApiParam(value = "The password for login in clear text", required = true) @RequestParam(value = "password", required = true) String password);
92+
com.netflix.hystrix.HystrixCommand<ResponseEntity<String>> loginUser( @NotNull @ApiParam(value = "The user name for login", required = true) @RequestParam(value = "username", required = true) String username, @NotNull @ApiParam(value = "The password for login in clear text", required = true) @RequestParam(value = "password", required = true) String password) throws IOException;
9293

9394

9495
@ApiOperation(value = "Logs out current logged in user session", notes = "", response = Void.class, tags={ "user", })

samples/client/petstore/spring-stubs/src/main/java/io/swagger/api/PetApi.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.springframework.web.bind.annotation.RequestParam;
1515
import org.springframework.web.bind.annotation.RequestPart;
1616
import org.springframework.web.multipart.MultipartFile;
17+
import java.io.IOException;
1718

1819
import java.util.List;
1920
import javax.validation.constraints.*;
@@ -68,7 +69,7 @@ public interface PetApi {
6869
produces = "application/json",
6970
consumes = "application/json",
7071
method = RequestMethod.GET)
71-
ResponseEntity<List<Pet>> findPetsByStatus( @NotNull @ApiParam(value = "Status values that need to be considered for filter", required = true, allowableValues = "available, pending, sold") @RequestParam(value = "status", required = true) List<String> status);
72+
ResponseEntity<List<Pet>> findPetsByStatus( @NotNull @ApiParam(value = "Status values that need to be considered for filter", required = true, allowableValues = "available, pending, sold") @RequestParam(value = "status", required = true) List<String> status) throws IOException;
7273

7374

7475
@ApiOperation(value = "Finds Pets by tags", notes = "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", response = Pet.class, responseContainer = "List", authorizations = {
@@ -85,7 +86,7 @@ public interface PetApi {
8586
produces = "application/json",
8687
consumes = "application/json",
8788
method = RequestMethod.GET)
88-
ResponseEntity<List<Pet>> findPetsByTags( @NotNull @ApiParam(value = "Tags to filter by", required = true) @RequestParam(value = "tags", required = true) List<String> tags);
89+
ResponseEntity<List<Pet>> findPetsByTags( @NotNull @ApiParam(value = "Tags to filter by", required = true) @RequestParam(value = "tags", required = true) List<String> tags) throws IOException;
8990

9091

9192
@ApiOperation(value = "Find pet by ID", notes = "Returns a single pet", response = Pet.class, authorizations = {
@@ -100,7 +101,7 @@ public interface PetApi {
100101
produces = "application/json",
101102
consumes = "application/json",
102103
method = RequestMethod.GET)
103-
ResponseEntity<Pet> getPetById(@ApiParam(value = "ID of pet to return",required=true ) @PathVariable("petId") Long petId);
104+
ResponseEntity<Pet> getPetById(@ApiParam(value = "ID of pet to return",required=true ) @PathVariable("petId") Long petId) throws IOException;
104105

105106

106107
@ApiOperation(value = "Update an existing pet", notes = "", response = Void.class, authorizations = {
@@ -150,6 +151,6 @@ public interface PetApi {
150151
produces = "application/json",
151152
consumes = "multipart/form-data",
152153
method = RequestMethod.POST)
153-
ResponseEntity<ModelApiResponse> uploadFile(@ApiParam(value = "ID of pet to update",required=true ) @PathVariable("petId") Long petId,@ApiParam(value = "Additional data to pass to server") @RequestPart(value="additionalMetadata", required=false) String additionalMetadata,@ApiParam(value = "file detail") @RequestPart("file") MultipartFile file);
154+
ResponseEntity<ModelApiResponse> uploadFile(@ApiParam(value = "ID of pet to update",required=true ) @PathVariable("petId") Long petId,@ApiParam(value = "Additional data to pass to server") @RequestPart(value="additionalMetadata", required=false) String additionalMetadata,@ApiParam(value = "file detail") @RequestPart("file") MultipartFile file) throws IOException;
154155

155156
}

samples/client/petstore/spring-stubs/src/main/java/io/swagger/api/StoreApi.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.springframework.web.bind.annotation.RequestParam;
1414
import org.springframework.web.bind.annotation.RequestPart;
1515
import org.springframework.web.multipart.MultipartFile;
16+
import java.io.IOException;
1617

1718
import java.util.List;
1819
import javax.validation.constraints.*;
@@ -43,7 +44,7 @@ public interface StoreApi {
4344
produces = "application/json",
4445
consumes = "application/json",
4546
method = RequestMethod.GET)
46-
ResponseEntity<Map<String, Integer>> getInventory();
47+
ResponseEntity<Map<String, Integer>> getInventory() throws IOException;
4748

4849

4950
@ApiOperation(value = "Find purchase order by ID", notes = "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", response = Order.class, tags={ "store", })
@@ -56,7 +57,7 @@ public interface StoreApi {
5657
produces = "application/json",
5758
consumes = "application/json",
5859
method = RequestMethod.GET)
59-
ResponseEntity<Order> getOrderById( @Min(1) @Max(5)@ApiParam(value = "ID of pet that needs to be fetched",required=true ) @PathVariable("orderId") Long orderId);
60+
ResponseEntity<Order> getOrderById( @Min(1) @Max(5)@ApiParam(value = "ID of pet that needs to be fetched",required=true ) @PathVariable("orderId") Long orderId) throws IOException;
6061

6162

6263
@ApiOperation(value = "Place an order for a pet", notes = "", response = Order.class, tags={ "store", })
@@ -68,6 +69,6 @@ public interface StoreApi {
6869
produces = "application/json",
6970
consumes = "application/json",
7071
method = RequestMethod.POST)
71-
ResponseEntity<Order> placeOrder(@ApiParam(value = "order placed for purchasing the pet" ,required=true ) @Valid @RequestBody Order body);
72+
ResponseEntity<Order> placeOrder(@ApiParam(value = "order placed for purchasing the pet" ,required=true ) @Valid @RequestBody Order body) throws IOException;
7273

7374
}

0 commit comments

Comments
 (0)