Commit cd8b9992 authored by Mario de la Ossa's avatar Mario de la Ossa Committed by Bob Van Landuyt

Graphql - fix pagination bug due to ms precision

When we use keyset pagination inside GraphQL our cursors are missing
millisecond data for timestamps, which causes pagination to not work
properly when objects have the same timestamp up to the second, as the
database sees for example '23:14:56' as '23:14:56.0000' which is
different from any other timestamps with millisecond values that are not
'0000'
parent 5b19eb8c
---
title: GraphQL - properly handle pagination of millisecond-precision timestamps
merge_request: 34352
author:
type: fixed
......@@ -194,7 +194,12 @@ module Gitlab
order_list.each do |field|
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
encode(ordering.to_json)
......
......@@ -33,7 +33,7 @@ describe Gitlab::Graphql::Pagination::Keyset::Connection do
let(:nodes) { Project.order(:updated_at) }
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
it 'includes the :id even when not specified in the order' do
......@@ -45,7 +45,7 @@ describe Gitlab::Graphql::Pagination::Keyset::Connection do
let(:nodes) { Project.order(:updated_at).order(:created_at) }
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
......@@ -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) }
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
......
......@@ -187,4 +187,62 @@ describe 'GraphQL' do
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
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