Merge pull request #9644 from tchak/graphql-fix-pagination-with-order-desc
fix(graphql): fix pagination with order desc
This commit is contained in:
commit
e7fc8661d5
3 changed files with 60 additions and 31 deletions
|
@ -42,23 +42,10 @@ module Connections
|
||||||
# -> d5, d6
|
# -> d5, d6
|
||||||
#
|
#
|
||||||
# si after ou before present, last ou first donne juste limit
|
# si after ou before present, last ou first donne juste limit
|
||||||
#
|
def limit_and_inverted(first: nil, last: nil, after: nil, before: nil)
|
||||||
# order:
|
|
||||||
# order, ne sert rien si after ou before
|
|
||||||
#
|
|
||||||
# first: 2, order: desc => last: 2
|
|
||||||
# -> d5, d6
|
|
||||||
#
|
|
||||||
# last: 2, order: desc => first 2
|
|
||||||
# -> d1, d2
|
|
||||||
def limit_and_inverted(first: nil, last: nil, after: nil, before: nil, order: nil)
|
|
||||||
limit = [first, last, max_page_size].compact.min + 1
|
limit = [first, last, max_page_size].compact.min + 1
|
||||||
inverted = last.present? || before.present?
|
inverted = last.present? || before.present?
|
||||||
|
|
||||||
if order == :desc && after.nil? && before.nil?
|
|
||||||
inverted = !inverted
|
|
||||||
end
|
|
||||||
|
|
||||||
[limit, inverted]
|
[limit, inverted]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -73,8 +60,10 @@ module Connections
|
||||||
def load_nodes
|
def load_nodes
|
||||||
@nodes ||= begin
|
@nodes ||= begin
|
||||||
ensure_valid_params
|
ensure_valid_params
|
||||||
|
limit, inverted = limit_and_inverted(first:, last:, after:, before:)
|
||||||
|
|
||||||
|
return load_nodes_deprecated_order(limit, inverted) if @deprecated_order == :desc
|
||||||
|
|
||||||
limit, inverted = limit_and_inverted(first:, last:, after:, before:, order: @deprecated_order)
|
|
||||||
expected_size = limit - 1
|
expected_size = limit - 1
|
||||||
|
|
||||||
nodes = resolve_nodes(limit:, before:, after:, inverted:)
|
nodes = resolve_nodes(limit:, before:, after:, inverted:)
|
||||||
|
@ -89,6 +78,22 @@ module Connections
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def load_nodes_deprecated_order(limit, inverted)
|
||||||
|
expected_size = limit - 1
|
||||||
|
|
||||||
|
if @deprecated_order == :desc && before.nil?
|
||||||
|
inverted = !inverted
|
||||||
|
end
|
||||||
|
|
||||||
|
nodes = resolve_nodes(limit:, before: after, after: before, inverted:)
|
||||||
|
|
||||||
|
result_size = nodes.size
|
||||||
|
@has_next_page = previous_page?(before, result_size, limit, inverted)
|
||||||
|
@has_previous_page = next_page?(after, result_size, limit, inverted)
|
||||||
|
|
||||||
|
nodes.first(expected_size)
|
||||||
|
end
|
||||||
|
|
||||||
def ensure_valid_params
|
def ensure_valid_params
|
||||||
if first.present? && last.present?
|
if first.present? && last.present?
|
||||||
raise GraphQL::ExecutionError.new('Arguments "first" and "last" are exclusive', extensions: { code: :bad_request })
|
raise GraphQL::ExecutionError.new('Arguments "first" and "last" are exclusive', extensions: { code: :bad_request })
|
||||||
|
@ -105,6 +110,10 @@ module Connections
|
||||||
if last.present? && last < 0
|
if last.present? && last < 0
|
||||||
raise GraphQL::ExecutionError.new('Argument "last" must be a non-negative integer', extensions: { code: :bad_request })
|
raise GraphQL::ExecutionError.new('Argument "last" must be a non-negative integer', extensions: { code: :bad_request })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if last.present? && @deprecated_order == :desc
|
||||||
|
raise GraphQL::ExecutionError.new('Argument "last" is not supported with order "desc"', extensions: { code: :bad_request })
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def timestamp_and_id_from_cursor(cursor)
|
def timestamp_and_id_from_cursor(cursor)
|
||||||
|
|
|
@ -197,17 +197,49 @@ describe API::V2::GraphqlController do
|
||||||
|
|
||||||
context 'with deprecated order' do
|
context 'with deprecated order' do
|
||||||
let(:variables) { { demarcheNumber: procedure.id, includeDossiers: true, first: 2, order: 'DESC' } }
|
let(:variables) { { demarcheNumber: procedure.id, includeDossiers: true, first: 2, order: 'DESC' } }
|
||||||
let(:start_cursor) { cursor_for(dossier2, order_column) }
|
let(:start_cursor) { cursor_for(dossier3, order_column) }
|
||||||
let(:end_cursor) { cursor_for(dossier3, order_column) }
|
let(:end_cursor) { cursor_for(dossier2, order_column) }
|
||||||
|
|
||||||
it {
|
it {
|
||||||
expect(gql_errors).to be_nil
|
expect(gql_errors).to be_nil
|
||||||
expect(gql_data[:demarche][:dossiers][:nodes].size).to eq(2)
|
expect(gql_data[:demarche][:dossiers][:nodes].size).to eq(2)
|
||||||
expect(gql_data[:demarche][:dossiers][:pageInfo][:hasNextPage]).to be_falsey
|
expect(gql_data[:demarche][:dossiers][:pageInfo][:hasNextPage]).to be_truthy
|
||||||
expect(gql_data[:demarche][:dossiers][:pageInfo][:hasPreviousPage]).to be_truthy
|
expect(gql_data[:demarche][:dossiers][:pageInfo][:hasPreviousPage]).to be_falsey
|
||||||
expect(gql_data[:demarche][:dossiers][:pageInfo][:startCursor]).to eq(start_cursor)
|
expect(gql_data[:demarche][:dossiers][:pageInfo][:startCursor]).to eq(start_cursor)
|
||||||
expect(gql_data[:demarche][:dossiers][:pageInfo][:endCursor]).to eq(end_cursor)
|
expect(gql_data[:demarche][:dossiers][:pageInfo][:endCursor]).to eq(end_cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context 'after' do
|
||||||
|
let(:variables) { { demarcheNumber: procedure.id, includeDossiers: true, first: 2, after: current_cursor, order: 'DESC' } }
|
||||||
|
let(:current_cursor) { cursor_for(dossier2, order_column) }
|
||||||
|
let(:start_cursor) { cursor_for(dossier1, order_column) }
|
||||||
|
let(:end_cursor) { cursor_for(dossier, order_column) }
|
||||||
|
|
||||||
|
it {
|
||||||
|
expect(gql_errors).to be_nil
|
||||||
|
expect(gql_data[:demarche][:dossiers][:nodes].size).to eq(2)
|
||||||
|
expect(gql_data[:demarche][:dossiers][:pageInfo][:hasNextPage]).to be_falsey
|
||||||
|
expect(gql_data[:demarche][:dossiers][:pageInfo][:hasPreviousPage]).to be_truthy
|
||||||
|
expect(gql_data[:demarche][:dossiers][:pageInfo][:startCursor]).to eq(start_cursor)
|
||||||
|
expect(gql_data[:demarche][:dossiers][:pageInfo][:endCursor]).to eq(end_cursor)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'before' do
|
||||||
|
let(:variables) { { demarcheNumber: procedure.id, includeDossiers: true, first: 2, before: current_cursor, order: 'DESC' } }
|
||||||
|
let(:current_cursor) { cursor_for(dossier1, order_column) }
|
||||||
|
let(:start_cursor) { cursor_for(dossier3, order_column) }
|
||||||
|
let(:end_cursor) { cursor_for(dossier2, order_column) }
|
||||||
|
|
||||||
|
it {
|
||||||
|
expect(gql_errors).to be_nil
|
||||||
|
expect(gql_data[:demarche][:dossiers][:nodes].size).to eq(2)
|
||||||
|
expect(gql_data[:demarche][:dossiers][:pageInfo][:hasNextPage]).to be_truthy
|
||||||
|
expect(gql_data[:demarche][:dossiers][:pageInfo][:hasPreviousPage]).to be_falsey
|
||||||
|
expect(gql_data[:demarche][:dossiers][:pageInfo][:startCursor]).to eq(start_cursor)
|
||||||
|
expect(gql_data[:demarche][:dossiers][:pageInfo][:endCursor]).to eq(end_cursor)
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'after' do
|
context 'after' do
|
||||||
|
|
|
@ -21,12 +21,6 @@ RSpec.describe Connections::CursorConnection do
|
||||||
it { is_expected.to eq(limit: 3, inverted: false) }
|
it { is_expected.to eq(limit: 3, inverted: false) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when asked for 2 first elements, in order desc' do
|
|
||||||
let(:args) { { first: 2, order: :desc } }
|
|
||||||
|
|
||||||
it { is_expected.to eq(limit: 3, inverted: true) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when exceeding the max_page_size' do
|
context 'when exceeding the max_page_size' do
|
||||||
let(:args) { { first: max_page_size + 1 } }
|
let(:args) { { first: max_page_size + 1 } }
|
||||||
|
|
||||||
|
@ -39,12 +33,6 @@ RSpec.describe Connections::CursorConnection do
|
||||||
it { is_expected.to eq(limit: 3, inverted: true) }
|
it { is_expected.to eq(limit: 3, inverted: true) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when asked for 2 last elements, in order desc' do
|
|
||||||
let(:args) { { last: 2, order: :desc } }
|
|
||||||
|
|
||||||
it { is_expected.to eq(limit: 3, inverted: false) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context '' do
|
context '' do
|
||||||
let(:args) { { after: :after, first: 2 } }
|
let(:args) { { after: :after, first: 2 } }
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue