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

Adds support for top-level links objects #1018

Merged
merged 3 commits into from
Feb 8, 2016

Conversation

leandrocp
Copy link
Contributor

It's a generic object which can be used to declare a top-level links object as specified on jsonapi: http://jsonapi.org/format/#document-top-level

render json: @post, links: { self: "/posts/1" }

@beauby
Copy link
Contributor

beauby commented Jul 27, 2015

Great, would love to see it merged!

@joaomdmoura
Copy link
Member

@leandrocp indeed, I like the idea of having links support, but the proposal does not guarantee it will be a links object as specified on the docs.
As the docs guarantee a structure:

A link MUST be represented as either:

  • a string containing the link's URL.
  • an object ("link object") which can contain the following members:
    • href: a string containing the link's URL.
    • meta: a meta object containing non-standard meta-information about the link

I think the implementation could also enforce the format. There is a lot of different approaches to achieve it, we could do something serializer-wise, so you would setup the serializer instead of the render call. I'll ty to give it some thought, what about you, how do you feel about it?

@leandrocp
Copy link
Contributor Author

@joaomdmoura I'm ok with enforcing the link object. Remember that this PR only adds a link to the top level, although the spec includes a link object at four levels: root (with or without pagination links), resource (data), relationships and included.

{
  "links": {
  },
  "data": [{
    "relationships": {
      "author": {
        "links": {
        }
      },
    },
    "links": {
    }
  }],
  "included": [{
    "links": {
    }
  }
  }]
}

Do you think we should achieve all of them or just at root level for now ?

About the implementation, do you think it's good idea exposing a LinksObject class to the user ? Something like:

render json: @posts, links: LinksObject.new("http://example.com/posts")
{
  "links": {
    "self": "http://example.com/posts"
  },
  "data": []
}

Pagination data can also be included at this link object, which could be obtained from @posts if using kaminari (http://www.rubydoc.info/github/amatsuda/kaminari/Kaminari/PageScopeMethods), or could be informed by the user.

unless self.class == FlattenJson
include_meta(hash)
include_links(hash)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means we are also adding links to the usual JsonAdapter. Any particular reasons for that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, we should support only JsonApi. I'll update it.

@joaomdmoura
Copy link
Member

Hey @leandrocp,

So, I know there is another PR to implement pagination going on, I haven't checked it yet (might do it tonight). I would ask you to check it out, maybe see if we can make both work together. that would be awesome I know @bacarini (the creator of that PR) is looking forward to make it work.

I would do just the top level link for now, wen can implement the other ones in specific PRs.

Just read the docs for linked objects, and actually I don't think we should enforce the format anymore 😁 you was right.

We could re-build the links object to check on the include_links1 if it's aHash` that match the rules:

  • href: a string containing the link's URL.
  • meta: a meta object containing non-standard meta-information about the link

But I don't think it worths.
Check my comment on the code, and let's check the pagination PR that I mentioned above.
Great work! Keep it up! 😄

@joaomdmoura
Copy link
Member

Almost forgot... You might need to rebase it 😁

@leandrocp leandrocp force-pushed the add-top-level-links branch 2 times, most recently from f610f72 to 516d89b Compare August 6, 2015 12:58
@leandrocp
Copy link
Contributor Author

@joaomdmoura rebase and squash done!

You linked PR #104, I think the correct one is #1040 😄
There's also PR #1041 and #1028 which add support for Link. I'm looking these PR.

@leandrocp leandrocp force-pushed the add-top-level-links branch from b6c382f to 1dcb3b3 Compare August 6, 2015 14:18
@@ -19,3 +19,4 @@ test/version_tmp
tmp
*.swp
.ruby-version
tags
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this tags file? 😁

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joaomdmoura you don't use exuberant ctags? such win
@leandrocp You can put tags in your global gitignore if this change doesn't make it in :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bf4 good tip, I had forgotten about global gitignore.
@joaomdmoura do you prefer to remove this line from .gitignore ? (that's ok for me)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, I just checked it, seems that I should be using it as well. No blocking.

@joaomdmoura
Copy link
Member

This looks awesome, I'm willing to merge it, just made a few more comments, can you check it please?
And yeap, I meant #1041 😁

@bacarini bacarini mentioned this pull request Aug 10, 2015
@leandrocp
Copy link
Contributor Author

Sorry for the late response. I'll update this PR today.

Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' })
]

render json: @profiles, links: { self: "/profiles/1" }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any thoughts about relative vs. absolute links?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed on jsonapi PR it's best to use absolute links. I already updated this line.

@leandrocp
Copy link
Contributor Author

@joaomdmoura updated docs as suggested. Feel free to correct if needed.

@@ -105,7 +105,7 @@ def self.root_name
name.demodulize.underscore.sub(/_serializer$/, '') if name
end

attr_accessor :object, :root, :meta, :meta_key, :scope
attr_accessor :object, :root, :meta, :meta_key, :links, :scope
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joaomdmoura @kurko serializers are for the resource -> attributes mapping and adapters are for building the response object, right?

to ensure the links is an adapter-specific rendering option, it should really be passed to the adapter by adding :links to the list of ADAPTER_OPTION_KEYS at the top of SerializableResource. That way, I think, we can keep the responsibilities of the the response in the adapter, and continue to limit the serialization of a resource to the serializer.

I wrote similarly in #1041 (comment) that I think meta should also be moved to the adapter, and is here mostly due to how the library has changed from 0.8 to 0.9 to 0.10 and the evolution of the json-api spec.

Your thoughts? I think you can make this change mostly by deleting the code in the serializers that the adapter uses and just have the links passed directly to the adapter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bf4 thanks for clarification 👍

The way I did was to conform with meta already present on the code. But as you said, and I agree, it should be moved to the adapter. I'll update the code as you suggested.

Another issue is that both PR (mine and #1041) are defining the attribute links of the @hash

See here and here

What's the best way to avoid conflict ? Just check if links is already present and append to it ?

render json: @posts, pagination: true
{
  "data": [
    { }
  ],
  "links": {
    "first": "http://example.com/articles?page[number]=1&page[size]=1",
    "prev": "http://example.com/articles?page[number]=2&page[size]=1",
    "next": "http://example.com/articles?page[number]=4&page[size]=1",
    "last": "http://example.com/articles?page[number]=13&page[size]=1"
  }
}
render json: @posts,
           pagination: true,
           links: { 
               "self": "http://example.com/articles",
               "last": "http://example.com/articles/last"
           }
{
  "data": [
    { }
  ],
  "links": {
    "self": "http://example.com/articles",
    "first": "http://example.com/articles?page[number]=1&page[size]=1",
    "prev": "http://example.com/articles?page[number]=2&page[size]=1",
    "next": "http://example.com/articles?page[number]=4&page[size]=1",
    "last": "http://example.com/articles/last"
  }
}

See that I replaced last from links. Is that the expected output, or should I conserve attributes already defined ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @leandrocp, from my point of view the keys first, prev, next and lastshould be responsibility of pagination and no one should override it. Thus, the others keys won't be replaced because I am doing update. So, besides the keys above every key pass through controllers will continue.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, links is a top-level member of the json-api response object. Pagination is a usage of the links object. So, my thinking is that this PR adds 'links' and the pagination one uses it to add pagination data. Make sense?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

There is a method to add_links in #1041 , so a think we can use it, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it should behave like include_meta such that there is an include_links (which calls include_pagination)? I've written some more (please don't shoot me :) thinking about how to 'pass pagination' in through the 'links' adapter option in https://github.com/rails-api/active_model_serializers/pull/1041/files#r36998222

@joaomdmoura
Copy link
Member

Hey @leandrocp please check out my last comment on 1041

@bf4
Copy link
Member

bf4 commented Aug 23, 2015

Now that the [pagination links pr #1041][https://github.com//pull/1041) has been merged, we may want to rebase and repurposed this one to handle the non-pagination link scenarios and how they may interact with pagination links.

@leandrocp
Copy link
Contributor Author

Let´s get back to work! I'll rebase and move the code to json_api adapter

@leandrocp leandrocp force-pushed the add-top-level-links branch from e3d1668 to bd9696f Compare August 25, 2015 12:51
@leandrocp
Copy link
Contributor Author

@joaomdmoura @bf4 please, could you take a look at the last commit ? It´s a first version of what I think is the right way to handle top level links. And please tell me if I'm on the right direction 😄

@bf4
Copy link
Member

bf4 commented Aug 25, 2015

@leandrocp First review looks nice. Might help to rebase off of master and then squash all this to one commit so we can just look at the diff

@@ -13,6 +13,7 @@ This is the documentation of AMS, it's focused on the **0.10.x version.**

- [How to add root key](howto/add_root_key.md)
- [How to add pagination links](howto/add_pagination_links.md)
- [How to add top-level links](howto/add_top_level_links.md) (```JSON-API``` only)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO, How to add pagination links should be like

- [How to add top-level links](howto/add_top_level_links.md) (```JSON-API``` only)
  - [How to add pagination links](howto/add_pagination_links.md)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or better yet, just one 'add_links.md' page since they're the same thing more or less

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correction, add_top_level_links.md because individual objects may have links

@leandrocp
Copy link
Contributor Author

@bf4 @joaomdmoura the whole idea of Links is to be reusable on all possible contexts of jsonapi. This is a draft to be discussed.

@leandrocp
Copy link
Contributor Author

Yeah I was on a deadline here. Sorry for taking so long.

@leandrocp
Copy link
Contributor Author

The internal design of the serializer has improved, so I need to update the code to make test pass.

@beauby
Copy link
Contributor

beauby commented Sep 16, 2015

@leandrocp I did the JsonApi adapter refactor so don't hesitate if you have any question!

@bf4
Copy link
Member

bf4 commented Dec 23, 2015

@leandrocp are we still working on this? I can help you update it to master

@bf4
Copy link
Member

bf4 commented Dec 23, 2015

ref: #1235

@leandrocp leandrocp force-pushed the add-top-level-links branch 2 times, most recently from 437d75d to 6775bdd Compare December 23, 2015 12:09
@leandrocp
Copy link
Contributor Author

@bf4 updated to master and integrated with #1247. Please check if it's good to merge.

@kurko
Copy link
Member

kurko commented Jan 25, 2016

@leandrocp needs rebase

@kurko
Copy link
Member

kurko commented Jan 25, 2016

LGTM

@beauby beauby added the S: LGTM label Jan 25, 2016
@beauby
Copy link
Contributor

beauby commented Jan 25, 2016

LGTM as well, @leandrocp if you wouldn't mind rebasing one last time (I know – sorry 👯)!

http://jsonapi.org/format/#document-top-level

fix failing tests

support for top-level links limited to jsonapi adapter

Move docs from README to docs/ dir

move links to json-api adapter & create Links class to hold links data
update according rubocop rules
@leandrocp leandrocp force-pushed the add-top-level-links branch from 6775bdd to 37e4f1c Compare February 3, 2016 12:16
@leandrocp
Copy link
Contributor Author

Done!

ping @kurko @beauby

@@ -65,6 +65,8 @@ Features:
CollectionSerializer for clarity, add ActiveModelSerializers.config.collection_serializer (@bf4)
- [#1295](https://github.com/rails-api/active_model_serializers/pull/1295) Add config `serializer_lookup_enabled` that,
when disabled, requires serializers to explicitly specified. (@trek)
- [#1247](https://github.com/rails-api/active_model_serializers/pull/1247) Add top-level links (@beauby)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, Looks like you'll have to amend the changelog to stick this at the top of features in master since rc4 is out

@kurko
Copy link
Member

kurko commented Feb 4, 2016

@bf4 has good points. Once those are addressed, this is a +2

@bf4 bf4 merged commit b55fc32 into rails-api:master Feb 8, 2016
bf4 added a commit that referenced this pull request Feb 8, 2016
@bf4
Copy link
Member

bf4 commented Feb 8, 2016

Merged in b55fc32 addressed follow-up issues in 5b953ff

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

Successfully merging this pull request may close these issues.

8 participants