From e046c03f57bbbab110d94a110709a654a9f707b0 Mon Sep 17 00:00:00 2001 From: Adrianna Chang Date: Wed, 10 Mar 2021 11:49:04 -0500 Subject: [PATCH] Introduce RunsPage model to handle cursor-based pagination of runs --- .../maintenance_tasks/tasks_controller.rb | 4 +- app/models/maintenance_tasks/runs_page.rb | 53 +++++++++++++++++++ .../maintenance_tasks/tasks/show.html.erb | 6 +-- .../maintenance_tasks/runs_page_test.rb | 48 +++++++++++++++++ test/system/maintenance_tasks/tasks_test.rb | 32 +++++++++++ 5 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 app/models/maintenance_tasks/runs_page.rb create mode 100644 test/models/maintenance_tasks/runs_page_test.rb diff --git a/app/controllers/maintenance_tasks/tasks_controller.rb b/app/controllers/maintenance_tasks/tasks_controller.rb index 7a05c86c..8fe5ebf5 100644 --- a/app/controllers/maintenance_tasks/tasks_controller.rb +++ b/app/controllers/maintenance_tasks/tasks_controller.rb @@ -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. diff --git a/app/models/maintenance_tasks/runs_page.rb b/app/models/maintenance_tasks/runs_page.rb new file mode 100644 index 00000000..afa56637 --- /dev/null +++ b/app/models/maintenance_tasks/runs_page.rb @@ -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] 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] 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 diff --git a/app/views/maintenance_tasks/tasks/show.html.erb b/app/views/maintenance_tasks/tasks/show.html.erb index 3c6494ca..eaf86302 100644 --- a/app/views/maintenance_tasks/tasks/show.html.erb +++ b/app/views/maintenance_tasks/tasks/show.html.erb @@ -41,12 +41,12 @@
<%= highlight_code(code) %>
<% end %> -<% if @previous_runs.present? %> +<% if @runs_page.records.any? %>

Previous Runs

- <%= 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 %> diff --git a/test/models/maintenance_tasks/runs_page_test.rb b/test/models/maintenance_tasks/runs_page_test.rb new file mode 100644 index 00000000..f4d996de --- /dev/null +++ b/test/models/maintenance_tasks/runs_page_test.rb @@ -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 diff --git a/test/system/maintenance_tasks/tasks_test.rb b/test/system/maintenance_tasks/tasks_test.rb index cc3426c5..29803441 100644 --- a/test/system/maintenance_tasks/tasks_test.rb +++ b/test/system/maintenance_tasks/tasks_test.rb @@ -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'