Want to document optional JSON parameters in Swagger - spring-boot

I have an API that I'm trying to document using Swagger. My API takes a POJO as input via JSON in the RequestBody, and returns, likewise, a POJO as JSON in the ResponseBody. Some of the fields in my JSON object are nullable, and others are required. I would like my Swagger documentation to reflect which fields are nullable and which are required. Is there a way to do this simply, without creating a Swagger config file which would likely be longer than manually documenting the API in a text editor?
As a concrete example, let's say I have a POJO that looks like this:
public class pojo {
private String val1;
private String val2;
private String val3;
//getters, setters, constructors, etc.
}
Let's say I wanted my Swagger documentation to tell the reader: "On request, do not send val1 (e.g. this API is a database insert and val1 corresponds to the PK of the table which is supposed to be auto-generated), val2 is optional, and val3 is required". How do I do this?
As a related question, how can I do something similar for the response body? Like, using the POJO above, how can I say "on response, you should expect val1 to be empty, val2 might have a value or might be null, and val3 should have a value, assuming the service was successful"?

In order to document optional parameters within your POJO object it is possible to use the #ApiModelProperty attribute, for example:
public class pojo {
#ApiModelProperty(value = "This parameter will be ignored", required = false)
private String val1;
#ApiModelProperty(value = "This parameter is optional", required = false)
private String val2;
#ApiModelProperty(required = true)
private String val3;
//getters, setters, constructors, etc.
}
Swagger will take these annotations into account and it should be reflected in the documentation:
And in yaml API documentation:
pojo:
type: object
required:
- val3
properties:
val1:
type: string
description: This parameter will be ignored
val2:
type: string
description: This parameter is optional
val3:
type: string

Related

Swagger: How to use password format (Parameter annotation) for a String field ? Currently it won't allow me to submit the form with a value

