How to get the URI of a resource in Grails? - rest

A very basic question. I want to provide URIs for some objects in my application. For example, a resource is available at:
http://localhost:8080/myapp/user/1
However, I'd like to serialize such a User object. This serialization should contain the public URI of the object itself. So for example, the model has a new method serializeToSomething, which would serialize:
id: 1
username: JohnDoe
email: johndoe#example.com
publicURI: http://localhost:8080/myapp/user/1
How can I let the model instance know of its URL?
Some notes:
This has to happen within the scope of the model, controller or service, and not within the view. Also I don't want to hardcode this.

See related question Can I use grails tag outside of GSP?
Basically you can use g.createLink in a controller or service, and it will return a string. So you can do something like:
def uri = g.createLink(controller: 'user', action: 'show', id: user.id, absolute: true)

Personally I ended up serializing 3 things: controller name, action name (typically "show") and id. Then I used these three in g.createLink when displaying deserialized objects in a View.
Sure, this won't work if you need deserialized object for external usage.

You can get the url in a controller using
request.forwardURI
A similar question has been asked before

Related

How to use DiscoveredResource to traverse to a single entity resource exposed by a RepositoryRestResource

I'm trying to set up a system with multiple applications connecting by use of a discovery server. I can traverse the hal responses to a specific resource, but I'm looking for a solution to get from a collection resource to a single resource and find the data for a specific entity.
In 1 application I have a RepositoryRestResource exposing some object:
#RestRepositoryResource(collectionResourceRel="things", itemResourceRel="thing") public interface ThingRepo extends CrudRepository<Thing,Long> {}
In some other application, I would like to get to a single thing. I have the id (let's say it's 1) and have the relation name of the collection and the single resource.
I would like to use a DiscoveredResource to get a link to this single item resource, or to the collection resource which I can then somehow expand using the ID (which would require a templated resource).
If at all possible I would not like to just add "/1" at the end of the URL.
this is how I currently create a DiscoveredResource to point to the collection resource:
new DiscoveredResource(new DynamicServiceInstanceProvider(discoveryClient, traverson -> traverson.follow("things"));
Should I and is it possible to add a templated link on a collection resource created by a #RepositoryRestResource. Or is there some other trick I am missing?
The solution here is to add a custom method as a #RestResource which exposes a relation with a templates URL you can then follow to.
Repo:
#RestRepositoryResource(collectionResourceRel="things", itemResourceRel="thing") public interface ThingRepo extends CrudRepository<Thing,Long> {
#RestResource(rel = "thing")
Thing findOneById(#Param("id") Long id);
}
Discovery + traverson:
DiscoveredResource resource = new DiscoveredResource(new DynamicServiceInstanceProvider(discoveryClient, traverson -> traverson.follow("things","search","thing"));
Link link = resource.getLink().expand(id);

Restful API for Templating

I am struggling with a design aspect of my restful api for templating collections of resources.
The endpoint calls for a json with the name to a particular template and a collections of tokens. The API will then create entries into numerous tables and use the tokens where appropriate.
A very simple example is:
*{
'template': 'DeviceTemplate'
'tokens': [
'customer': 1234,
'serial_number': '12312RF3242a',
'ip_address': '1.1.1.1'
]
}*
This creates a new device for the customer with that ip address along with several other objects, for instance interfaces, device users etc etc. I use the tokens in various places where needed.
I'm not sure how to make this endpoint restful.
The endpoint for /device is already taken if you want to create this resource individually. The endpoint I need is for creating everything via the template.
I want to reserve the POST /template endpoint for creating the actual template itself and not for implementing it with the tokens to create the various objects.
I want to know how to call the endpoint without using a verbs.
I also want to know if its a good idea to structure a POST with a nested JSON.
I'd suggest that you create an action on the template object itself. So right now if you do /templates/<id> you are given an object. You should include in that object a url endpoint for instantiating an instance of that template. Github follows a scheme that I like a lot [1] where within an object there will be a key pointing to another url with a suffix _url. So for instance, your response could be something like:
{
"template": "DeviceTemplate",
"id": "127",
...
"create_url": "https://yourdomain.com/templates/127/create",
...
}
Then this way you treat a POST to that endpoint the same as if this template (DeviceTemplate) was its own resource.
The way to conceptualize this is you're calling a method on an object instead of calling a function.
[1] - For example https://developer.github.com/v3/#failed-login-limit

can Shapeless a good resource for extend properties in a request object finagle Service

I'm coming from clojure/pedestal and something than I like is could extend the properties in a context object, context are a bit similar to middlewares and filter
intercep 1 int 2
--> context -----------> context --------> context ---> controller
request request request
user: "tom" user:"tom"
admin : true
the initial context includes the request and
in every interceptor I add new data to the context, for instance check the session and get the user-details based in its cookies (int 1) or get if he's admin (inter 2)...when the data come to the controller it includes useful information
I know the static limitation but I think than maybe it's possible using shapeless..for instance, in my finatra controller I would like something like...
type SuperRequest = ......
maybe would need some implicit heres
get("/hi") { request: SuperRequest =>
info("hi")
"Hello!! " + request.myHlist.get("name")
}
I think than the benefit would be a better composition because object doesnt compose well
.filter(filter1) //without HList I'd need create a new SuperRequest here with the user name
.filter(filter2) //and here other new object with the admin details
could be it a good approach? do you know a better alternative for extends the object in a composable way...

Sail.js - routing to methods, custom policies & PATCH method

I have a few questions that I couldn't find answers anywhere online.
Does sails.js framework support HTTP PATCH method? If not - does anyone know if there is a planned feature in the future?
By default if I create method in a controller it is accessible with GET request is it the routes.js file where I need to specify that method is accessible only via POST or other type of methods?
How would you create a policy that would allow to change protected fields on entity only for specific rights having users. I.e: user that created entity can change "name", "description" fields but would not be able to change "comments" array unless user is ADMIN?
How would you add a custom header to "find" method which specifies how many items there are in database? I.e.: I have /api/posts/ and I do query for finding specific items {skip: 20; limit: 20} I would like to get response with those items and total count of items that would match query without SKIP and LIMIT modifiers. One thing that comes to my mind is that a policy that adds that that custom header would be a good choice but maybe there is a better one.
Is there any way to write a middle-ware that would be executed just before sending response to the client. I.e.: I just want to filter output JSON not to containt some values or add my own without touching the controller method.
Thank you in advance
I can help with 2 and 5. In my own experience, here is what I have done:
2) I usually just check req.method in the controller. If it's not a method I want to support, I respond with a 404 page. For example:
module.exports = {
myAction: function(req, res){
if (req.method != 'POST')
return res.notFound();
// Desired controller action logic here
}
}
5) I create services in api/services when I want to do this. You define functions in a service that accept callbacks as arguments so that you can then send your response from the controller after the service function finishes executing. You can access any service by the name of the file. For example, if I had MyService.js in api/services, and I needed it to work with the request body, I would add a function to it like this:
exports.myServiceFunction = function(requestBody, callback){
// Work with the request body and data access here to create
// data to give back to the controller
callback(data);
};
Then, I can use this service from the controller like so:
module.exports = {
myAction: function(req, res){
MyService.myServiceFunction(req.body, function(data){
res.json(data);
});
}
}
In your case, the data that the service sends back to the controller through the callback would be the filtered JSON.
I'm sorry I can't answer your other questions, but I hope this helps a bit. I'm still new to Sails.js and am constantly learning new things, so others might have better suggestions. Still, I hope I have answered two of your questions.

