98 lines
2.7 KiB
Ruby
98 lines
2.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Issue < ApplicationRecord
|
|
belongs_to :project
|
|
has_many :events, dependent: :nullify
|
|
|
|
enum :status, { open: 0, resolved: 1, ignored: 2 }
|
|
|
|
validates :title, presence: true
|
|
|
|
scope :recent, -> { order(last_seen: :desc) }
|
|
scope :by_frequency, -> { order(count: :desc) }
|
|
|
|
# Callbacks for email notifications
|
|
after_create :notify_new_issue, if: :should_notify_new_issue?
|
|
after_update :notify_issue_reopened, if: :was_reopened?
|
|
|
|
# Real-time updates
|
|
after_create_commit do
|
|
broadcast_refresh_to(project)
|
|
end
|
|
|
|
after_update_commit do
|
|
broadcast_refresh # Refreshes the issue show page
|
|
broadcast_refresh_to(project, "issues") # Refreshes the project's issues index
|
|
end
|
|
|
|
def self.group_event(event_payload, project)
|
|
fingerprint = generate_fingerprint(event_payload)
|
|
|
|
find_or_create_by(project: project, fingerprint: fingerprint) do |issue|
|
|
issue.title = extract_title(event_payload)
|
|
issue.exception_type = extract_exception_type(event_payload)
|
|
issue.first_seen = Time.current
|
|
issue.last_seen = Time.current
|
|
issue.status = :open
|
|
end
|
|
end
|
|
|
|
def self.generate_fingerprint(payload)
|
|
# Use Sentry's fingerprint if provided
|
|
if payload["fingerprint"].present?
|
|
payload["fingerprint"].join("::")
|
|
else
|
|
# Generate from exception type + location
|
|
type = payload.dig("exception", "values", 0, "type")
|
|
file = payload.dig("exception", "values", 0, "stacktrace", "frames", -1, "filename")
|
|
line = payload.dig("exception", "values", 0, "stacktrace", "frames", -1, "lineno")
|
|
|
|
# Fallback to message if no exception
|
|
if type.blank?
|
|
message = payload["message"] || "Unknown Error"
|
|
Digest::MD5.hexdigest(message)
|
|
else
|
|
"#{type}::#{file}::#{line}"
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.extract_title(payload)
|
|
payload.dig("exception", "values", 0, "value") ||
|
|
payload["message"] ||
|
|
payload.dig("exception", "values", 0, "type") ||
|
|
"Unknown Error"
|
|
end
|
|
|
|
def self.extract_exception_type(payload)
|
|
payload.dig("exception", "values", 0, "type")
|
|
end
|
|
|
|
def record_event!(timestamp: Time.current)
|
|
update!(
|
|
count: count + 1,
|
|
last_seen: timestamp
|
|
)
|
|
end
|
|
|
|
private
|
|
|
|
def notify_new_issue
|
|
IssueMailer.new_issue(self).deliver_later
|
|
end
|
|
|
|
def notify_issue_reopened
|
|
IssueMailer.issue_reopened(self).deliver_later
|
|
end
|
|
|
|
def should_notify_new_issue?
|
|
# Only notify for new issues in production environment or if explicitly enabled
|
|
Rails.env.production? || ENV['SPLAT_EMAIL_NOTIFICATIONS'] == 'true'
|
|
end
|
|
|
|
def was_reopened?
|
|
# Check if status changed from resolved to open
|
|
saved_change_to_status?(from: 1, to: 0) # resolved=1, open=0
|
|
end
|
|
end
|