jobs: add ability to search jobs on /jobs page.
Add ability to search jobs on the /jobs page by job type or by status. Fixes #2577 (Search filters for delayed jobs). This wasn't possible before with DelayedJobs because it stored the job data in a YAML string, which made it difficult to search jobs by type. GoodJobs stores job data in a JSON object, which is easier to search in Postgres.
This commit is contained in:
@@ -4,30 +4,30 @@ class JobsController < ApplicationController
|
|||||||
respond_to :html, :xml, :json, :js
|
respond_to :html, :xml, :json, :js
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@jobs = authorize GoodJob::ActiveJobJob.order(created_at: :desc).extending(PaginationExtension).paginate(params[:page], limit: params[:limit]), policy_class: GoodJobPolicy
|
@jobs = authorize BackgroundJob.paginated_search(params)
|
||||||
respond_with(@jobs)
|
respond_with(@jobs)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cancel
|
def cancel
|
||||||
@job = authorize GoodJob::ActiveJobJob.find(params[:id]), policy_class: GoodJobPolicy
|
@job = authorize BackgroundJob.find(params[:id])
|
||||||
@job.discard_job("Canceled")
|
@job.discard_job("Canceled")
|
||||||
respond_with(@job)
|
respond_with(@job)
|
||||||
end
|
end
|
||||||
|
|
||||||
def retry
|
def retry
|
||||||
@job = authorize GoodJob::ActiveJobJob.find(params[:id]), policy_class: GoodJobPolicy
|
@job = authorize BackgroundJob.find(params[:id])
|
||||||
@job.retry_job
|
@job.retry_job
|
||||||
respond_with(@job)
|
respond_with(@job)
|
||||||
end
|
end
|
||||||
|
|
||||||
def run
|
def run
|
||||||
@job = authorize GoodJob::ActiveJobJob.find(params[:id]), policy_class: GoodJobPolicy
|
@job = authorize BackgroundJob.find(params[:id])
|
||||||
@job.reschedule_job
|
@job.reschedule_job
|
||||||
respond_with(@job)
|
respond_with(@job)
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@job = authorize GoodJob::ActiveJobJob.find(params[:id]), policy_class: GoodJobPolicy
|
@job = authorize BackgroundJob.find(params[:id])
|
||||||
@job.destroy
|
@job.destroy
|
||||||
respond_with(@job)
|
respond_with(@job)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -23,4 +23,19 @@ class ApplicationJob < ActiveJob::Base
|
|||||||
discard_on ActiveJob::DeserializationError do |_job, error|
|
discard_on ActiveJob::DeserializationError do |_job, error|
|
||||||
DanbooruLogger.log(error)
|
DanbooruLogger.log(error)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# A list of all available job types. Used by the /jobs search form.
|
||||||
|
def self.job_classes
|
||||||
|
[
|
||||||
|
AmcheckDatabaseJob, BigqueryExportAllJob, DeleteFavoritesJob,
|
||||||
|
DmailInactiveApproversJob, IqdbAddPostJob, IqdbRemovePostJob,
|
||||||
|
PopulateSavedSearchJob, PruneApproversJob, PruneBansJob,
|
||||||
|
PruneBulkUpdateRequestsJob, PrunePostDisapprovalsJob, PrunePostsJob,
|
||||||
|
PruneRateLimitsJob, PruneUploadsJob, RegeneratePostCountsJob,
|
||||||
|
RegeneratePostJob, RetireTagRelationshipsJob,
|
||||||
|
UploadPreprocessorDelayedStartJob, UploadServiceDelayedStartJob,
|
||||||
|
VacuumDatabaseJob, DiscordNotificationJob, BigqueryExportJob,
|
||||||
|
ProcessBulkUpdateRequestJob, PruneJobsJob
|
||||||
|
]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -256,6 +256,8 @@ module Searchable
|
|||||||
case type
|
case type
|
||||||
when :string, :text
|
when :string, :text
|
||||||
search_text_attribute(name, params)
|
search_text_attribute(name, params)
|
||||||
|
when :uuid
|
||||||
|
search_uuid_attribute(name, params)
|
||||||
when :boolean
|
when :boolean
|
||||||
search_boolean_attribute(name, params)
|
search_boolean_attribute(name, params)
|
||||||
when :integer, :float, :datetime, :interval
|
when :integer, :float, :datetime, :interval
|
||||||
@@ -385,6 +387,24 @@ module Searchable
|
|||||||
relation
|
relation
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def search_uuid_attribute(attr, params)
|
||||||
|
relation = all
|
||||||
|
|
||||||
|
if params[attr].present?
|
||||||
|
relation = relation.where(attr => params[attr])
|
||||||
|
end
|
||||||
|
|
||||||
|
if params[:"#{attr}_eq"].present?
|
||||||
|
relation = relation.where(attr => params[:"#{attr}_eq"])
|
||||||
|
end
|
||||||
|
|
||||||
|
if params[:"#{attr}_not_eq"].present?
|
||||||
|
relation = relation.where.not(attr => params[:"#{attr}_not_eq"])
|
||||||
|
end
|
||||||
|
|
||||||
|
relation
|
||||||
|
end
|
||||||
|
|
||||||
def search_association_attribute(attr, params, current_user)
|
def search_association_attribute(attr, params, current_user)
|
||||||
association = reflect_on_association(attr)
|
association = reflect_on_association(attr)
|
||||||
relation = all
|
relation = all
|
||||||
|
|||||||
49
app/models/background_job.rb
Normal file
49
app/models/background_job.rb
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# A BackgroundJob is a job in the good_jobs table. This class is simply an
|
||||||
|
# extension of GoodJob::ActiveJobJob, with a few extra methods for searching jobs.
|
||||||
|
#
|
||||||
|
# @see https://github.com/bensheldon/good_job/blob/main/lib/good_job/active_job_job.rb
|
||||||
|
class BackgroundJob < GoodJob::ActiveJobJob
|
||||||
|
concerning :SearchMethods do
|
||||||
|
class_methods do
|
||||||
|
def default_order
|
||||||
|
order(created_at: :desc)
|
||||||
|
end
|
||||||
|
|
||||||
|
def status_matches(status)
|
||||||
|
case status.downcase
|
||||||
|
when "queued"
|
||||||
|
queued
|
||||||
|
when "running"
|
||||||
|
running
|
||||||
|
when "finished"
|
||||||
|
finished
|
||||||
|
when "discarded"
|
||||||
|
discarded
|
||||||
|
else
|
||||||
|
all
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def name_matches(name)
|
||||||
|
class_name = name.tr(" ", "_").camelize + "Job"
|
||||||
|
where_json_contains(:serialized_params, { job_class: class_name })
|
||||||
|
end
|
||||||
|
|
||||||
|
def search(params)
|
||||||
|
q = search_attributes(params, :id, :created_at, :updated_at, :queue_name, :priority, :serialized_params, :scheduled_at, :performed_at, :finished_at, :error, :active_job_id, :concurrency_key, :cron_key, :retried_good_job_id, :cron_at)
|
||||||
|
|
||||||
|
if params[:name].present?
|
||||||
|
q = q.name_matches(params[:name])
|
||||||
|
end
|
||||||
|
|
||||||
|
if params[:status].present?
|
||||||
|
q = q.status_matches(params[:status])
|
||||||
|
end
|
||||||
|
|
||||||
|
q.apply_default_order(params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class GoodJobPolicy < ApplicationPolicy
|
class BackgroundJobPolicy < ApplicationPolicy
|
||||||
def index?
|
def index?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
@@ -2,6 +2,12 @@
|
|||||||
<div id="a-index">
|
<div id="a-index">
|
||||||
<h1>Jobs</h1>
|
<h1>Jobs</h1>
|
||||||
|
|
||||||
|
<%= search_form_for(jobs_path) do |f| %>
|
||||||
|
<%= f.input :name, collection: ApplicationJob.job_classes.map { |klass| klass.name.titleize.delete_suffix(" Job") }, include_blank: true, selected: params[:search][:name] %>
|
||||||
|
<%= f.input :status, collection: %w[Running Queued Finished Discarded], include_blank: true, selected: params[:search][:status] %>
|
||||||
|
<%= f.submit "Search" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<%= table_for @jobs, class: "striped autofit" do |t| %>
|
<%= table_for @jobs, class: "striped autofit" do |t| %>
|
||||||
<% t.column "Name" do |job| %>
|
<% t.column "Name" do |job| %>
|
||||||
<%= job.job_class.titleize.delete_suffix(" Job") %>
|
<%= job.job_class.titleize.delete_suffix(" Job") %>
|
||||||
@@ -28,7 +34,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% t.column column: "control" do |job| %>
|
<% t.column column: "control" do |job| %>
|
||||||
<% if GoodJobPolicy.new(CurrentUser.user, job).update? %>
|
<% if policy(job).update? %>
|
||||||
<%= link_to "Run", run_job_path(job), method: :put, remote: true %> |
|
<%= link_to "Run", run_job_path(job), method: :put, remote: true %> |
|
||||||
<%= link_to "Retry", retry_job_path(job), method: :put, remote: true %> |
|
<%= link_to "Retry", retry_job_path(job), method: :put, remote: true %> |
|
||||||
<%= link_to "Cancel", cancel_job_path(job), method: :put, remote: true %> |
|
<%= link_to "Cancel", cancel_job_path(job), method: :put, remote: true %> |
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
GoodJob.active_record_parent_class = "ApplicationRecord"
|
||||||
GoodJob.retry_on_unhandled_error = false
|
GoodJob.retry_on_unhandled_error = false
|
||||||
GoodJob.preserve_job_records = true
|
GoodJob.preserve_job_records = true
|
||||||
GoodJob.on_thread_error = ->(exception) do
|
GoodJob.on_thread_error = ->(exception) do
|
||||||
|
|||||||
Reference in New Issue
Block a user