Swagger UI with polymorphism is not showing the correct model - spring-boot

My swagger UI (version 2.8.0) is not inferring well my model:
Audience.class:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
#JsonSubTypes({
#JsonSubTypes.Type(value = AudienceA.class, name = "AudienceA"),
#JsonSubTypes.Type(value = AudienceB.class, name = "AudienceB")
})
public interface Audience {
void doSomething();
}
AudienceA.class:
public class AudienceA implements Audience {
private String someStringField;
public AudienceA(){
}
#Override
public void doSomething(){
//do some stuff
}
public String getSomeStringField(){
return someStringField;
}
}
AudienceB.class:
Similar to AudienceA class.
SwaggerUI shows an empty model:
I was expecting something like displaying something about AudienceA or AudienceB model. Is this the expected behaviour of the UI?
Thanks.

Swagger has had its issues with polymorphism , especially Swagger 2.0. A lot of it these issues were corrected with Open API 3.0
You might want to look at following links to see how the polymorphism is handled in Open API 3.0
Open API 3.0 - https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/
Where as there is another post which probably addresses the issue mentioned by you here
https://groups.google.com/forum/#!msg/swagger-swaggersocket/1z0f0pLplrM/WylVo2ioDAAJ

Related

How to write #ApiResponse which may return a Class or a List of that class using OpenAPI 3 Swagger in Spring Boot

As written in documentation we can use anyOf with #Schema if we want to define multiple responses.
#ApiResponse(responseCode = "201", description = "OK",
content = #Content(schema = #Schema(anyOf = {Product.class, Activity.class})))
My controller returns either a Product or a List<Product>. I would like to specify this in my OpenAPI 3 documentation.
I would like to know if it's possible.
If Yes, then how?
If No, then is there any workaround?
I don't only want to specify List.class. I want to specify List<Product>.
P.S.:- Searching on Google didn't get me any results that I can use.
Ok, thats a tough one.
Basically if you really want to return a List of Objects or one Object, then you can create a basic interface like this
public interface Response {
}
And then you can create your Object, which implements the response
public class Hello implements Response {
private String message;
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
}
Finally we can create the List of our Object. For that we need to create a class, which extends the ArrayList and implements our Interface
public class HelloList extends ArrayList<Hello> implements Response {
}
After that we can just set our schema as implementation
#ApiResponse(responseCode = "200", description = "hello world", content = #Content(mediaType = "application/json", schema = #Schema(implementation = Response.class)))
On the Clientside you need to check the instance of the Response-Object, so that you can parse either the Object or the List
Response response = someCall();
if (response instanceof Hello) {
System.out.println(processHello((Hello) response);
}
if (response instanceof HelloList) {
System.out.println(processHelloList((HelloList) response);
}
This example works, but its very very complex und confusing. A better way to design your api, would be to return just the list. I don't see the benefit to seperate one object or the list of it.

Spring 5 Webflux functional endpoints - How to perform input validation?

According to the current doc (5.0.0.RELEASE) Spring Webflux supports validation when working with annotated controllers:
By default if Bean Validation is present on the classpath — e.g.
Hibernate Validator, the LocalValidatorFactoryBean is registered as a
global Validator for use with #Valid and Validated on #Controller
method arguments.
However nothing is said about how to automate it with functional endpoints. In fact, the only example of input processing in the documentation doesn't validate anything:
public Mono<ServerResponse> createPerson(ServerRequest request) {
Mono<Person> person = request.bodyToMono(Person.class);
return ServerResponse.ok().build(repository.savePerson(person));
}
Are we supposed to do this manually or there is some automatic way to do it?
In Spring version 5.0, there is no automatic way to do validation in functional endpoints, and as such validation must be done manually.
Though there are currently no concrete plans to do so, we might add some sort of validation in the future. But even then it will be an explicit method call, and not an automatic mechanism. Overall, the functional endpoint model is designed to be a lot more explicit than the annotation-based model.
As arjen-poutsma said, it seems there is no way of running automated validations on Spring 5 functional endpoints.
Spring documentation is not very clear about this, and it doesn't suggest any approach.
On this Baeldung article, you'll find an idea on how you can run validations using this approach (disclaimer: I'm the writer of the article :) )
In a nutshell, you can follow these steps:
Implement Spring Validators to evaluate your resources
Create an abstract class with the basic procedure that any handler will follow when processing a request, leaving up to the children classes what to do when the data is valid
Make your request handler classes extend this abstract class, implementing this abstract method, stating the body it will be expecting, and what validator needs to be used to validate it
EDIT:
I've been following this related Spring issue, and it seems we now count with official documentation regarding this subject: https://github.com/spring-projects/spring-framework/blob/master/src/docs/asciidoc/web/webflux-functional.adoc#validation
The suggested approach is to use validators as explained in the article.
At the current version(2.0.4.RELEASE) there isn't a way to do automatic validation with handles, however you always could make a manual validation like this:
#Slf4j
#Component
#FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
#RequiredArgsConstructor
public class MyHandlerValidator implements HandlerValidator<MyResource> {
Validator validator;
#Override
public void callValidator(final MyResource fdr) {
final DataBinder binder = new DataBinder(fdr);
binder.setValidator(validator);
binder.validate();
if (binder.getBindingResult().hasErrors()) {
final String reason = binder.getBindingResult().getFieldError().toString();
log.error(reason);
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, reason);
}
}
}
The thing with this, its that the you should throw a WebExchangeBindException like automatic validation does, however i could't create a MethodParameter witch is a dependency to create this exception.
UPDATE:
Spring show us a way to do it, which is similar to my solution, but, not enough in my opinion on documentation
Just to demo some working code. If you need simple validation based on the object annotations like:
#Value
#Builder
#Jacksonized
public class SigninRequest {
#NotBlank(message = "The username is mandatory")
#Email(message = "The username should be valid Email")
String username;
#NotBlank(message = "The password is mandatory")
String password;
}
At the handler you need just one simple additional operator doOnNext:
#Component
#RequiredArgsConstructor
public class AuthHandler {
private final AuthService authService;
private final ObjectValidator validator;
public Mono<ServerResponse> signin(ServerRequest request) {
return ok().body(
request.bodyToMono(SigninRequest.class)
.doOnNext(validator::validate) //<-- just one single line
.flatMap(login -> authService.authenticate(login.getUsername(), login.getPassword())),
AuthResult.class);
}
}
The ObjectValidator is doing actual validation and throws the runtime exception with the 4xx error in case of validation errors:
#Component
#RequiredArgsConstructor
public class ObjectValidator {
private final Validator validator;
public <T> T validate(T object) {
var errors = validator.validate(object);
if (errors.isEmpty()) {
return object;
} else {
String errorDetails = errors.stream().map(er -> er.getMessage()).collect(Collectors.joining(", "));
throw new ObjectValidationException(errorDetails);
}
}
}
And the exception:
#ResponseStatus(code = HttpStatus.UNPROCESSABLE_ENTITY)
public class ObjectValidationException extends RuntimeException {
public ObjectValidationException(String errorDetails) {
super("Please supply the valid data: " + errorDetails);
}
}
If you properly setup global error handling you can keep you handler code clean and reuse the object validator across all your handlers.

