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

Pagination Links #1041

Merged
merged 20 commits into from
Aug 22, 2015
Merged

Pagination Links #1041

merged 20 commits into from
Aug 22, 2015

Conversation

bacarini
Copy link
Contributor

@bacarini bacarini commented Aug 5, 2015

I would like to discuss on how we should display pagination links on AMS.

I think you can relie on kaminariand will_paginategems to do so. Having said that, I created a class to build links.

related issue

@bacarini bacarini mentioned this pull request Aug 5, 2015
If you want pagination links in your response, specify it in the `render`

```ruby
render json: @posts, pagination: true
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for this. Did you try subclassing the ArraySerializer? e.g. https://github.com/rails-api/active_model_serializers#2-for-an-array-resource and just setting the meta? Have you seen pagination gems such as https://github.com/davidcelis/api-pagination ?

Copy link
Member

Choose a reason for hiding this comment

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

Great! @bf4 about the meta thing, it would work, but it would not follow json-api conventions.

@bacarini I think the README might not be the best place for describing this feature, mostly because it's to a particular adapter (json-api) and also because would be awesome to have some examples.
We are trying to consolidate our new docs, would be great if instead of adding this to the README you could open a new article there with this PR describing the feature, some examples and some of the gems that ppl can use in order to make it work.

@bacarini
Copy link
Contributor Author

bacarini commented Aug 6, 2015

Hi @bf4,

Actually, I got same ideas from api-pagination gem. I used to use it, but It does not apply to JSON-API specification. That gem builds links on headers, like below.

screen shot 2015-08-06 at 11 15 42 am

I want to insert pagination into the response body. At least, I understood that from JSON-API. Is it right?

@bf4
Copy link
Member

bf4 commented Aug 6, 2015

Hmm.. well, it should subclass the ArraySerializer for sure, but besides testing that we can set meta data in a collection, I'm not sure it's the job of this library to expose anything more than the meta option for any pagination libraries to use.

@bf4
Copy link
Member

bf4 commented Aug 6, 2015

My bad, it should be in a links object, not meta http://jsonapi.org/format/#fetching-pagination

Pagination

A server MAY choose to limit the number of resources returned in a response to a subset ("page") of the whole set available.

A server MAY provide links to traverse a paginated data set ("pagination links").

Pagination links MUST appear in the links object that corresponds to a collection. To paginate the primary data, supply pagination links in the top-level links object. To paginate an included collection returned in a compound document, supply pagination links in the corresponding links object.

The following keys MUST be used for pagination links:

  • first: the first page of data
  • last: the last page of data
  • prev: the previous page of data
  • next: the next page of data

The page query parameter is reserved for pagination. Servers and clients SHOULD use this key for pagination operations.
For example, a page-based strategy might use query parameters such as page[number] and page[size], an offset-based strategy might use page[offset] and page[limit], while a cursor-based strategy might use page[cursor].

"links": {
  "self": "http://example.com/posts",
  "first": "http://example.com/posts?page[number]=1",
}

@bf4
Copy link
Member

bf4 commented Aug 6, 2015

I'm happy to pair with you to work on the links object. (We'll need tests, too, and might want to start with a PR to the json-api/example page to make sure we're getting it right)

@bacarini
Copy link
Contributor Author

bacarini commented Aug 6, 2015

great... So, I will work on it. Should we edit this page example or create another one?

@bf4
Copy link
Member

bf4 commented Aug 7, 2015

add more examples to it.. see e.g. json-api/json-api#828 (error objects) which is a result of me working on error objects here in AMS #1004


include_meta(hash)
include_pagination_links(hash) if options && options[:pagination]
end
Copy link
Member

Choose a reason for hiding this comment

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

I think these changes should be in the json_api adapter, no/

@bf4
Copy link
Member

bf4 commented Aug 7, 2015

Don't forget to add tests! They'll be required before this gets merged, so better now than later, right?

@bacarini
Copy link
Contributor Author

bacarini commented Aug 7, 2015

Hey @bf4, I've changed a few things according to your comments. I will add more test later on.

Meanwhile, it would be great some reviews on the new code.

tks

hash = serializable_hash(options)
include_meta(hash) unless self.class == FlattenJson
hash
serializable_hash(options).tap do |hash|
Copy link
Member

Choose a reason for hiding this comment

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

👍

Copy link
Member

Choose a reason for hiding this comment

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

@joaomdmoura OT: Might it make more sense for the FlattenJson adapter to implement a no-op include_meta so the superclass doesn't need to know about the subclass?

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 I couldn't agree more with you about this approach. I will be happy to work on it, but in a different PR.

Copy link
Member

Choose a reason for hiding this comment

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

Did it in #1048 because

Copy link
Member

Choose a reason for hiding this comment

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

#1048 Merged already.

@joaomdmoura
Copy link
Member

Great work @bacarini. Btw awesome PR on json-api as well @bf4 !

Don't forget to add tests! They'll be required before this gets merged, so better now than later, right?

👍 Indeed I see you added a test, would be great to have one covering the action_controller flow. You can check how we have being doing it here

@bacarini bacarini changed the title WIP - Pagination Links Pagination Links Aug 8, 2015
@bacarini
Copy link
Contributor Author

bacarini commented Aug 8, 2015

@joaomdmoura @bf4, I added test to pagination feature and also included it to /doc.

I've exchanged test/action_controller/json_api_linked_test.rb to a new different directory, test/action_controller/json_api/linked_test.rb. In my opinion, it is clearer and more organized. What do you think guys?

I've also opened a PR on json-api to make sure we're getting it right, as @bf4 said.

@enriikke
Copy link

enriikke commented Aug 9, 2015

This is awesome! Thanks for the work on this @bacarini 💥 👍 😄

I would like to make a suggestion. Sometimes pagination is tied to a search or filtered query. Looking at the docs, the returned pagination links look like ?page=1&per_page=1 so any other parameter that was sent with the request is not returned.

This might be up to individual interpretation but the way the pagination links are returned will require the client to take additional steps before it can use them (i.e. prefixing the api endpoint, adding filters or search query).

I'm suggesting that pagination links should be returned in a way that no additional processing is needed by the client. I'm working on a project using JSON API that needed pagination links and this is similar to how I approached it:

url = request.original_url.sub(/\?.*$/, "")
params = request.query_parameters.merge(page: { number: page, size: size }).to_param

"#{url}?#{params}"

In my case, I'm using a page hash as suggested in this note http://jsonapi.org/format/#fetching-pagination:

Note: JSON API is agnostic about the pagination strategy used by a server. Effective pagination
strategies include (but are not limited to): page-based, offset-based, and cursor-based. The page query
parameter can be used as a basis for any of these strategies. For example, a page-based strategy
might use query parameters such as page[number] and page[size], an offset-based strategy might use
page[offset] and page[limit], while a cursor-based strategy might use page[cursor].

In any case, this is great. I like that this gives the ability to add a root links object without overriding meta. I will be happy to assist in any way I can 🔨

@bacarini
Copy link
Contributor Author

According to a comment on json-api PR, I added complete URIs, not just query strings.

It will accept link/self like this PR or the original request url.

@enriikke, I can see your point, but what do you think to discuss it on json-api PR?

@bacarini bacarini force-pushed the master branch 2 times, most recently from 018b71c to 9dbf7c3 Compare August 10, 2015 14:22
@bacarini
Copy link
Contributor Author

@enriikke may bad, I misunderstood your previous comment.

I added new code to merge with urls parameters that already exists.

@enriikke
Copy link

Awesome @bacarini I'm looking forward to this PR being merged 👍 😃

Pagination links will be included in your response automatically as long
as the resource is paginated using Kaminari or WillPaginate
and if you are using a JSON-API adapter. The others adapters does not have this feature.
@bacarini
Copy link
Contributor Author

@bf4 no worries! 😃

I`ve changed a few things, could you review?
tks a lot 👍

