Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot access subresources in generated javascript client without top-level getter. (was: How to use nested api with javascript client?) #96

Open
ryskajakub opened this issue Dec 16, 2014 · 19 comments
Labels

Comments

@ryskajakub
Copy link
Contributor

Hi,

it's not clear for me, how to use the javascript generated client with a nested resource. Calling the top-level is easy:

var api = new MyApi("/api");
api.TopLevel.list(function (x) {doSomething(x);});

How do I call the nested resource?
Let's say I have this schema /toplevel/<id>/resource/.
Calling that resource with curl, for example /api/v1.0.0/toplevel/5/resource/ works, but I don't know how to call it with the generated client. This code doesn't work:

var api = new MyAPi("/api");
var topLevel = api.TopLevel("/5");
topLevel.Resource.create(/* new json item */);

Any help appreciated :-)

@hesselink
Copy link
Member

Off the top of my head, I think it depends on how you defined your resource. If it has a named identifier, it should be something like api.TopLevel.byFoo(5).Resource.list(). If it has an unnamed identifier, it would be api.TopLevel(5).Resource.list(). Please let me know if that works.

@ryskajakub
Copy link
Contributor Author

@hesselink I use unnamedSingle and api.TopLevel(5) already throws an exception.

undefined is not a function
at return TopLevel.access(url, secureUrl, modifyRequest);

@hesselink
Copy link
Member

That's strange. I just tested with an unnamedSingle in our code base, and the code I gave seems to work. Can you share (parts of) your actual code? Also, what version of rest-gen did you use to generate the client?

@ryskajakub
Copy link
Contributor Author

Sure.
I use rest-gen-0.16.1.3,
It looks like the JQuery version does not affect it, because it fails before the request is made.
The Router looks like this

root `compose` ((route topLevelRes) `compose` (route nestedRes))

TopLevel schema and nested Schema respectively looks like this:

import Safe (readMay)
topLevelS = withListing () $ unnamedSingle readMay
nestedS = noListing $ named []

So, to summarize, my api is running at /api and this code doesn't work:

var myApi = new MyApi("/api")
myApi.TopLevel.list() // this is ok
myApi.TopLevel(5).Nested
Uncaught TypeError: undefined is not a function

@hesselink
Copy link
Member

Sorry for not coming back to you sooner. I changed one of the endpoints (Post) in rest-example to have an unnamed getter. The schema now looks like this:

  , R.schema = withListing () $ unnamedSingleRead ById

I could then run the following code from node, and get a result:

var Api = require("./out/api.js");
var api = new Api("http://localhost:3000");
api.Post(1).get().then(function (x) { console.log("got it", x); })

So that clears up the javascript API. The question is why it doesn't work for you. Perhaps you haven't defined a getter? The resource record should have a get property that is Just handler for a get function to be generated in the API.

If that's not it, I'm not sure what else to do. Perhaps you could provide a complete example that doesn't work, that I can compile and run?

@hesselink
Copy link
Member

Looking at your code again, I think myApi.TopLevel(5).get() should work, but I'm not sure what you expect after that, since Nested doesn't have a listing or a single getter, so there's nothing you can do with it. If you did have e.g. a listing,myApi.TopLevel(5).Nested.list() should work. On the example api that I changed, api.Post(1).Comment.list() works.

@ryskajakub
Copy link
Contributor Author

  1. In which commit is that? I have this one: cce989b - from today and the the schema field looks like this:
, R.schema = withListing () $ named [("id", singleRead ById), ("latest", single Latest)]
  1. In my setting, I want to do a POST request, by calling create on the TopLevel resource. So that listing-and-getterless schema should be fine. The problem is that I couldn't get over the TopLevel single resource identification yet = I couldn't get over the point, where code like myApi.TopLevel(5) wouldn't throw an exception. Sorry that my examples are kind of confusing :-/

@hesselink
Copy link
Member

  1. Sorry, that was unclear. I just changed it locally to test, I didn't commit anything. The one schema line is actually the whole diff, if you want to try it.

  2. Hmm, the TopLevel access should work as I wrote above. Perhaps it's because of the way you're composing the API: I just noticed you're associating compose differently than we do. I'd write root -/ route topLevelRes --/ route nestedRes, which associates as

(root `compose` route topLevelRes) `compose` route nestedRes

Could you see if that fixes the issue?

@ryskajakub
Copy link
Contributor Author

  1. OK, I'm going to try it.

  2. In the latest uploaded rest-api, the operators you mentioned are defined in this way:

    (-/) :: Router m s -> Router s t -> Router m s | infixl 4 |
    (--/) :: Router m s -> Router s t -> Router m s | infixl 5 |

My reasoning is that in root -/ route topLevelRes --/ route nestedRes the --/ operator has higher precedence than the -/ and so the implicit parens are like this: rootcompose(route topLevelRescomposeroute nestedRes).
Having the parens the other way cause the code to not typecheck for me.

@hesselink
Copy link
Member

  1. You're right, I was confused, sorry.

@ryskajakub
Copy link
Contributor Author

  1. Ok, so now I now more about what's going on. If I don't specify handler for Resource's get, then the access function is not generated on the client. Calling api.TopLevel(1) thus tries to call the access on TopLevel which throws an exception.

What is the reasoning behind that? Are you trying to verify that the resource with such id exist before allowing the user to call the nested apis?

In my opinion, the user should be allowed to access all the nested resources despite the fact that the get isn't generated, anyway he can do so, since the corresponding /toplevel/<id>/nested/ resource is accepting requests, it only cannot be done with the generated javascript code, but he must do it with handwritten jQuery.ajax for example.

@hesselink
Copy link
Member

Ah, so if I understand correctly, if you have a resource without a getter, you cannot access any subresources using the generated Javascript client? Is that correct? That does sound like a bug.

@ryskajakub
Copy link
Contributor Author

Yes, you can reproduce the behaviour on the rest-example.

@hesselink
Copy link
Member

(As an aside, I'm having trouble seeing how you would use this in the real world: if you do have a listing, then you might as well have a single getter. If you don't have a listing, how will a user know which resources exist?)

@hesselink hesselink changed the title How to use nested api with javascript client? Cannot access subresources in generated javascript client without top-level getter. (was: How to use nested api with javascript client?) Dec 22, 2014
@ryskajakub
Copy link
Contributor Author

I was actually planning to add the the single getter in future, so knowing that adding the getter would fix the problem would be sufficient for me, that's right.

@hesselink
Copy link
Member

Great, then I'll leave this one open to fix it eventually. Thanks for helping figure this one out!

@bergmark
Copy link
Member

bergmark commented May 2, 2015

You also cannot remove a resources that doesn't have a getter.

@bergmark bergmark added the bug label May 2, 2015
@hesselink
Copy link
Member

What do you mean exactly? Remove what? The subresource or the resource itself? And in the API, or only in the generated javascript?

@bergmark
Copy link
Member

bergmark commented May 2, 2015

You cannot remove the resource itself with the generated JS. Haskell client is ok.

It generates e.g. Foo.prototype.remove which you can never construct without a getter. Updates are okay, they are Foo.saveBy....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants