How to define swagger annotation for json payload - java

how to define swagger annotation for this example post API.TenantConfiguration is getting as a json payload.
#Consumes({ "application/json", "application/xml" })
#POST
public Message configureSettings(TenantConfiguration configuration)
throws AndroidAgentException {
.....................
}

I found a solution to annotate json consuming Jax-rs Apis.It's working properly.
#POST
#ApiOperation(
consumes = MediaType.APPLICATION_JSON,
httpMethod = "POST",
value = "Configuring Android Platform Settings",
notes = "Configure the Android platform settings using this REST API"
)
#ApiResponses(value = {
#ApiResponse(code = 201, message = "Android platform configuration saved successfully"),
#ApiResponse(code = 500, message = "Internal Server Error")
})
Message configureSettings(#ApiParam(name = "configuration", value = "AndroidPlatformConfiguration")
TenantConfiguration configuration) throws AndroidAgentException;
Mapping class for the JSON object.
#XmlRootElement(
name = "tenantConfiguration"
)
#XmlAccessorType(XmlAccessType.NONE)
#ApiModel(
value = "TenantConfiguration",description = "This class carries all
information related to a Tenant configuration"
)
public class TenantConfiguration implements Serializable {
#XmlElement(
name = "type"
)
#ApiModelProperty(
name = "type",
value = "type of device",
required = true
)
private String type;
#ApiModelProperty(
name = "configuration",
value = "List of Configuration Entries",
required = true
)
#XmlElement(
name = "configuration"
)
private List<ConfigurationEntry> configuration;
public TenantConfiguration() {
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
public List<ConfigurationEntry> getConfiguration() {
return this.configuration;
}
public void setConfiguration(List<ConfigurationEntry> configuration) {
this.configuration = configuration;
}
}

Related

How to add an example in #ApiResponse with Swagger?