hash
serializable_hash(options).tap do |hash|
include_meta(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 should go away when you rebase, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

gone!

@bacarini
Copy link
Contributor Author

@joaomdmoura and @bf4 updated docs as suggested.

assert_equal expected_links, response['links']
end

def test_render_only_prev_and_first_pagination_links_with_additional_params
Copy link
Member

Choose a reason for hiding this comment

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

👍

@joaomdmoura
Copy link
Member

LGTM 👍 only this still open.
@bf4 I know you are deeply involved in the review of this one so I'll also wit a LGTM from you before merge.

}
```

AMS relies on either [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate). Please install either dependency by adding one of those to your Gemfile.
Copy link
Member

Choose a reason for hiding this comment

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

nitpick: AMS relies on either Kaminari or WillPaginate for pagination.

alternative to consider: AMS pagination relies on a paginated collection with the methods current_page, total_pages, and size, such as are supported by both Kaminari or WillPaginate.

(links left out for ease of me typing :)

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
Copy link
Member

bf4 commented Aug 20, 2015

@joaomdmoura looks good to me. The interface is solid and the tests cover the usage well. I'd like to iterate on it a bit, but we can do that in another PR. @bacarini Has put in a lot of work and it will be the basis for more like it, I am sure! 👍 all around!

@bf4
Copy link
Member

bf4 commented Aug 20, 2015

I don't think it necessarily needs to be rebased since I think the commits tell a good story.

@bf4
Copy link
Member

bf4 commented Aug 20, 2015

After this is merged, I think #1018 will need to be repurposed to handle the non-pagination link scenarios and how they may interact with pagination links.

@kalys
Copy link

kalys commented Aug 22, 2015

@bf4 Hi. When are you going to merge this PR?

@joaomdmoura
Copy link
Member

Awesome work @bacarini . I'me merging this.

joaomdmoura added a commit that referenced this pull request Aug 22, 2015
@joaomdmoura joaomdmoura merged commit 87c47f8 into rails-api:master Aug 22, 2015
@bf4
Copy link
Member

bf4 commented Aug 23, 2015

@kalys I don't have commit :) Thanks @joaomdmoura for stewarding this and @bacarini for all the work!

@bacarini
Copy link
Contributor Author

That is great!!!
Tks @bf4 and @joaomdmoura. 👍

I am glad we could make it through. 😃

@leandrocp
Copy link
Contributor

Great work @bacarini 🍻

@bacarini
Copy link
Contributor Author

cheers mate 🍻 😄

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

Successfully merging this pull request may close these issues.

6 participants