I have a problem getting Swagger to generate correct documentation for an API call that has an optional path parameter.
I'm building an API that peers into a hierarchical structure, similar to a file system. I want to call the same method to get the root structure as I do to get a sub resource. E.g:
Get the root: /folder
Get a sub folder: /folder/path/to
My Jax-rs method looks like this:
#GET #Path("folder{path:.*}")
Response folderContents(#ApiParam(value = "The folder to list", required = false) #PathParam("path") String path)
{...}
My method call works, but my swagger documentation is incorrect and doesn't work. Swagger-ui generates GET calls that look like this when I run it:
http://localhost:8080/storage-war/rest/filestore/folder{path:.*}
I'm looking for a way to either force Swagger to generate the correct signature or rebuild my regular expression so that my generated Swagger is correct.
Previously I'v tried using #Path("folder/{path:.}")*; his generated correct Swagger documentation but didn't match my no path given case. I've also tried #Path("/folder{p:/?}{path:(.)}")*; This produced a working method call but incorrect Swagger docs.
Is there a straightforward way to do what I'm looking for?
Edit:
In the end I created separate method calls for root and folders. Then I decorated the root call with it with #ApiOperation(hidden = true). This way I have an extra method in my code but only one method show up in my Swagger docs.
#GET #Path("folder/{path:.*}")
Response folderContents(#PathParam("path") String path)
{...}
#GET #Path("folder")
#ApiOperation(hidden = true)
Response rootContents()
{...}
In swagger, path parameters are always required. Understanding that in many frameworks and in practice they can be optional, but in the swagger definition they are required. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#fixed-fields-7
Related
I'm creating a RESTful service with Jersey (2.28) and use Apache Shiro for permission handling. So I used the buildin HttpMethodPermissionFilter which creates permissions like resource:read or resource:write. Now I have the problem that a user may only be allowed to read or write a specific resource and that I would need something like resource:write:<id> or resource:write:<name> or what ever as identifier.
I thought about extending the filter but at that point - even while I could access the body or the url - I have no idea how the data looks like.
Solutions I thought about:
Always pass a query parameter in the url, like /api/resource?id=xxx and if given apply that parameter for the permission string. But there is no way to tell if the parameter is required or not if both resource:read and resource:read:<id> exist. The filter might create a wrong permission for the given url. I could apply the filter only to urls where I know it must be the case, but seems all a bit wonky and error prone.
Remove the filter and ask for the permissions inside of the requested method.
#GET
#Path("/resource/{id}")
public Response getResource(#PathParam("id") String id) {
if(AuthorizationHandler.hasPermission("resource:read:" + id) {
return Response.status(Status.OK).entity("Resource GET works").build();
}
// return 403 or handle exception or ...
}
Somewhat like that, but it will leave me with exception handling in every method which also seems not much preferable. Maybe I could use an ExceptionMapper to handle responses... haven't tried that.
Does maybe someone else have another idea how to solve this efficently or maybe point me to an already existing solution? I'd prefere to use the #RequiresPermissions("resource:read") annotation (or a custom one), but could also define the urls / filters in the shiro.ini file /api/resource/** = noSessionCreation, jwtf, rest[resource] or I fallback to solution 2 if that's recommended.
Using JAX-RS, I have the following 3 #Paths.
#Path(JobRest.PATH)
#Api(value = JobRest.PATH, description = "REST APIs for Jobs")
public interface JobRest {
public static final String PATH = "/job";
#GET
#Path("/last")
#Produces(MediaType.APPLICATION_JSON)
public Job retrieveLastJob(...);
#GET
#Path("/{jobId}")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Job retrieveJob(...., #PathParam("jobId") String jobId, );
#GET
#Produces(MediaType.APPLICATION_JSON)
public JobList retrieveAllJobs(....);
}
/job correctly calls retrieveAllJobs()
/job/1236 correctly calls retrieveJob(..., "1236", ...).
I expected that /job/last would call retrieveLastJob(...), since it matches, but it calls retrieveJob(..., "last", ...) instead.
How do I change the notation so that /job/last will call retrieveLastJob(...)?
TL;DR
Remove the #Consumes(MediaType.APPLICATION_JSON) on the retrieveJob method. For one, it does not accept a body, so it does not consume anything. Secondly it conflicts with the expected behavior.
I've tested with both Jersey and RESTeasy and it seems to be a difference in implementation. Jersey works fine with your code, while RESTeasy always hits the retrieveJob method, as you are experiencing.
Here's my take. If you look at the JAX-RS spec; 3.7.2 Request Matching, there's a semi-cryptic algorithm for matching resources, that goes something like this.
Get all matching resource class (by path), put them into a set.
Get all matching resource methods (by path), put them into a set.
Sort the methods by best matching path (most literal characters go first).
Sort by media type (with consumes and produces).
From my perspective, in this particular case, after step 3, the retrieveLastJob should automatically win, as it has the most literal characters. The producing media types are the same, and the consumes media type should not even matter, since it is a GET request with no Content-Type to do any matching.
My guess it RESTeasy still uses the annotation to sort even though it should not even be taken into consideration in this case. So it would appear that the method with the annotation is given more precedence, as it appears to be more specific, by way of just having an annotation, while the other does not. But that (step 4) level of specificity really shouldn't matter in this case.
I don't know if it's a bug against the spec. It's not too clear on how it should be handled, but I personally think the Jersey behavior is the correct behavior, for the simple fact that this level of specificity should not matter in this particular case. In any case, it is not correct to have the #Consumes annotation anyway for a GET request with no body.
We are building a RESTful JAXRS web service for use with a Jersey HTTP client. We'd like to make heavy use of subresource locators to prevent cluttering code in single large source files. E.g. (minimal example):
#Path("places")
public class PlacesResource {
// Use the following to e.g. GET /places/123
#GET #Path("{id}")
#Produces("application/json")
public Response getPlace(#PathParam("id") int id) {
//...
return Response.ok(null).build();
}
// Use the following to e.g. GET /places/123/comments/42
#Path("{id}")
public PlaceResource place(#PathParam("id") int id) {
Place p = DAO.getInstance().getPlace(id);
return new PlaceResource(p); // singular (a different resource class)
}
}
This works fine. Removing either of these methods makes calls to resources as specified in the leading comments not work. (HTTP response 405: method not allowed)
However, while using this setup the following warning is printed to the Tomcat log:
[http-nio-8084-exec-6] org.glassfish.jersey.internal.Errors.logErrors The following warnings have been detected: WARNING: The resource (or sub resource) Resource{"{id}", 0 child resources, 5 resource methods, 1 sub-resource locator, 4 method handler classes, 0 method handler instances} with path "{id}" contains (sub) resource method(s) and sub resource locator. The resource cannot have both, methods and locator, defined on same path. The locator will be ignored.
It says the locator will be ignored, but it is very much working. What's wrong? By the way, I'd much prefer to be able to use the subresource locator even for path /places/{id}. It should just use the #GET-annotated method in the subresource class; this returns a 405 error code, as stated, though.
Yup, it's illegal. When you add a sub resource, it's responsible for managing its root path as well. So for the path /places/{id} the service wouldn't know which one to use (the method or the sub resource) since they both claim to manage that path. The sub resource locator is indeed ignored, but only for that one ambiguous path (/places/{id}). The path /places/{id}/other stuff is not ambiguous since it doesn't match the GET method's path, so it isn't ignored.
To remove the ambiguity, try modifying the sub resource to only match paths that specify the place ID and other path components. I don't have access to my IDE to test at the moment, but something like this should work:
// Use the following to e.g. GET /places/123/comments/42
#Path("{id}/{otherStuff: [a-zA-Z0-9_/]+}")
public PlaceResource place(#PathParam("id") int id) {
Place p = DAO.getInstance().getPlace(id);
return new PlaceResource(p); // singular (a different resource class)
}
Does anybody have a quick method to generate slugs and permalinks in Grails 1.3.7/2.0.0.RC1?
The main restriction: this method should work with non-latin characters.
Russian/bulgarian cirillic, deutsch umlauts etc...
Any suggestions ?
Grails 2.0.0.RC1
From the 2.0.0.RC1 docs:
Link Generation API
A general purpose LinkGenerator class is now available that is usable
anywhere within a Grails application and not just within the context
of a controller. For example if you need to generate links in a
service or an asynchronous background job outside the scope of a
request:
LinkGenerator grailsLinkGenerator
def generateLink() { grailsLinkGenerator.link(controller:"book", action:"list") }
Although it's not stated explicitly, I assume the reference to grailsLinkGenerator is obtained via dependency injection
Grails 1.3.7
You can use either the createLink or resource tags to generate links. If you're generating permalinks, I assume you'll want these to be absolute URLs. If so, you'll need to use either the absolute or base attribute when using these tags.
If you use the absolute attribute, be sure to set the value of grails.serverURL in Config.groovy
Link Permanence
The text above describes how to generate links to resources in a Grails application, but doesn't say anything about how to make these links permanent. AFAIK, the link to a resource will always remain the same as long as you don't change anything that is used in the URL mapping scheme (as defined in UrlMappings.groovy)
By default the URL mapping scheme uses
the resource's ID
the controller name
the action name
So if you never change these for the links of interest, you should be good.
As easy as:
title.replaceAll("[\\W]+", "-")
That makes it.
I have controller with
render(messages);
And i have route
GET / Application.index
I want to implement some rest features, and add this route
GET /api/index Application.index(format:'json')
I have template not found exception. How can i say to play use renderJSON() when format is json without any code changes?
As you use the render() method, Play! will search a template file with the name of the action (detail on Play! website : http://www.playframework.org/documentation/1.2.2/controllers#template).
You have to use renderJSON(params...), it will bypass the default template!
Your use case doesn't really make sense. In the standard render() call, you are likely passing some pojos to the template to use (or none at all), this is a varargs method. In the renderJSON() call you always need to pass an object which can be serialized to json by the Gson library, or a string with is already in json.
Add a new method to your Application class that handles json responses:
# normal index page
GET / Application.index
# api request
GET /api/index Application.indexJson