Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions lib/graphoid/queries/pagination.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,22 @@ def self.build(model)
field :skip, GraphQL::Types::Int, null: true
field :data, [grapho.type], null: true, extras: [:lookahead]

def data(lookahead:)
object ||= @object
# Mongoid::Criteria uses method_missing to send the method to the underlying Model
# Just implement def self.lookahead(object, lookahead) in your model to manage
# eager loading
obj = object.lookahead(object, lookahead) if object.respond_to? :lookahead
object = obj if obj
return object.eager_load if object.respond_to? :eager_load
object
define_method(:data) do |lookahead:|
scope = if model.respond_to?(:graphoid_lookahead)
model.graphoid_lookahead(object, lookahead: lookahead)
elsif object.respond_to?(:lookahead)
object.lookahead(object, lookahead) || object
else
object
end

if model.respond_to?(:graphoid_eager_load)
model.graphoid_eager_load(scope, lookahead: lookahead)
elsif scope.respond_to?(:eager_load)
scope.eager_load
else
scope
end
end

def page_size
Expand Down
78 changes: 78 additions & 0 deletions spec/tester_mongo/spec/graphoid/queries/pagination_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,83 @@
)
end
end

context 'with paginated data model hooks', limit: 2, skip_param: 0 do
let(:hook_arguments) { {} }

before do
arguments = hook_arguments
selected_name = level2.name
Level.define_singleton_method(:graphoid_lookahead) do |scope, lookahead:|
arguments[:prepare_scope] = scope
arguments[:prepare_lookahead] = lookahead
arguments[:prepared_scope] = scope.where(name: selected_name)
end
Level.define_singleton_method(:graphoid_eager_load) do |scope, lookahead:|
arguments[:load_scope] = scope
arguments[:load_lookahead] = lookahead
scope.to_a
end

post :execute, params: { query: query }
end

after do
Level.singleton_class.remove_method(:graphoid_lookahead)
Level.singleton_class.remove_method(:graphoid_eager_load)
end

it 'passes the resolved scope and lookahead through both model hooks' do
expect(hook_arguments[:prepare_scope]).to be_a(Mongoid::Criteria)
expect(hook_arguments[:prepare_scope].options).to include(limit: 2, skip: 0)
expect(hook_arguments[:load_scope]).to equal(hook_arguments[:prepared_scope])
expect(hook_arguments[:load_lookahead]).to equal(hook_arguments[:prepare_lookahead])
selected_fields = hook_arguments[:prepare_lookahead].selections.map { |selection| selection.field.name }

expect(selected_fields).to contain_exactly('id', 'name', 'createdAt')
end

it 'uses the collection returned by the model hook' do
expect(JSON.parse(response.body)['data']['levels']['data']).to contain_exactly(
{
'id' => level2.id.to_s,
'name' => level2.name,
'createdAt' => level2.created_at.iso8601(3)
}
)
end
end

context 'with legacy model loading methods', limit: 2, skip_param: 0 do
let(:legacy_calls) { [] }

before do
calls = legacy_calls
hook_result = [level2]
Level.define_singleton_method(:lookahead) do |_scope, _lookahead|
calls << :lookahead
hook_result
end

post :execute, params: { query: query }
end

after do
Level.singleton_class.remove_method(:lookahead)
end

it 'falls back to the legacy lookahead convention' do
expect(response).to have_http_status(200)
expect(JSON.parse(response.body)['errors']).to be_nil
expect(JSON.parse(response.body)['data']['levels']['data']).to contain_exactly(
{
'id' => level2.id.to_s,
'name' => level2.name,
'createdAt' => level2.created_at.iso8601(3)
}
)
expect(legacy_calls).to eq([:lookahead])
end
end
end
end