app/models/problem.rb
# Represents a single Problem. The problem may have been
# reported as various Errs, but the user has grouped the
# Errs together as belonging to the same problem.
class Problem < ActiveRecord::Base
serialize :messages, Hash
serialize :user_agents, Hash
serialize :hosts, Hash
belongs_to :app, inverse_of: :problems
has_many :errs, :inverse_of => :problem, :dependent => :destroy
has_many :comments, :inverse_of => :err, :dependent => :destroy
validates_presence_of :environment
before_create :cache_app_attributes
after_initialize :default_values
scope :resolved, where(:resolved => true)
scope :unresolved, where(:resolved => false)
scope :ordered, order("last_notice_at desc")
def self.for_apps(apps)
return where(app_id: apps.pluck(:id)) if apps.is_a? ActiveRecord::Relation
where(app_id: apps.map(&:id))
end
validates_presence_of :last_notice_at, :first_notice_at
def default_values
if self.new_record?
self.user_agents ||= Hash.new
self.messages ||= Hash.new
self.hosts ||= Hash.new
self.comments_count ||= 0
self.notices_count ||= 0
self.resolved = false if self.resolved.nil?
self.first_notice_at ||= Time.new
self.last_notice_at ||= Time.new
end
end
def self.all_else_unresolved(fetch_all)
if fetch_all
scoped
else
where(:resolved => false)
end
end
def self.in_env(env)
env.present? ? where(:environment => env) : scoped
end
def notices
Notice.for_errs(errs).ordered
end
def comments_allowed?
Errbit::Config.allow_comments_with_issue_tracker || !app.issue_tracker_configured?
end
def resolve!
self.update_attributes!(:resolved => true, :resolved_at => Time.now)
end
def unresolve!
self.update_attributes!(:resolved => false, :resolved_at => nil)
end
def unresolved?
!resolved?
end
def self.merge!(*problems)
ProblemMerge.new(problems).merge
end
def merged?
errs.length > 1
end
def unmerge!
attrs = {:error_class => error_class, :environment => environment}
problem_errs = errs.to_a
problem_errs.shift
[self] + problem_errs.map(&:id).map do |err_id|
err = Err.find(err_id)
app.problems.create(attrs).tap do |new_problem|
err.update_attribute(:problem_id, new_problem.id)
new_problem.reset_cached_attributes
end
end
end
def self.ordered_by(sort, order)
case sort
when "app"; order("app_name #{order}")
when "message"; order("message #{order}")
when "last_notice_at"; order("last_notice_at #{order}")
when "last_deploy_at"; order("last_deploy_at #{order}")
when "count"; order("notices_count #{order}")
else raise("\"#{sort}\" is not a recognized sort")
end
end
def self.in_date_range(date_range)
where(["first_notice_at <= ? AND (resolved_at IS NULL OR resolved_at >= ?)", date_range.end, date_range.begin])
end
def reset_cached_attributes
ProblemUpdaterCache.new(self).update
end
def cache_app_attributes
if app
self.app_name = app.name
self.last_deploy_at = if (last_deploy = app.deploys.where(:environment => self.environment).last)
last_deploy.created_at.utc
end
Problem.where(id: self).update_all(
app_name: self.app_name,
last_deploy_at: self.last_deploy_at
)
end
end
def remove_cached_notice_attributes(notice)
update_attributes!(
:messages => attribute_count_descrease(:messages, notice.message),
:hosts => attribute_count_descrease(:hosts, notice.host),
:user_agents => attribute_count_descrease(:user_agents, notice.user_agent_string)
)
end
def issue_type
# Return issue_type if configured, but fall back to detecting app's issue tracker
attributes['issue_type'] ||=
(app.issue_tracker_configured? && app.issue_tracker.label) || nil
end
def inc(attr, increment_by)
self.update_attribute(attr, self.send(attr) + increment_by)
end
def self.search(value)
t = arel_table
where(t[:error_class].matches("%#{value}%")
.or(t[:where].matches("%#{value}%"))
.or(t[:message].matches("%#{value}%"))
.or(t[:app_name].matches("%#{value}%"))
.or(t[:environment].matches("%#{value}%"))
)
end
private
def attribute_count_descrease(name, value)
counter, index = send(name), attribute_index(value)
if counter[index] && counter[index]['count'] > 1
counter[index]['count'] -= 1
else
counter.delete(index)
end
counter
end
def attribute_index(value)
Digest::MD5.hexdigest(value.to_s)
end
end