I have a Spring Boot app (2.7.0) with spring docs (1.6.9)
Endpoint defined as follows:
#PostMapping(value = "/upload", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
#Operation(summary = "blah blah blah")
public ResponseEntity<UploadSummary> uploadData(
#RequestParam("file") final MultipartFile csvFile,
#Parameter(schema = #Schema(type = "string", format = "password"))
#RequestParam("password") final String password,
#RequestParam(name = "dryRun", required = false, defaultValue = "true") final boolean dryRun) {
I want to have the password input field on the Swagger interface mask the input. The only change I have made above is to include a single #Parameter annotation, nothing else.
The Swagger interface does not appear to like this single Parameter annotation at all, regardless of format. If I input a value in the password field, it is always highlighted with a red border and I cannot execute.
I tried upgrading to 1.6.12 but still doesn't work.
Perhaps other annotations or code needs to be setup before these Parameter annotations and Schema types work ?

RestTemplate fetch nested json from response

I need to consume a REST endpoint using spring RestTemplate. Below is the sample response from the endpoint and I need to fetch nested employee json and map it to model object. I found various complex solutions like
Deserializing response to a wrapper object and then fetching Employee object from it
Getting the response as a string and then converting it to json and deserializing
None of these solutions are simple and clean. Not sure if there is a clean and automatic way of doing it like this
ResponseEntity<Employee> response = restTemplate.exchange(URL,..?..);
Response -
{
"employee": {
"id": "123",
"first_name": "foo",
"last_name": "bar"
},
"session": {
"id": "1212121",
"createdDate": "2022-08-18T19:35:30Z"
}
}
Model object -
public class Employee {
private long emplId;
private String fName;
private String lName;
}
RestTemplate can do all of this for you, but first, your object doesn't match your response.
Ideally, you would have a Class that has an Employee and Session, like this
public class HaveSomeClassMan {
private Employee employee;
private Session session;
}
Then you can do
HaveSomeClassMan haveSomeClassMan = restTemplate.postForObject("URL", "requestObject", HaveSomeClassMan.class);
But, since you already have a JSON string, here's how you can convert it to a JSONObject, get the Employee out of it, and use the ObjectMapper to convert it to an Employee object.
JSONObject jsonObject = new JSONObject(s); // where s is your json string
String theEmployee = jsonObject.get("employee").toString(); // get the "employee" json from the JSONObject
Employee employee = new ObjectMapper().readValue(theEmployee, Employee.class); // Use the object mapper to convert it to an Employee
You do need to pay attention, though, as your model doesn't line up with your json. Your model calls the first name field, fname, yet your json has this as first_name. These properties all need to line up. If you want to keep your model as you've defined it, you need to annotate your fields with what their corresponding json field is, like this:
#JsonProperty("first_name")
private String fName;
This tells the mapper to map the JSON field first_name to the property fName. Regardless of whether you let RestTemplate do the mapping for you or you do it manually via the JSONObject approach, your model needs to match your JSON string. You can do this implicitly just by naming the fields in your model the same as they are in your JSON, or explicitly by adding the #JsonProperty to each field that doesn't implicitly map.

How to collect all fields annotated with #RequestParam into one object

I would like to gather all of my query parameters into a pojo and perform additional validation of the fields.
I have read that I can simply create an object and spring-boot will automatically set those request parameters on it.
#GetMaping
public ResponseEntity<?> listEntities(#RequestParam(value = "page-number", defaultValue = "0") #Min(0) Integer pageNumber,
#RequestParam(value = "page-size", defaultValue = "100") #Min(1) Integer pageSize ... )
I am thinking to create a class called RequestParamsDTO, where I'd have my query params responsible for the pagination.
But in order to have those fields set on the RequestParamsDTO, I'd have to match the name of the request param with the field name. But it won't be a valid variable name: page-size.
There must be some workaround, similar to #RequestParam's value attribute, that would set given request param on my field in the DTO.
Please advise.
Someone already purposed this feature before such that you can do the following .But unfortunately it is declined due to inactivity response :
public class RequestParamsDTO{
#RequestParam(value="page-number",defaultValue="0")
#Min(0)
private Integer pageNumber;
#RequestParam(value = "page-size", defaultValue = "100")
#Min(1)
Integer pageSize
}
The most similar things that you can do is using its #ModelAttribute which will resolve the parameter in the following orders:
From the model if already added by using Model.
From the HTTP session by using #SessionAttributes.
From a URI path variable passed through a Converter (see the next example).
From the invocation of a default constructor.
From the invocation of a “primary constructor” with arguments that match to Servlet request parameters. Argument names are determined through JavaBeans #ConstructorProperties or through runtime-retained parameter names in the bytecode.
That means the RequestParamsDTO cannot not have any default constructor (constructor that is without arguments) .It should have a "primary constructor" which you can use #ConstructorProperties to define which request parameters are mapped to the constructor arguments :
public class RequestParamsDTO{
#Min(0)
Integer pageNumber;
#Min(1)
Integer pageSize;
#ConstructorProperties({"page-number","page-size"})
public RequestParamsDTO(Integer pageNumber, Integer pageSize) {
this.pageNumber = pageNumber != null ? pageNumber : 0;
this.pageSize = pageSize != null ? pageSize : 100;
}
}
And the controller method becomes:
#GetMaping
public ResponseEntity<?> listEntities(#Valid RequestParamsDTO request){
}
Notes:
There is no equivalent annotation for #RequestParam 's defaultValue,so need to implement in the constructor manually.
If the controller method argument does not match the values in this , it will resolved as #ModelAttribute even though #ModelAttribute is not annotated on it explicitly.
To be honest this seems like a lot of effort for a functionality that exists already in spring-boot. You can either extend your repositories from PagingAndSortingRepository and have pagination added whenever you call a collection resource.
Or you can write a custom query method (or overwrite an existing one) and add this:
Page<Person> findByFirstname(String firstname, Pageable pageable);
This way spring boot will automatically add all those parameters you want to the Request.

How to remove some fields of an Object in Spring Boot response control?

this is one of my REST controller,
#RestController
#RequestMapping("/users/Ache")
public class Users {
#GetMapping
public User getUser() {
User user = new User();
return user;
}
}
As response, Spring boot will translate my Object to JSON,
this is response:
{
"username": "Ache",
"password": "eee",
"token": "W0wpuLAUQCwIH1r2ab85gWdJOiy2cp",
"email": null,
"birthday": null,
"createDatetime": "2019-03-15T01:39:11.000+0000",
"updateDatetime": null,
"phoneNumber": null
}
I want to remove password and token fields, How can I do?
I know two hard ways:
create a new hash map
and add some necessary fields, but it too complex
set those two fields to null
but it still leaves two null valued fields, it is too ugly.
Any better solution?
Spring leverages Jackson library for JSON marshalling by default. The easiest solution that comes to mind is making use of Jackson's #JsonIgnore but that would ignore the property on both serialization and de-serialization. So the right approach would be annotating the field with #JsonProperty(access = Access.WRITE_ONLY).
For instance, inside a hypothetical User class:
#JsonProperty(access = Access.WRITE_ONLY)
private String password;
#JsonProperty(access = Access.WRITE_ONLY)
private String token;
An alternative would be using #JsonIgnore only on the getter:
#JsonIgnore
public String getPassword() {
return this.password;
}
You can also create another class, for instance UserResponse with all the fields except password and token, and make it your return type. Of course it involves creating an object and populating it, but you leave your User class clean without Jackson annotations, and de-couples your model from your representation.
Keep the getter and setter but add the WRITE_ONLY JsonProperty. This way password validations will work when you use the entity as the request body.
#NotBlank
#JsonProperty(access = Access.WRITE_ONLY)
private String password;

How to parse Dynamic lists with Gson annotations?

I am using Retrofit and Gson to query an API, however I have never come across a JSON response like it.
The Response:
{
Response: {
"Black":[
{"id":"123","code":"RX766"},
{"id":"324","code":"RT344"}],
"Green":[
{"id":"3532","code":"RT983"},
{"id":"242","code":"RL982"}],
"Blue":[
{"id":"453","code":"RY676"},
{"id":"134","code":"R67HJH"}]
}
}
The problem is the list elements id eg "black" is dynamic, so I a have no idea what they will be.
So far I have created a class for the inner type;
class Type {
#SerializedName("id") private String id;
#SerializedName("code") private String code;
}
Is it possible to have the following?
class Response {
#SerializedName("response")
List<Type> types;
}
And then move the list ID into the type, so the Type class would become;
class Type {
#SerializedName("id") private String id;
#SerializedName("code") private String code;
#SerializedName("$") private String type; //not sure how this would be populated
}
If not, how else could this be parsed with just Gson attributes?
Ok so I have just seen this question;
How to parse dynamic JSON fields with GSON?
which looks great, is it possible to wrap a generic map with the response object?
If the keys are dynamic you want a map.
class Response {
#SerializedName("response")
Map<String, List<Type>> types;
}
After deserialization you can coerce the types into something more semantic in your domain.
If this is not suitable you will need to register a TypeAdapter or a JsonDeserializer and do custom deserialization of the map-like data into a simple List.

Resources