I would like to add an example with Swagger in my method, I have tried a few things, but they didn't work.
I have my Interface, where I define the method:
#Api(value = "test API")
#RequestMapping("/api/v1/product")
public interface TestController {
#ApiOperation(
value = "Service that return a Product",
notes = "This service returns a Product by the ID",
nickname = "getProductById",
response = ProductResponse.class)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "The request has succeeded.", response = ProductResponse.class),
#ApiResponse(code = 500, message = "Internal server error.", response = ProductResponse.class) })
#GetMapping(
value = "/productById",
produces = { "application/json" }
)
ResponseEntity<ProductResponse> getProductById(#RequestParam(value = "productId", required = true) String productId);
The ProductResponse class is the following:
#Getter
#Setter
#AllArgsConstructor
public class ProductResponse {
private Product product;
private CustomException customException;
}
The Product class is the following:
#Getter
#Setter
#AllArgsConstructor
public class Product {
#JsonProperty("id")
private String id;
#JsonProperty("productName")
private String productName;
#JsonProperty("productDescription")
private String productDescription;
#JsonProperty("unitPrice")
private Double unitPrice;
And the CustomException class is the following:
#Getter
public class CustomException {
private final String message;
private final String errorCode;
private final String errorType;
private final Exception exceptionDetail;
public CustomException(String message, String errorCode, String errorType, Exception exceptionDetail) {
this.message = message;
this.errorCode = errorCode;
this.errorType = errorType;
this.exceptionDetail = exceptionDetail;
}
When the response is 200, the response is like:
{
"product": {
"id": "12345",
"productName": "Product name",
"productDescription": "This is a description",
"unitPrice": 3.25
},
"customException": null
}
But when the response is 500, the response is like:
{
"product": "null,",
"customException": {
"message": "/ by zero",
"errorCode": "500",
"errorType": "Internal server error",
"exceptionDetail": null,
"cause": null,
"stackTrace": [
{
"classLoaderName": "app",
"moduleName": null,
"moduleVersion": null,
"methodName": "getProductById",
"fileName": "TestControllerImpl.java",
"lineNumber": 33,
"className": "com.myproject.testmicroservice.controller.impl.TestControllerImpl",
"nativeMethod": false
}
]
}
}
How can I add a custom example in the #ApiResponse annotation?
You are probably missing the #Operation annotation, where inside you put the #ApiResponse.
Example:
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
#Operation(responses = {
#ApiResponse(responseCode = "200", content = #Content(examples = {
#ExampleObject(name = "getUserAttribute",
summary = "Retrieves a User's attributes.",
description = "Retrieves a User's attributes.",
value = "[{\"value\": [\"area1\", \"area2\", \"area3\"], \"key\":\"GENERAL_AREAS\"}, {\"value\":\"933933933\", \"key\":\"FONyE\"}]")
}, mediaType = MediaType.APPLICATION_JSON_VALUE))})
public ResponseEntity<List<UserPreferenceDto>> getUserPreferenceByCode(
#Pattern(regexp = "\\w+") #PathVariable String userCode, #Parameter(hidden = true) Pageable pageable) {
...
}
Good evening hope you are doing well. In the case you are describing, I would do something like this
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "Found the book",
content = { #Content(mediaType = "application/json",
schema = #Schema(implementation = Book.class)) }),
#ApiResponse(responseCode = "400", description = "Invalid id supplied",
content = #Content),
the approach described is explained here. I think that paragraph 9. Generate Documentation Using #Operation and #ApiResponses is of particular interest in your case. I hope this helps, Have a good night
You can try something like this. In your controller you already have #ApiResponses annotation. What you need to do is add #ApiModel to your Product class and then add
#ApiModelProperty(notes = "Your comments", required = true, example = "example value")
to members of your Product class i.e. ProductResponse and CustomException. One thing that you will need to verify is whether #ApiModelProperty can be set on custom objects like ProductResponse and CustomException. If not you will need to set #ApiModelProperty 1 level deep.
As shown in article, the examples are auto populated from model property to response.
PS: As of now, I do not have setup of a swagger project so can only help you theoretically.
may be this late answer but incase any one need it, you can add the requestBody description along with content type within #Operation
#io.swagger.v3.oas.annotations.Operation(summary = "", description = "",
requestBody = #io.swagger.v3.oas.annotations.parameters.RequestBody(content = #Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))

Sprinboot backend and reactjs frontend FormData requst error

My springboot model is this:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Document(collection = "products")
public class Product {
#Id
private String id;
#NotEmpty(message = "name is mandatory")
private String name;
#NotEmpty(message = "price is mandatory")
private int price;
private MultipartFile file;
}
#AllArgsConstructor
#Data
#Builder
public class AddProductCommand implements Serializable {
#TargetAggregateIdentifier
private String id;
#NotNull(message = "no product details were supplied")
#Valid
private Product product;
public AddProductCommand(){
}
}
Since I have to send a MultipartFile from the reactjs, I must use FormData. I have tried the following:
async handleSubmit(event) {
event.preventDefault();
try {
let product = new FormData();
product.append("name", this.state.name);
product.append("price", this.state.price);
product.append("file", this.state.file);
let addProductCommand = new FormData();
addProductCommand.append("product", product);
const response = await axios.post(SELL_URL, addProductCommand , {
headers: {
'Content-Type': 'multipart/form-data'
}
});
this.clearState();
event.target.reset();
this.props.history.push("/buy");
} catch (err) {
console.log(JSON.stringify(err));
}
}
However, I got following error in the springboot:
DefaultHandlerExceptionResolver : Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'addProductCommand' on field 'product': rejected value [[object FormData]]; codes [typeMismatch.addProductCommand.product,typeMismatch.product,typeMismatch.com.cognizant.user.core.models.Product,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [addProductCommand.product,product]; arguments []; default message [product]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'com.cognizant.user.core.models.Product' for property 'product'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.cognizant.user.core.models.Product' for property 'product': no matching editors or conversion strategy found]]
What have I missed and how should I fix this error?
Add controller code. I am trying to implement it as CQRS design. Here is the controller. I nedd to write more description not much code. Otherwise stackoverflow doesn't allow me do add more code :(.
#RestController
#RequestMapping(path = "/api/v1/addProduct")
public class AddProductController {
private final CommandGateway commandGateway;
#Autowired
public AddProductController(CommandGateway commandGateway) {
this.commandGateway = commandGateway;
}
#PostMapping
public ResponseEntity<AddProductResponse> registerUser(#Valid #ModelAttribute AddProductCommand command) {
var id = UUID.randomUUID().toString();
command.setId(id);
try {
commandGateway.sendAndWait(command);
return new ResponseEntity<>(new AddProductResponse(id, "Product added successfully!"), HttpStatus.CREATED);
} catch (Exception e) {
var safeErrorMessage = "Error while processing add product request for id - " + id;
System.out.println(e);
return new ResponseEntity<>(new AddProductResponse(id, safeErrorMessage), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
for the above scenario, you are appending form data to the product key addProductCommand.append("product", product);
so in your controller, you should get the product entity. add this to your controller; #ModelAttribute("product")
public ResponseEntity<AddProductResponse> registerUser(#Valid #ModelAttribute("product") AddProductCommand command)

How to insert JSON object into database using spring boot and angularjs?

I am new to Spring boot so the question may sound silly. I want to insert a json object into database. But it is giving me an error like:
"Failed to evaluate Jackson deserialization for type".
On console, I am getting an error like:
Http 415 Unsupported Media type error with JSON
Here is my POJO class:
#Entity
#Table(name = "academics")
public class Academics {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "adhaarcard")
private String adhaarCard;
#Column(name = "grade")
private List grades;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAdhaarCard() {
return adhaarCard;
}
public void setAdhaarCard(String adhaarCard) {
this.adhaarCard = adhaarCard;
}
public List getGrades() {
return grades;
}
public void setGrades(List grades) {
this.grades = grades;
}
}
My controller function:
#RequestMapping(value="saveacademics",method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> saveAvademics(#RequestBody Academics academics) {
academicsService.save(academics);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(academics.getId()).toUri();
return ResponseEntity.created(location).build();
}
Angularjs Code:
$scope.saveAcademics = function() {
var adhaar = sessionStorage.getItem("emp-key");
var _data = {
"adhaarCard":adhaar,
"grades": {
"graduation":
{ "ssc": "SSC", "hsc": "HSC", "quOne": $scope.qone
},
"specialization":
{ 'ssc': "N.A", 'hsc': $scope.hscSpl, 'qoneSpl': $scope.qoneSpl},
"grade":
{ 'ssc': $scope.sscGrade, 'hsc': $scope.hscGrade, 'qoneGrade': $scope.qoneGrade},
"university":
{ 'ssc': $scope.sscUni, 'hsc': $scope.hscUni, 'qoneUni': $scope.qoneUni},
"year":
{ 'ssc': $scope.sscYear, 'hsc': $scope.hscYear, 'qoneYear': $scope.qoneYear}
}
};
console.log(_data);
$http({
url: 'saveacademics',
method: "POST",
data: JSON.stringify(_data)
})
.then(function(response) {
alert("Success");
},
function(response) { // optional
alert("Error Occoured.");
});
}
Try MediaType.APPLICATION_JSON instead on MediaType.APPLICATION_JSON_VALUE
#RequestMapping(value="saveacademics",method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON)
MediaType.APPLICATION_JSON is a "public constant media type for application/json", whereas MediaType.APPLICATION_JSON_VALUE is a "String equivalent of MediaType.APPLICATION_JSON".
Json that you generate from angular js and your Java pojo are not matching.
Better you can reformat json as below
{ adhaarCard: "12", grades : [
{ university: "univ name", specialization: "sadd", grade: 83, graduation:"SSC", year: 2007 },
{ university: "univ name", specialization: "sadd", grade: 67, graduation:"HSC", year: 2009 }
]
}
And mapping PoJo class as
#Column(name = "adhaarcard")
private String adhaarCard;
#Column(name = "grade")
private List<Grades> grades ;
Grades.java
private String university;
private String specialization;
private int grade;
private Sting graduation;
private int year;
Add Column mapping and getter, setters.
Tip:
Instead of #RequestMapping you can use #PostMapping which is a shorthand. So you wont get confused with media type, it take application/json as default type.
#RequestMapping(value="saveacademics",method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
replace with
#PostMapping("saveacademics")

Java Integer null turns into 0

When calling a REST service to save an Integer I've noticed that null value is turned into 0 and I don't know why this is happening. This is my class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "CompanyPreference") public class CompanyPreference {
public CompanyPreference() {
super();
}
#XmlElement
private Integer preferenceValueNumber;
public void setPreferenceValueNumber(Integer preferenceValueNumber) {
this.preferenceValueNumber = preferenceValueNumber;
}
public Integer getPreferenceValueNumber() {
return preferenceValueNumber;
}
}
and this is my web service
#POST
#SecureIt
#Consumes(value = { MediaType.APPLICATION_JSON })
#Produces(value = { MediaType.APPLICATION_JSON })
#Path("/{companyCode}/{companyType}/preferences/update")
#ApiOperation(value = "Update Company Preferences", response = DefaultResponse.class, authorizations = { #Authorization(value="Bearer") })
public Response savePreferences(#ApiParam(value = "Company Code", required = true) #PathParam("companyCode") String companyCode,
#ApiParam(value = "Company Type", required = true) #PathParam("companyType") String companyType,
#ApiParam(value = "Array of Company Preference objects", required = true) CompanyPreference[] companyPreferences,
#Context ContainerRequestContext crc) throws ResponseError {
for(int i = 0; i < companyPreferences.length; i++) {
System.out.println(companyPreferences[i].getPreferenceValueNumber());
}
}
Even though preferenceValueNumber is an Integer not a primitive int, it still gets turned into 0 when my request looks like this...
[
{ preferenceValueNumber: "" }, //this returns 0
{ preferenceValueNumber: 30 } //this returns 30 as it should
]
How can I keep the original value, null?

using Swagger #ApiResponse responseContainer not working when code is 400

In Swagger Java API, when I use a responsecontainer="List" (Or "Set") with a code=400, I am not getting the model of the response on Swagger-GUI. I am just getting Array[Object].
Here is the concrete case:
#CrossOrigin
#RestController
#RequestMapping(value = "/api")
#Loggable(prepend = true, trim = false)
public class ConfigResource {
private final ConfigResourceDelegate delegate;
#Inject
public ConfigResource(final ConfigResourceDelegate delegate) {
this.delegate = delegate;
}
#RequestMapping(
value = "/v1/config",
method = PUT,
consumes = APPLICATION_JSON_UTF8_VALUE,
produces = APPLICATION_JSON_UTF8_VALUE
)
#ApiResponses(value = {#ApiResponse(code=202,message = "ACCEPTED" ),
#ApiResponse(code=200,response = Rejection.class, responseContainer
= "Set", message = "BAD_REQUEST"),
#ApiResponse(code=500, message = "INTERNAL_SERVER_ERROR")})
public ResponseEntity<?> putConfig(final #RequestBody ConfigDto
configDto){
return delegate.putConfig(riskConfigDto);
}
}
Here is the Rejection Class:
public class Rejection {
private Long id;
private RejectionDTO rejection;
private String originMessage;
public Rejection() {
}
public Long getId() {
return id;
}
public RejectionDTO getRejection() {
return rejection;
}
public String getOriginMessage() {
return originMessage;
}
public void setId(Long id) {
this.id = id;
}
public void setRejection(RejectionDTO rejection) {
this.rejection = rejection;
}
public void setOriginMessage(String originMessage) {
this.originMessage = originMessage;
}
}
So normally i'am supposed to have this model between [] in the swagger UI. However, I am getting Array[Object]:
See screen capture
To make your example work, you need to change your return value from wildcard, ResponseEntity<?>, to a concrete class, ResponseEntity<List<Rejection>>. Also, you need to change responseContainer to a List from Set.
#RequestMapping(
value = "/v1/config",
method = PUT,
consumes = APPLICATION_JSON_UTF8_VALUE,
produces = APPLICATION_JSON_UTF8_VALUE
)
#ApiResponses(value = {#ApiResponse(code=202,message = "ACCEPTED" ),
#ApiResponse(code=200,response = Rejection.class, responseContainer
= "List", message = "BAD_REQUEST"),
#ApiResponse(code=500, message = "INTERNAL_SERVER_ERROR")})
public ResponseEntity<List<Rejection>> putConfig(final #RequestBody ConfigDto
configDto){
return delegate.putConfig(riskConfigDto);
}

Categories

Resources