Commit f225a5cc authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch '221174-graphql-pagination-bug' into 'master'

Graphql - fix pagination bug due to ms precision

See merge request gitlab-org/gitlab!34352
parents 7c2e7301 cd8b9992
---
title: GraphQL - properly handle pagination of millisecond-precision timestamps
merge_request: 34352
author:
type: fixed
...@@ -194,7 +194,12 @@ module Gitlab ...@@ -194,7 +194,12 @@ module Gitlab
order_list.each do |field| order_list.each do |field|
field_name = field.attribute_name field_name = field.attribute_name
ordering[field_name] = node[field_name].to_s field_value = node[field_name]
ordering[field_name] = if field_value.is_a?(Time)
field_value.strftime('%Y-%m-%d %H:%M:%S.%N %Z')
else
field_value.to_s
end
end end
encode(ordering.to_json) encode(ordering.to_json)
......
...@@ -33,7 +33,7 @@ describe Gitlab::Graphql::Pagination::Keyset::Connection do ...@@ -33,7 +33,7 @@ describe Gitlab::Graphql::Pagination::Keyset::Connection do
let(:nodes) { Project.order(:updated_at) } let(:nodes) { Project.order(:updated_at) }
it 'returns the encoded value of the order' do it 'returns the encoded value of the order' do
expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.to_s) expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.strftime('%Y-%m-%d %H:%M:%S.%N %Z'))
end end
it 'includes the :id even when not specified in the order' do it 'includes the :id even when not specified in the order' do
...@@ -45,7 +45,7 @@ describe Gitlab::Graphql::Pagination::Keyset::Connection do ...@@ -45,7 +45,7 @@ describe Gitlab::Graphql::Pagination::Keyset::Connection do
let(:nodes) { Project.order(:updated_at).order(:created_at) } let(:nodes) { Project.order(:updated_at).order(:created_at) }
it 'returns the encoded value of the order' do it 'returns the encoded value of the order' do
expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.to_s) expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.strftime('%Y-%m-%d %H:%M:%S.%N %Z'))
end end
end end
...@@ -53,7 +53,7 @@ describe Gitlab::Graphql::Pagination::Keyset::Connection do ...@@ -53,7 +53,7 @@ describe Gitlab::Graphql::Pagination::Keyset::Connection do
let(:nodes) { Project.order(Arel.sql('projects.updated_at IS NULL')).order(:updated_at).order(:id) } let(:nodes) { Project.order(Arel.sql('projects.updated_at IS NULL')).order(:updated_at).order(:id) }
it 'returns the encoded value of the order' do it 'returns the encoded value of the order' do
expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.to_s) expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.strftime('%Y-%m-%d %H:%M:%S.%N %Z'))
end end
end end
end end
......
...@@ -187,4 +187,62 @@ describe 'GraphQL' do ...@@ -187,4 +187,62 @@ describe 'GraphQL' do
end end
end end
end end
describe 'keyset pagination' do
let_it_be(:project) { create(:project, :public) }
let_it_be(:issues) { create_list(:issue, 10, project: project, created_at: Time.now.change(usec: 200)) }
let(:page_size) { 6 }
let(:issues_edges) { %w(data project issues edges) }
let(:end_cursor) { %w(data project issues pageInfo endCursor) }
let(:query) do
<<~GRAPHQL
query project($fullPath: ID!, $first: Int, $after: String) {
project(fullPath: $fullPath) {
issues(first: $first, after: $after) {
edges { node { iid } }
pageInfo { endCursor }
}
}
}
GRAPHQL
end
# TODO: Switch this to use `post_graphql`
# This is not performing an actual GraphQL request because the
# variables end up being strings when passed through the `post_graphql`
# helper.
#
# https://gitlab.com/gitlab-org/gitlab/-/issues/222432
def execute_query(after: nil)
GitlabSchema.execute(
query,
context: { current_user: nil },
variables: {
fullPath: project.full_path,
first: page_size,
after: after
}
)
end
it 'paginates datetimes correctly when they have millisecond data' do
# let's make sure we're actually querying a timestamp, just in case
expect(Gitlab::Graphql::Pagination::Keyset::QueryBuilder)
.to receive(:new).with(anything, anything, hash_including('created_at'), anything).and_call_original
first_page = execute_query
edges = first_page.dig(*issues_edges)
cursor = first_page.dig(*end_cursor)
expect(edges.count).to eq(6)
expect(edges.last['node']['iid']).to eq(issues[4].iid.to_s)
second_page = execute_query(after: cursor)
edges = second_page.dig(*issues_edges)
expect(edges.count).to eq(4)
expect(edges.last['node']['iid']).to eq(issues[0].iid.to_s)
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment