rest - hateoas interceptor

46 views
Skip to first unread message

Andrew Eisenberg

unread,
Jul 15, 2013, 11:39:34 PM7/15/13
to cuj...@googlegroups.com
Hi Scott (and others),

I'm finally working on a project where I get to use cujo again, so I am happy. :-)

I am using the hateoas interceptor for the rest library and there is some surprising behavior.  I figured out why this is happening, but only after looking at the implementation. 

So, let's say I want to poll the server. I start by grabbing the root resource and accessing a link through a setInterval call:

    var client = rest.chain(mime).chain(hateoas);
    client('/').then(function(response) {
         var link = response.entity._links.streams;
         setInterval(function() {
            link.then(function(response2) {
                console.log('response: '+ response2);
            });
        }, 1000);
    });

This will not work since accessing the streams property creates a new promise and calling then() on it will only access the same fulfillment each time.  So, the server is only pinged once.

However, the following does indeed work to ping the server every second:

    var client = rest.chain(mime).chain(hateoas);
    client('/').then(function(response) {
         var links = response.entity._links;
         setInterval(function() {
            links.streams.then(function(response2) {
                console.log('response: '+ response2);
            });
        }, 1000);
    });

This is because you are using a javascript getter to ensure that each time the streams property is accessed, a new promise is 'gotten'.

This feels unintuitive to me and I was indeed confused for a while.  For me, instead of making streams a property with a getter attached to it, it would be more intuitive if streams were a function that otherwise behaved the same way.

ie- invoking response.entity._links.streams() would produce a new promise on each invocation.

regards,
Andrew

ps- looks like rest is not one of the allowed tags, so just using the 'any' tag.


Scott Andrews

unread,
Jul 16, 2013, 12:40:36 AM7/16/13
to cuj...@googlegroups.com
Hey Andrew,

I debated that behavior when implementing the interceptor. I agree it's somewhat strange that multiple property accesses return different values. I left it that way as a request could fail, or grow stale.

I'm tempted to convert the getter to always return the same promise for multiple accesses, if you need to retry the request, you can get a client for the relationship via the .clientFor(rel) method on the host object.

I'm also growing to hate '_links' as the default property. Any opinion on making the default value '', the actually entity object?

-Scott

Sent from my iPad


--
You received this message because you are subscribed to the Google Groups "cujojs" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cujojs+un...@googlegroups.com.
To post to this group, send email to cuj...@googlegroups.com.
Visit this group at http://groups.google.com/group/cujojs.
To view this discussion on the web visit https://groups.google.com/d/msgid/cujojs/477b1497-e134-4592-a5c8-883ada864363%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Brian Cavalier

unread,
Jul 16, 2013, 6:53:02 AM7/16/13
to cuj...@googlegroups.com
Hey Andrew!  I'll let Scott handle the rest stuff, but just wanted to say thanks for the heads-up about the tags.  I just added 'rest' to the available tags for the group.

Andrew Eisenberg

unread,
Jul 16, 2013, 11:56:06 AM7/16/13
to cuj...@googlegroups.com
The way I'm intending on using the links is to grab all of them when
the page loads and cache them. I guess you're saying I should be using
'clientFor' in this case. That does clear things up. Thanks for the
clarification.

And for my original question, I'd go for consistency on property vs
variable access on the links. In my examples above, I'd want to see
both links.streams and link refer to the same thing on each access. I
would have liked to see a function that returns a new promise each
time, but just using a single link that returns a promise when
accessed might work too.

And your third comment, using the _links property does seem odd, like
I'm accessing an internal property or something, but I'm not really
sure what you're suggesting.
> You received this message because you are subscribed to a topic in the
> Google Groups "cujojs" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/cujojs/xUSPDAp30ps/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> cujojs+un...@googlegroups.com.
> To post to this group, send email to cuj...@googlegroups.com.
> Visit this group at http://groups.google.com/group/cujojs.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/cujojs/1373949636141.bed89c5c%40Nodemailer.

Scott Andrews

unread,
Jul 16, 2013, 2:46:02 PM7/16/13
to cuj...@googlegroups.com
On Tue, Jul 16, 2013 at 11:56 AM, Andrew Eisenberg <aeise...@gopivotal.com> wrote:
The way I'm intending on using the links is to grab all of them when
the page loads and cache them. I guess you're saying I should be using
'clientFor' in this case. That does clear things up.  Thanks for the
clarification.

It sounds like what you were doing is fine, unless you need to expire the cache and fetch a fresh version of the resource.  clientFor() is most commonly used if you need to make a non-GET request, or need to add interceptors to the client.

And for my original question, I'd go for consistency on property vs
variable access on the links.  In my examples above, I'd want to see
both links.streams and link refer to the same thing on each access.  I
would have liked to see a function that returns a new promise each
time, but just using a single link that returns a promise when
accessed might work too.

Sounds fair, I'll make this change.

And your third comment, using the _links property does seem odd, like
I'm accessing an internal property or something, but I'm not really
sure what you're suggesting.

Suppose we have the entity:
{
  foo: 'bar',
  links: [{ rel: 'next', href: '...' }]
}

By dedault today, the object graph you will get back is:

{
  foo: 'bar',
  _links: {
    next: <promise for the href>,
    nextLink: { rel: 'next', href: '...' }
  },
  links: [{ rel: 'next', href: '...' }]
}

What I'm proposing is that we switch the default graph to:

{
  foo: 'bar',
  next: <promise for the href>,
  nextLink: { rel: 'next', href: '...' },
  links: [{ rel: 'next', href: '...' }]
}

If you want to keep the indexed links independent of host object, you can use the 'target' config property the same as today.  FYI, the behavior I describe is already possible if you explicitly set the target value to an empty string.

-Scott

 

Scott Andrews

unread,
Jul 23, 2013, 10:57:39 AM7/23/13
to cuj...@googlegroups.com
Andrew,

I made these changes on the rest.js dev branch.  Feel free to take that for a spin and let me know.

-Scott

Andrew Eisenberg

unread,
Jul 23, 2013, 11:54:40 AM7/23/13
to cuj...@googlegroups.com

Andrew Eisenberg

unread,
Jul 23, 2013, 7:33:07 PM7/23/13
to cuj...@googlegroups.com
This change is a bit more intuitive to me. Now, at least the accessor
functions are more what I expect.

However, I have another question about the returned response object.
The response object has quite a few properties and some of them seem
like duplicates. For example, say you have a link called 'jobs'. The
response object will have a response.jobs getter that returns the
promise, a response.jobsLink with href and rel properties. Also,
there is response.links[0] which seems to be a copy of
response.jobsLink.

I'm guessing that response.jobsLink is meant to be private and used
for the construction of the getter in response.jobs. Could you not
remove response.jobsLink and have the getter use the object stored in
response.links[0]?

On Tue, Jul 23, 2013 at 8:54 AM, Andrew Eisenberg

Scott Andrews

unread,
Jul 23, 2013, 9:36:18 PM7/23/13
to cuj...@googlegroups.com
The short answer is that this behavior hasn't changed.

The links array is from the response sent across the wire, we don't change it.  You're correct that response.jobsLink is the same as response.links[0].  The difference is that there is no order to the items in the array.  We make it easier to find the relationship by indexing the value of the relationship based on the rel; it's a non-enumerable convenience, nothing more.

-Scott



Reply all
Reply to author
Forward
0 new messages