Change the way BURs are processed. Before, we spawned a background job for each line of the BUR, then processed each job sequentially. Now, we process the entire BUR sequentially in a single background job. This means that: * BURs are truly sequential now. Before certain things like removing aliases weren't actually performed in a background job, so they were performed out-of-order before everything else in the BUR. * Before, if an alias or implication line failed, then subsequent alias or implication lines would still be processed. This was because each alias or implication line was queued as a separate job, so a failure of one job didn't block another. Now, if any alias or implication fails, the entire BUR will fail and stop processing after that line. This may be good or bad, depending on whether we actually need the BUR to be processed in order or not. * Before, BURs were processed inside a database transaction (except for the actual updating of posts). Now they're not. This is because we can't afford to hold transactions open while processing long-running aliases or implications. This means that if BUR fails in the middle when it is initially approved, it will be left in a half-complete state. Before it would be rolled back and left in a pending state with no changes performed. * Before, only one BUR at a time could be processed. If multiple BURs were approved at the same time, then they would queue up and be processed one at a time. Now, multiple BURs can be processed at the same time. This may be undesirable when processing large BURs, or BURs that must be approved in a specific order. * Before, large tag category changes could time out. This was because they weren't actually performed in a background job. Now they are, so they shouldn't time out.
130 lines
3.6 KiB
Ruby
130 lines
3.6 KiB
Ruby
class BulkUpdateRequest < ApplicationRecord
|
|
attr_accessor :title, :reason
|
|
|
|
belongs_to :user
|
|
belongs_to :forum_topic, optional: true
|
|
belongs_to :forum_post, optional: true
|
|
belongs_to :approver, optional: true, class_name: "User"
|
|
|
|
validates :reason, presence: true, on: :create
|
|
validates :script, presence: true
|
|
validates :title, presence: true, if: ->(rec) { rec.forum_topic_id.blank? }
|
|
validates :forum_topic, presence: true, if: ->(rec) { rec.forum_topic_id.present? }
|
|
validates :status, inclusion: { in: %w[pending approved rejected] }
|
|
validate :validate_script, if: :script_changed?
|
|
|
|
before_save :update_tags, if: :script_changed?
|
|
after_create :create_forum_topic
|
|
|
|
scope :pending_first, -> { order(Arel.sql("(case status when 'pending' then 0 when 'approved' then 1 else 2 end)")) }
|
|
scope :pending, -> {where(status: "pending")}
|
|
scope :approved, -> { where(status: "approved") }
|
|
scope :rejected, -> { where(status: "rejected") }
|
|
scope :has_topic, -> { where.not(forum_topic: nil) }
|
|
|
|
module SearchMethods
|
|
def default_order
|
|
pending_first.order(id: :desc)
|
|
end
|
|
|
|
def search(params = {})
|
|
q = search_attributes(params, :id, :created_at, :updated_at, :script, :tags, :user, :forum_topic, :forum_post, :approver)
|
|
q = q.text_attribute_matches(:script, params[:script_matches])
|
|
|
|
if params[:status].present?
|
|
q = q.where(status: params[:status].split(","))
|
|
end
|
|
|
|
params[:order] ||= "status_desc"
|
|
case params[:order]
|
|
when "id_desc"
|
|
q = q.order(id: :desc)
|
|
when "id_asc"
|
|
q = q.order(id: :asc)
|
|
when "updated_at_desc"
|
|
q = q.order(updated_at: :desc)
|
|
when "updated_at_asc"
|
|
q = q.order(updated_at: :asc)
|
|
else
|
|
q = q.apply_default_order(params)
|
|
end
|
|
|
|
q
|
|
end
|
|
end
|
|
|
|
module ApprovalMethods
|
|
def forum_updater
|
|
@forum_updater ||= ForumUpdater.new(forum_topic)
|
|
end
|
|
|
|
def approve!(approver)
|
|
transaction do
|
|
CurrentUser.scoped(approver) do
|
|
processor.validate!(:approval)
|
|
update!(status: "approved", approver: approver)
|
|
processor.process_later!
|
|
forum_updater.update("The #{bulk_update_request_link} (forum ##{forum_post.id}) has been approved by @#{approver.name}.")
|
|
end
|
|
end
|
|
end
|
|
|
|
def create_forum_topic
|
|
CurrentUser.scoped(user) do
|
|
body = "[bur:#{id}]\n\n#{reason}"
|
|
self.forum_topic = ForumTopic.create(title: title, category_id: 1, creator: user) unless forum_topic.present?
|
|
self.forum_post = forum_topic.forum_posts.create(body: body, creator: user) unless forum_post.present?
|
|
save
|
|
end
|
|
end
|
|
|
|
def reject!(rejector = User.system)
|
|
transaction do
|
|
update!(status: "rejected")
|
|
forum_updater.update("The #{bulk_update_request_link} (forum ##{forum_post.id}) has been rejected by @#{rejector.name}.")
|
|
end
|
|
end
|
|
|
|
def bulk_update_request_link
|
|
%{"bulk update request ##{id}":#{Routes.bulk_update_requests_path(search: { id: id })}}
|
|
end
|
|
end
|
|
|
|
def validate_script
|
|
if processor.invalid?(:request)
|
|
errors.add(:base, processor.errors.full_messages.join("; "))
|
|
end
|
|
end
|
|
|
|
extend SearchMethods
|
|
include ApprovalMethods
|
|
|
|
def update_tags
|
|
self.tags = processor.affected_tags
|
|
end
|
|
|
|
def processor
|
|
@processor ||= BulkUpdateRequestProcessor.new(self)
|
|
end
|
|
|
|
def is_tag_move_allowed?
|
|
processor.is_tag_move_allowed?
|
|
end
|
|
|
|
def is_pending?
|
|
status == "pending"
|
|
end
|
|
|
|
def is_approved?
|
|
status == "approved"
|
|
end
|
|
|
|
def is_rejected?
|
|
status == "rejected"
|
|
end
|
|
|
|
def self.available_includes
|
|
[:user, :forum_topic, :forum_post, :approver]
|
|
end
|
|
end
|