I have Swagger code annotations for documentation purposes similar to the following:
#RequestMapping(value = "/campaigns/{campaignUuid}",
method = RequestMethod.GET)
#ApiOperation(value = "...",
httpMethod = "GET",
notes = "...",
response = XCampaign.class,
tags = { "Campaigns" })
#ApiResponses(value = { #ApiResponse(code = 200,
message = "OK",
response = XCampaign.class),
#ApiResponse(code = 401,
message = "Unauthorized",
response = HttpErrorResponse.class),
#ApiResponse(code = 400,
message = "Bad Request",
response = HttpErrorResponse.class),
#ApiResponse(code = 404,
message = "Not Found",
response = HttpErrorResponse.class),
#ApiResponse(code = 500,
message = "Internal Server Error",
response = HttpErrorResponse.class) })
Basically, I want to have the following responses documented in Swagger:
On 200:
{
'campaign': {
...
},
links: {
...
}
}
On 4xx or 5xx:
{
error: {
...
}
}
The problem I am having is the "nested" component. If I list the class itself as the Response class, it is obviously not given back as nested JSON. If I try to nest it however, like follows:
public class HttpErrorResponse {
private ErrorResponse error;
private class ErrorResponse {
public final String code;
public final String message;
public ErrorResponse(String code, String message) {
this.code = code;
this.message = message;
}
}
}
Then the Swagger Docs show an empty Object:
HttpErrorResponse {}
I've also taken a look at Spring HATEOAS, and think that I will be able to achieve the 2xx Response type using that, how can I achieve what I want in general with nested types?
Add something like this to your Docket
.alternateTypeRules(
newRule(typeResolver.resolve(DeferredResult.class,
typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
typeResolver.resolve(WildcardType.class)))
Which substitutes DeferredResult<ResponseEntity<T>> with T generically, for more info see the useful guid for how to configure Swagger here.
Also import all required lib, including the one which is tricky to figure out:
import static springfox.documentation.schema.AlternateTypeRules.*;
Related
I have a microservice that need to consume from another microservice. The thing is that the controller has an authentication header. So how can I get that token from it to make a call from this new rest client?
I have used swagger codegen to get the different endpoints that I need to check.
The interface is as follows:
#javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.SpringCodegen", date = "2022-06-20T09:40:39.340424400+02:00[Europe/Berlin]")
#Api(value = "getRelationByCucoId", description = "the getRelationByCucoId API")
public interface GetRelationByCucoIdApi {
#ApiOperation(value = "Retrieve a list of CustomerIDs for a certain CucoId", nickname = "getRelationByCucoIdUsingGET", notes = "This is a description", response = CucoPerson.class, responseContainer = "List", tags={ "Customers", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully retrieved list", response = CucoPerson.class, responseContainer = "List"),
#ApiResponse(code = 400, message = "The provided CucoID format or length is incorrect (Integer up to 10 digit)"),
#ApiResponse(code = 401, message = "You are not authorized to view the resource, check if the JWT was provided in the header"),
#ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
#ApiResponse(code = 404, message = "The resource you were trying to reach is not found") })
#RequestMapping(value = "/getRelationByCucoId/{cucoId}",
produces = { "application/json" },
method = RequestMethod.GET)
ResponseEntity<List<CucoPerson>> getRelationByCucoIdUsingGET(#ApiParam(value = "cucoId",required=true) #PathVariable("cucoId") Integer cucoId
,#ApiParam(value = "Authentication Header" ) #RequestHeader(value="GS-AUTH-TOKEN", required=false) String GS_AUTH_TOKEN, #AuthenticationPrincipal Principal principal
);
}
And the implementation:
#Override
public ResponseEntity<List<CucoPerson>> getRelationByCucoIdUsingGET(Integer cucoId, String GS_AUTH_TOKEN, Principal principal) {
return new ResponseEntity<>(new ArrayList<>(), HttpStatus.OK);
}
So how can I get it authenticated?
Thanks!
I built a dummy api and I want to test it on swagger. The swagger.json has been successfully generated and executed to show the swagger UI.
But there is the 404 error that cannot find the response part.
How can I solve this?
This is the built swagger UI.
And this is the code.
#Service
#Api
public class className () {
#GET
#Path("/oauth2/authorize")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#ApiOperation(value = "Authorization Grant", notes = "Authorization Grant")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully Granted the Authorization"),
#ApiResponse(code = 400, message = "Missing or invalid request body"),
#ApiResponse(code = 403, message = "Forbidden"),
#ApiResponse(code = 404, message = "Schema not found"),
#ApiResponse(code = 500, message = "Internal error")})
public Response authorizationGrant(
#HeaderParam("X-AUTH-TOKEN") final String token,
#HeaderParam("X-CSRF-TOKEN") final String csrftoken,
#ApiParam(value = "Client ID", required = true) #QueryParam("Client ID") final String clientId,
#ApiParam(value = "Scope", required = true) #QueryParam("Scope") final String scope,
#ApiParam(value = "Redirect Uri", required = true) #QueryParam("Redirect Uri") final String redirectUri,
#ApiParam(value = "Response Type", required = true) #QueryParam("Response Type") final String responseType ) throws AccessDeniedException {
return Response
.status(Response.Status.OK)
.entity("{\"hello\": \"This is a JSON response\"}")
.type(MediaType.APPLICATION_JSON)
.build();
}
}
Please tell me what you need more to be clear with this error.
The problem solved!!!
I hope this answer could help for others who are suffered from this trouble. :)
The error was from the #Api Definition part. I should have define the path in that part.
This is the corrected code.
#Path("/oauth2")
#Service
#Api
public class className () {
.....
#GET
#Path("/authorize")
.....
}
As you can see the #Api definition part requires the #Path annotation.
:)
I am trying to use swagger with java.
Using NSwag studio I am able to generate all my endpoints except one that returns a list of objects.
Here is my action in controller:
#ApiOperation(value = "getAll", nickname = "getAll", responseContainer = "List", response = DiakEntity.class)
#GetMapping("/api/diakok")
#ResponseBody
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
public List<DiakEntity> GetDiakok() throws Exception
{
ServiceObjectResponse<List<DiakEntity>> request = _diakService.getAll();
if(!request.getIsSuccess())
{
throw new Exception(request.getMessage());
}
return request.getObject();
}
I am using swagger-annotations 1.5.23, springfox-swagger-ui 2.9.2, springfox-swagger2 2.9.2.
If I test from Postman it works.
Also tried like this:
#ApiOperation(value = "getAll", nickname = "getAll")
#ApiResponse(code = 200, responseContainer="List", response=DiakEntity.class, message = "Gets all diak objects")
#GetMapping("/api/diakok")
#ResponseBody
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
public ResponseEntity<List<DiakEntity>> GetDiakok() throws Exception
{
ServiceObjectResponse<List<DiakEntity>> request = _diakService.getAll();
if(!request.getIsSuccess())
{
throw new Exception(request.getMessage());
}
return new ResponseEntity<>(request.getObject(), HttpStatus.OK);
}
thnx
Please try with the following annotation for swagger.
#ApiOperation(value = "getAll", nickname = "getAll")
#ApiResponse(code = 200, responseContainer="List", response=DiakEntity.class)
At the end I changed my action as below, and it started to work
#ApiOperation(value = "all", nickname = "all")
#PostMapping("/api/diak/all")
#ResponseBody
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
public List<DiakEntity> GetAll(#RequestBody #Valid RequestDiakByName data) throws Exception
{
ServiceObjectResponse<List<DiakEntity>> request = _diakService.getAll();
if(!request.getIsSuccess())
{
throw new Exception(request.getMessage());
}
return request.getObject();
}
I have written a code which will throw the following message if the list id specify in the URL didn't find in the db. It should send a json response with error message but i am getting exception class name also with message:
Expected Output from rest API:
{
"code": 404,
"message": "Watchlist dnd was not found"
}
code:
#RolesAllowed({ "admin" })
#Path("/{listId}")
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Returns a watchlist.", notes = "")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "The watchlist was returned.", response = Watchlist.class),
#ApiResponse(code = 404, message = "The watchlist was not found.", response = ErrorMessage.class),
#ApiResponse(code = 500, message = "Internal server error.", response = ErrorMessage.class) })
public Watchlist getList(#PathParam("listId") String listId, #HeaderParam("x-access-token") String jwtToken,
#Context SecurityContext sec, #Context final HttpServletResponse response) throws Exception {
final String sourceMethod = "getList";
if (logger.isLoggable(Level.FINER)) {
logger.entering(CLASSNAME, sourceMethod);
}
WatchlistService service = new WatchlistService(cedmPersitence);
Watchlist list = service.getWatchList(listId);
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLASSNAME, sourceMethod);
}
return list;
}
public Watchlist getWatchList(String listId) throws IOException,NotFoundException{
Watchlist list = new Watchlist();
list.setListId(listId);
if(listId !=null) {
HBasePersistence persistence = new HBasePersistence();
persistence.init("watchlist");
List<WatchlistEntry> watchListEntries = persistence.getWatchlistByListId(listId);
if (watchListEntries == null || watchListEntries.isEmpty()) {
throw new NotFoundException("Watchlist " + listId + " was not found");
}
list.setEntries(watchListEntries);
}
return list;
}
But I am getting this response:
{
"code": 404,
"message": "class com.ibm.cedm.exception.NotFoundException:Watchlist dnd was not found"
}
anybody know why is it so ?
As you can see in the reference:
https://docs.oracle.com/javaee/7/api/javax/ws/rs/NotFoundException.html#NotFoundException-java.lang.String- , when you pass the error message to a NotFoundException, surely the Throwable.getMessage() enrich that message with the class name.
NotFoundException also accepts a Response as parameter, so instead of passing a message you can build the response by passing the Response.
final Response.ResponseBuilder response = Response.status(Response.Status.NOT_FOUND);
response.entity("Watchlist " + listId + " was not found").type("text/plain");
throw new NotFoundException(response.build());
I use swagger to create a RESTful API, and have several endpoints which return the same errors and responses:
#GET
#Path("/some/endpoint")
#ApiOperation(
value = "Some method",
notes = "Some method")
#ApiResponses(
value = {
#ApiResponse(code = 200, message = RestConstants.HTTP_200, response = Response.class),
#ApiResponse(code = 400, message = RestConstants.HTTP_400, response = Error.class),
#ApiResponse(code = 401, message = RestConstants.HTTP_401, response = Error.class),
#ApiResponse(code = 403, message = RestConstants.HTTP_403, response = Error.class),
#ApiResponse(code = 404, message = RestConstants.HTTP_404, response = Error.class),
#ApiResponse(code = 500, message = RestConstants.HTTP_500, response = Error.class)
})
public Response someMethod(){...}
The amount of #ApiResonses is may about to change. As of now, I need to declare all of theses for my individual endpoint methods.
Is there a way to use a constant value as an #ApiResponses value, e.g. like:
#ApiResponses(value = MY_RESPONSES)
Am I missing something?
This unfortunately isn't possible using the Swagger annotations.
For this to work ApiResponse would have to be a normal class/interface rather than an annotation.