ServiceStack Routing with ravendb ids

I've an entity with an ID of
public string ID {get;set;}
activities/1
(which comes from RavenDB).
I'm registering the following routes in my ServiceStack AppHost
Routes
.Add<Activity>("/activities")
.Add<Activity("/activities/{id}");
I'm using a backbone app to POST and PUT to my REST Service.
What happens out-of-the-box:
id property is serialized into the json as "activities/1"
id property is encoded into route as "activities%2F1"
ServiceStack gives precedence to the URL based id property, so my string gets the encoded value which is no use to RavenDb directly.
The options I'm aware of:
Change backbone to post to "/activities" and let the JSON Serialiser kick in
Change RavenDb ID generation to use hyphens rather than slashes
Make my Id property parse for the encoded %2F on set and convert to a slash
Both have disadvantages in that I either lose RESTfulness in my API, which is undesirable, or I don't follow RavenDb conventions, which are usually sensible out-of-the-fox. Also, I've a personal preference for having slashes.
So I'm wondering if there are any other options in servicestack that I could use to sort this issue that involve less compromise? Either Serialiser customisation or wildcard routing are in my head....
I have the same problem with ASP.Net WebAPI, so I don't think this is so much a ServiceStack issue, but just a general concern with dealing with Raven style id's on a REST URL.
For example, let's say I query GET: /api/users and return a result like:
[{
Id:"users/1",
Name:"John"
},
{
Id:"users/2",
Name:"Mary"
}]
Now I want to get a specific user. If I follow pure REST approach, the Id would be gathered from this document, and then I would pass it in the id part of the url. The problem here is that this ends up looking like GET: /api/users/users/1 which is not just confusing, but the slash gets in the way of how WebAPI (and ServiceStack) route url parameters to action methods.
The compromise I made was to treat the id as an integer from the URL's perspective only. So the client calls GET: /api/users/1, and I define my method as public User Get(int id).
The cool part is that Raven's session.Load(id) has overloads that take either the full string form, or the integer form, so you don't have to translate most of the time.
If you DO find yourself needing to translate the id, you can use this extension method:
public static string GetStringIdFor<T>(this IDocumentSession session, int id)
{
var c = session.Advanced.DocumentStore.Conventions;
return c.FindFullDocumentKeyFromNonStringIdentifier(id, typeof (T), false);
}
Calling it is simple as session.GetStringIdFor<User>(id). I usually only have to translate manually if I'm doing something with the id other than immediately loading a document.
I understand that by translating the ids like this, that I'm breaking some REST purist conventions, but I think this is reasonable given the circumstances. I'd be interested in any alternative approaches anyone comes up with.
I had this problem when trying out Durandal JS with RavenDB.
My workaround was to change the URL very slightly to get it to work. So in your example:
GET /api/users/users/1
Became
GET /api/users/?id=users/1
From jQuery, this becomes:
var vm = {};
vm.users = [];
$.get("/api/users/?" + $.param( { id: "users/1" })
.done(function(data) {
vm.users = data;
});