Skip to content

Commit

Permalink
Introduce RunsPage model to handle cursor-based pagination of runs
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianna-chang-shopify committed Mar 10, 2021
1 parent 6cde08a commit e046c03
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 4 deletions.
4 changes: 3 additions & 1 deletion app/controllers/maintenance_tasks/tasks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ def index
def show
@task = TaskData.find(params.fetch(:id))
set_refresh if @task.last_run&.active?
@pagy, @previous_runs = pagy(@task.previous_runs)

@runs_page = RunsPage.new(@task.previous_runs, params[:cursor])
# @pagy, @previous_runs = pagy(@task.previous_runs)
end

# Runs a given Task and redirects to the Task page.
Expand Down
53 changes: 53 additions & 0 deletions app/models/maintenance_tasks/runs_page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true
module MaintenanceTasks
# This class is responsible for handling cursor-based pagination for Run
# records.
#
# @api private
class RunsPage
# The number of Runs to show on a single Task page.
RUNS_PER_PAGE = 20

# Initializes a Runs Page with a Runs relation and a cursor. This page is
# used by the views to render a set of Runs.
# @param runs [ActiveRecord::Relation<MaintenanceTasks::Run>] the relation
# of Run records to be paginated.
# @param cursor [String, nil] the id that serves as the cursor when
# querying the Runs dataset to produce a page of Runs. If nil, the first
# Runs in the relation are used.
def initialize(runs, cursor)
@runs = runs
@cursor = cursor
end

# Returns the records for a Page, taking into account the cursor if one is
# present. Limits the number of records to 20.
#
# @return [ActiveRecord::Relation<MaintenanceTasks::Run>] a limited amount
# of Run records.
def records
runs_after_cursor = if @cursor.present?
@runs.where('id < ?', @cursor)
else
@runs
end
runs_after_cursor.limit(RUNS_PER_PAGE)
end

# Returns the cursor to use for the next Page of Runs. It is the id of the
# last record on the current Page.
#
# @return [Integer] the id of the last record for the Page.
def next_cursor
records.last.id
end

# Returns whether this Page is the last one.
#
# @return [Boolean] whether this Page contains the last Run record in the
# Runs dataset that is being paginated.
def last?
@runs.last == records.last
end
end
end
6 changes: 3 additions & 3 deletions app/views/maintenance_tasks/tasks/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@
<pre><code><%= highlight_code(code) %></code></pre>
<% end %>

<% if @previous_runs.present? %>
<% if @runs_page.records.any? %>
<hr/>

<h4 class="title is-4">Previous Runs</h4>

<%= render @previous_runs %>
<%= render @runs_page.records %>

<%= pagination(@pagy) %>
<%= link_to "Next page", task_path(@task, cursor: @runs_page.next_cursor) unless @runs_page.last? %>
<% end %>
48 changes: 48 additions & 0 deletions test/models/maintenance_tasks/runs_page_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# frozen_string_literal: true

require 'test_helper'

module MaintenanceTasks
class RunsPageTest < ActiveSupport::TestCase
setup do
@task_name = 'Maintenance::TestTask'
21.times do
Run.create!(
task_name: @task_name,
started_at: Time.now,
tick_count: 10,
tick_total: 10,
status: :succeeded,
ended_at: Time.now
)
end
@runs = Run.where(task_name: @task_name).order(created_at: :desc)
end

test '#records returns the most recent 20 runs when there is no cursor' do
runs_page = RunsPage.new(@runs, nil)
assert_equal @runs.first(20), runs_page.records
end

test '#records returns 20 runs after cursor if one is present' do
runs_page = RunsPage.new(@runs, @runs.first.id)
assert_equal @runs.last(20), runs_page.records
end

test '#next_cursor returns the id of the last run in the record set' do
last_id = @runs.last.id
runs_page = RunsPage.new(@runs, @runs.first.id)
assert_equal last_id, runs_page.next_cursor
end

test '#last? returns true if the last run in the record set is the last run for the relation' do
runs_page = RunsPage.new(@runs, @runs.first.id)
assert_predicate runs_page, :last?
end

test '#last? returns false if the last run in the record set is not the last run for the relation' do
runs_page = RunsPage.new(@runs, nil)
refute_predicate runs_page, :last?
end
end
end
32 changes: 32 additions & 0 deletions test/system/maintenance_tasks/tasks_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,38 @@ class TasksTest < ApplicationSystemTestCase
assert_text 'Ran for less than 5 seconds, finished 8 days ago.'
end

test 'view a Task with multiple pages of Runs' do
Run.create!(
task_name: 'Maintenance::TestTask',
created_at: 1.hour.ago,
started_at: 1.hour.ago,
tick_count: 2,
tick_total: 10,
status: :errored,
ended_at: 1.hour.ago
)
21.times do |i|
Run.create!(
task_name: 'Maintenance::TestTask',
created_at: i.minutes.ago,
started_at: i.minutes.ago,
tick_count: 10,
tick_total: 10,
status: :succeeded,
ended_at: i.minutes.ago
)
end

visit maintenance_tasks_path

click_on('Maintenance::TestTask')
assert_no_text 'Errored'

click_on('Next page')
assert_text 'Errored'
assert_no_link 'Next page'
end

test 'show a deleted Task' do
visit maintenance_tasks_path + '/tasks/Maintenance::DeletedTask'

Expand Down

0 comments on commit e046c03

Please sign in to comment.