ShouldSerialize* methods are not called in Web API documentation using SWAGGER

I am using Swagger for Web API documentation.
In my Web API, I have entities as below:
public class BaseEntity
{
public string BaseEntProp1{get;set;}
public string BaseEntProp2{get;set;}
public virtual bool ShouldSerializeBaseEntProp1()
{
return true;
}
public virtual bool ShouldSerializeBaseEntProp1()
{
return true;
}
}
public class DerivedEntity1 : BaseEntity
{
[JsonIgnore]
public string DerEntProp1{get;set;}
public string DerEntProp2{get;set;}
public override bool ShouldSerializeBaseEntProp1()
{
return false;
}
public override bool ShouldSerializeBaseEntProp1()
{
return false;
}
}
I used DerivedEntity1 as the input parameter for a Web API method and generated the swagger documentation.
Until this it is fine but the problem is, DerivedEntity1 JSON string in that documentation shows both BaseEntProp1 & BaseEntProp2 which are supposed to be excluded. Can someone help me how to exclude those?
Note:
1. DerivedEntity1's DerEntProp1 property is properly excluded.
2. Just to confirm, in my Startup method after the documentation is generated, I have hardcoded following:
var temp = new DerivedEntity1 {
BaseEntProp1 = "should be ignored",
BaseEntProp2 = "this also should be ignored"};
string tempJson = JsonConvert.SerializeObject(temp);
Above test passed, i.e., tempJson doesn't have both BaseEntProp1 & BaseEntProp2. So, I suspect somehow SWAGGER is failing to call proper ShouldSerialize* methods. Any help is highly appreciated.
Thanks
Finally I solved it in a different way as the problem is not related to Swagger.
I have created base abstract classes with virtual properties. In derived classes I have overloaded those properties and decorated with JsonIgnore attribute. This solved my problem.

Spring Data Rest - Add link to search endpoint

In our Spring-Data-Rest Project we have a custom (fuzzy) search on a /buergers/search/findBuergerFuzzy?searchString="..." endpoint.
Is it possible to add a link for it on the /buergers/search endpoint (Without overriding the automatically exposed Repository findBy Methods)?
The Controller exposing the search:
#BasePathAwareController
#RequestMapping("/buergers/search/")
public class BuergerSearchController {
#Autowired
QueryService service;
#RequestMapping(value = "/findBuergerFuzzy", method = RequestMethod.GET)
public
#ResponseBody
ResponseEntity<?> findBuergerFuzzy(PersistentEntityResourceAssembler assembler, #Param("searchString") String searchString) {
if (searchString.length() < 3)
throw new IllegalArgumentException("Search String must be at least 3 chars long.");
List<Buerger> list = service.query(searchString, Buerger.class, new String[]{"vorname", "nachname", "geburtsdatum", "augenfarbe"});
final List<PersistentEntityResource> collect = list.stream().map(assembler::toResource).collect(Collectors.toList());
return new ResponseEntity<Object>(new Resources<>(collect), HttpStatus.OK);
}
}
UPDATE: This is an outdated workaround answer. Upgrade to Spring HATEOAS 1.0.
Old Workaround:
Digging the spring-data-rest source i found the RepositorySearchesResource which seems to solve the problem.
#Component
public class SearchResourcesProcessor implements ResourceProcessor<RepositorySearchesResource> {
#Override
public RepositorySearchesResource process(RepositorySearchesResource repositorySearchesResource) {
final String search = repositorySearchesResource.getId().getHref();
final Link findFullTextFuzzy = new Link(search + "/findFullTextFuzzy{?q}").withRel("findFullTextFuzzy");
repositorySearchesResource.add(findFullTextFuzzy);
return repositorySearchesResource;
}
}
Because we generate this code by templates, this is sufficient and fullfills our needs. Make sure to check the comments for the right and safe way.
Version
migrate-to-1.0.changes
ResourceSupport is now RepresentationModel
Resource is now EntityModel
Resources is now CollectionModel
PagedResources is now PagedModel
Code
The code for new version:
import org.springframework.data.rest.webmvc.RepositorySearchesResource;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.server.RepresentationModelProcessor;
import org.springframework.stereotype.Component;
#Component
public class RepositorySearchesProcessor implements RepresentationModelProcessor<RepositorySearchesResource> {
#Override
public RepositorySearchesResource process(RepositorySearchesResource model) {
System.out.println(model.getDomainType());
model.add(Link.of(model.getRequiredLink("self").getHref() + "/findFullTextFuzzy{?q}").withRel("findFullTextFuzzy"));
return model;
}
}
How
About how to find what resource or model you use, after setting breakpoints in each method of RepresentationModel, you maybe find something useful :

How to generate code based on another class?

To create our test data, we use the following variation of the Builder pattern (simplified example!):
Sample class:
public class Person
{
public string Name { get; set; }
public string Country { get; set; }
}
The builder:
public class PersonBuilder
{
private string name;
private string country;
public PersonBuilder()
{
SetDefaultValues();
}
private void SetDefaultValues()
{
name = "TODO";
country = "TODO";
}
public Person Build()
{
return new Person
{
Name = name,
Country = country
};
}
public PersonBuilder WithName(string name)
{
this.name = name;
return this;
}
public PersonBuilder WithCountry(string country)
{
this.country = country;
return this;
}
}
NOTE: The context of the example itself is not relevant. The important thing here is how in the example, the a builder class like PersonBuilder can completely be generated by looking at the entity class (Person) and applying the same pattern - see below.
Now imagine that the person class has 15 properties instead of 2. It would take some monkeywork to implement the builder class, while theoretically, it could automatically be generated from the Person class. We could use code generation to quickly set up the builder class, and add custom code later if needed.
The code generation process would have to be aware of the context (name and properties of the person class), so simple text-based code generation or regex magic doesn't feel right here. A solution that is dynamic, not text-based and can be triggered quickly from inside visual studio is preferred.
I'm looking for the best way to perform code generation for scenarios like this.
Reflection? Codesmith? T4 templates? Resharper Live templates with macros?
I'm looking forward to see some great answers :)
The T4 solution is a well Visual Studio integrated option. You can use reflection inside the T4 template to actually generate the code.
We added a feature in CodeSmith Generator 5.x that allows you to generate off of existing code. Please take a look at that documentation here. Also you can use reflection or any .NET library in a CodeSmith Generator Template.
Thanks
-Blake Niemyjski
If it is for test only, consider a mocking framework like RhinoMocks:
internal class PersonBuilder
{
private MockRepository _mockRepository;
private IPerson _person;
public PersonBuilder()
{
_mockRepository = new MockRepository();
_person = _mockRepository.Stub<IPerson>();
}
public PersonBuilder WithName(string name)
{
_person.Name = name;
return this;
}
public PersonBuilder WithCountry(string Country)
{
_person.Country= Country;
return this;
}
public IPerson Build()
{
_mockRepository.ReplayAll();
return _person;
}
}
This way your builder can evolve along with your need. Further, you don't need to change your Build method. Just add "WithX" methods.
Have a look into the ABSE modeling approach and its IDE, AtomWeaver. ABSE is a template-based modeling and code generation framework, where a model (has nothing to do with UML or MDA) is created by applying "building blocks" (Atoms). These Atoms are template/program hybrids and are context-aware: an Atom can generate code according to its placement on the tree and on the presence/absence of certain Atoms.
The model host (AtomWeaver in this case) will "execute" the model in order to obtain the desired source code. The model can be "the source": change the model and regenerate as many times as necessary.
AtomWeaver is not integrated into Visual Studio, but can work alongside it without any problems.

Resources