Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start using STI for decision model #16008

Merged
merged 8 commits into from Apr 22, 2024
3 changes: 1 addition & 2 deletions src/api/app/components/reports_modal_component.html.haml
Expand Up @@ -21,8 +21,7 @@
-# Decision-related canned responses exclusively:
= render CannedResponsesDropdownComponent.new(canned_responses)
= form.text_area(:reason, class: 'form-control mb-3', required: true, placeholder: 'Reason for the decision')
-# TODO: this should be adapted when we move to Decision#type
= form.select(:kind, DECISION_KIND_MAP.to_a, {}, class: 'form-select')
= form.select(:type, Decision::TYPES, {}, class: 'form-select')
danidoni marked this conversation as resolved.
Show resolved Hide resolved
.modal-footer
- reports.each do |report|
= form.hidden_field(:report_ids, multiple: true, value: report.id)
Expand Down
3 changes: 0 additions & 3 deletions src/api/app/components/reports_modal_component.rb
@@ -1,9 +1,6 @@
class ReportsModalComponent < ApplicationComponent
attr_reader :reportable, :reportable_name, :user, :reports

# TODO: temporary solution until Decision#type replaces Decision#kind with new values
DECISION_KIND_MAP = { 'cleared' => 'cleared', 'favored' => 'favor' }.freeze

def initialize(reportable:, reportable_name:, user:, reports:)
super

Expand Down
2 changes: 1 addition & 1 deletion src/api/app/controllers/webui/decisions_controller.rb
Expand Up @@ -19,6 +19,6 @@ def create
private

def decision_params
params.require(:decision).permit(:reason, :kind, report_ids: [])
params.require(:decision).permit(:reason, :type, report_ids: [])
end
end
11 changes: 10 additions & 1 deletion src/api/app/models/decision.rb
@@ -1,11 +1,14 @@
class Decision < ApplicationRecord
TYPES = { favored: 'DecisionFavored', cleared: 'DecisionCleared' }.freeze

validates :reason, presence: true, length: { maximum: 65_535 }
validates :type, presence: true, length: { maximum: 255 }
hellcp-work marked this conversation as resolved.
Show resolved Hide resolved

belongs_to :moderator, class_name: 'User', optional: false

has_many :reports, dependent: :nullify

# TODO: Remove this after type is deployed
enum kind: {
cleared: 0,
favor: 1
Expand All @@ -14,8 +17,13 @@ class Decision < ApplicationRecord
after_create :create_event
after_create :track_decision

def description
'The moderator decided on the report'
end

private

# TODO: Replace this with `AbstractMethodCalled` after type is deployed
def create_event
case kind
when 'cleared'
Expand All @@ -29,8 +37,9 @@ def event_parameters
{ id: id, moderator_id: moderator.id, reason: reason, report_last_id: reports.last.id, reportable_type: reports.first.reportable.class.name }
end

# TODO: Remove kind after type is deployed
def track_decision
RabbitmqBus.send_to_bus('metrics', "decision,kind=#{kind} hours_before_decision=#{hours_before_decision},count=1")
RabbitmqBus.send_to_bus('metrics', "decision,kind=#{kind},type=#{type} hours_before_decision=#{hours_before_decision},count=1")
end

def hours_before_decision
Expand Down
11 changes: 11 additions & 0 deletions src/api/app/models/decision_cleared.rb
@@ -1,4 +1,15 @@
class DecisionCleared < Decision
after_create :create_event

def description
'The moderator decided to clear the report'
end

private

def create_event
Event::ClearedDecision.create(event_parameters)
end
end

# == Schema Information
Expand Down
34 changes: 34 additions & 0 deletions src/api/app/models/decision_favored.rb
@@ -0,0 +1,34 @@
class DecisionFavored < Decision
after_create :create_event

def description
'The moderator decided to favor the report'
end

private

def create_event
Event::FavoredDecision.create(event_parameters)
end
end

# == Schema Information
#
# Table name: decisions
#
# id :bigint not null, primary key
# kind :integer default("cleared")
# reason :text(65535) not null
# type :string(255) not null, default("DecisionCleared")
# created_at :datetime not null
# updated_at :datetime not null
# moderator_id :integer not null, indexed
#
# Indexes
#
# index_decisions_on_moderator_id (moderator_id)
#
# Foreign Keys
#
# fk_rails_... (moderator_id => users.id)
#
4 changes: 2 additions & 2 deletions src/api/app/policies/appeal_policy.rb
Expand Up @@ -31,12 +31,12 @@ def create?
def decision_cleared_report_from_user?
return false unless record.appellant == user

record.decision.kind == 'cleared' && record.decision.reports.pluck(:user_id).include?(user.id)
record.decision.type == 'DecisionCleared' && record.decision.reports.pluck(:user_id).include?(user.id)
end

def decision_favored_report_of_action_from_user?
return false unless record.appellant == user

record.decision.kind == 'favor' && "#{@report.reportable_type}Policy".constantize.new(user, @report.reportable).update?
record.decision.type == 'DecisionFavored' && "#{@report.reportable_type}Policy".constantize.new(user, @report.reportable).update?
end
end
2 changes: 2 additions & 0 deletions src/api/app/policies/decision_favored_policy.rb
@@ -0,0 +1,2 @@
class DecisionFavoredPolicy < DecisionPolicy
end
6 changes: 3 additions & 3 deletions src/api/app/views/webui/appeals/_list_decision.html.haml
Expand Up @@ -2,7 +2,7 @@
.card-body
%h3 Decision
%p
The moderator decided to
= decision.kind
the reports. The reason was:
= succeed '.' do
= decision.description
The reason was:
= decision.reason
@@ -0,0 +1,15 @@
# frozen_string_literal: true

class SetTheDecisionTypeFromDecisionKind < ActiveRecord::Migration[7.0]
def up
Decision.in_batches do |batch|
batch.each do |decision|
decision.type = decision.favor? ? 'DecisionFavored' : 'DecisionCleared'
end
end
end

def down
raise ActiveRecord::IrreversibleMigration
end
end
2 changes: 1 addition & 1 deletion src/api/db/data_schema.rb
@@ -1 +1 @@
DataMigrate::Data.define(version: 20240321103332)
DataMigrate::Data.define(version: 20240417122944)
@@ -1,3 +1,5 @@
require 'decision' # To break the circular dependency between Decision and DecisionFavored and DecisionCleared

eduardoj marked this conversation as resolved.
Show resolved Hide resolved
RSpec.describe NotificationActionDescriptionComponent, type: :component do
context 'when the notification is for a Event::RequestStatechange event with a request having only a target' do
let(:target_project) { create(:project, name: 'project_123') }
Expand Down