cf9ee8fda7
In https://gitlab.com/gitlab-org/gitlab-ce/builds/4218398, the build failed because the last_activity_at column was only being updated once per hour. We can fix this spec by stubbing out the throttling and adjusting the spec to test the right event timestamp.
352 lines
6.2 KiB
Ruby
352 lines
6.2 KiB
Ruby
class Event < ActiveRecord::Base
|
|
include Sortable
|
|
default_scope { where.not(author_id: nil) }
|
|
|
|
CREATED = 1
|
|
UPDATED = 2
|
|
CLOSED = 3
|
|
REOPENED = 4
|
|
PUSHED = 5
|
|
COMMENTED = 6
|
|
MERGED = 7
|
|
JOINED = 8 # User joined project
|
|
LEFT = 9 # User left project
|
|
DESTROYED = 10
|
|
|
|
RESET_PROJECT_ACTIVITY_INTERVAL = 1.hour
|
|
|
|
delegate :name, :email, to: :author, prefix: true, allow_nil: true
|
|
delegate :title, to: :issue, prefix: true, allow_nil: true
|
|
delegate :title, to: :merge_request, prefix: true, allow_nil: true
|
|
delegate :title, to: :note, prefix: true, allow_nil: true
|
|
|
|
belongs_to :author, class_name: "User"
|
|
belongs_to :project
|
|
belongs_to :target, polymorphic: true
|
|
|
|
# For Hash only
|
|
serialize :data
|
|
|
|
# Callbacks
|
|
after_create :reset_project_activity
|
|
|
|
# Scopes
|
|
scope :recent, -> { reorder(id: :desc) }
|
|
scope :code_push, -> { where(action: PUSHED) }
|
|
|
|
scope :in_projects, ->(projects) do
|
|
where(project_id: projects.map(&:id)).recent
|
|
end
|
|
|
|
scope :with_associations, -> { includes(project: :namespace) }
|
|
scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) }
|
|
|
|
class << self
|
|
def reset_event_cache_for(target)
|
|
Event.where(target_id: target.id, target_type: target.class.to_s).
|
|
order('id DESC').limit(100).
|
|
update_all(updated_at: Time.now)
|
|
end
|
|
|
|
def contributions
|
|
where("action = ? OR (target_type in (?) AND action in (?))",
|
|
Event::PUSHED, ["MergeRequest", "Issue"],
|
|
[Event::CREATED, Event::CLOSED, Event::MERGED])
|
|
end
|
|
|
|
def limit_recent(limit = 20, offset = nil)
|
|
recent.limit(limit).offset(offset)
|
|
end
|
|
end
|
|
|
|
def visible_to_user?(user = nil)
|
|
if push?
|
|
true
|
|
elsif membership_changed?
|
|
true
|
|
elsif created_project?
|
|
true
|
|
elsif issue? || issue_note?
|
|
Ability.allowed?(user, :read_issue, note? ? note_target : target)
|
|
else
|
|
((merge_request? || note?) && target.present?) || milestone?
|
|
end
|
|
end
|
|
|
|
def project_name
|
|
if project
|
|
project.name_with_namespace
|
|
else
|
|
"(deleted project)"
|
|
end
|
|
end
|
|
|
|
def target_title
|
|
target.try(:title)
|
|
end
|
|
|
|
def created?
|
|
action == CREATED
|
|
end
|
|
|
|
def push?
|
|
action == PUSHED && valid_push?
|
|
end
|
|
|
|
def merged?
|
|
action == MERGED
|
|
end
|
|
|
|
def closed?
|
|
action == CLOSED
|
|
end
|
|
|
|
def reopened?
|
|
action == REOPENED
|
|
end
|
|
|
|
def joined?
|
|
action == JOINED
|
|
end
|
|
|
|
def left?
|
|
action == LEFT
|
|
end
|
|
|
|
def destroyed?
|
|
action == DESTROYED
|
|
end
|
|
|
|
def commented?
|
|
action == COMMENTED
|
|
end
|
|
|
|
def membership_changed?
|
|
joined? || left?
|
|
end
|
|
|
|
def created_project?
|
|
created? && !target && target_type.nil?
|
|
end
|
|
|
|
def created_target?
|
|
created? && target
|
|
end
|
|
|
|
def milestone?
|
|
target_type == "Milestone"
|
|
end
|
|
|
|
def note?
|
|
target.is_a?(Note)
|
|
end
|
|
|
|
def issue?
|
|
target_type == "Issue"
|
|
end
|
|
|
|
def merge_request?
|
|
target_type == "MergeRequest"
|
|
end
|
|
|
|
def milestone
|
|
target if milestone?
|
|
end
|
|
|
|
def issue
|
|
target if issue?
|
|
end
|
|
|
|
def merge_request
|
|
target if merge_request?
|
|
end
|
|
|
|
def note
|
|
target if note?
|
|
end
|
|
|
|
def action_name
|
|
if push?
|
|
if new_ref?
|
|
"pushed new"
|
|
elsif rm_ref?
|
|
"deleted"
|
|
else
|
|
"pushed to"
|
|
end
|
|
elsif closed?
|
|
"closed"
|
|
elsif merged?
|
|
"accepted"
|
|
elsif joined?
|
|
'joined'
|
|
elsif left?
|
|
'left'
|
|
elsif destroyed?
|
|
'destroyed'
|
|
elsif commented?
|
|
"commented on"
|
|
elsif created_project?
|
|
if project.external_import?
|
|
"imported"
|
|
else
|
|
"created"
|
|
end
|
|
else
|
|
"opened"
|
|
end
|
|
end
|
|
|
|
def valid_push?
|
|
data[:ref] && ref_name.present?
|
|
rescue
|
|
false
|
|
end
|
|
|
|
def tag?
|
|
Gitlab::Git.tag_ref?(data[:ref])
|
|
end
|
|
|
|
def branch?
|
|
Gitlab::Git.branch_ref?(data[:ref])
|
|
end
|
|
|
|
def new_ref?
|
|
Gitlab::Git.blank_ref?(commit_from)
|
|
end
|
|
|
|
def rm_ref?
|
|
Gitlab::Git.blank_ref?(commit_to)
|
|
end
|
|
|
|
def md_ref?
|
|
!(rm_ref? || new_ref?)
|
|
end
|
|
|
|
def commit_from
|
|
data[:before]
|
|
end
|
|
|
|
def commit_to
|
|
data[:after]
|
|
end
|
|
|
|
def ref_name
|
|
if tag?
|
|
tag_name
|
|
else
|
|
branch_name
|
|
end
|
|
end
|
|
|
|
def branch_name
|
|
@branch_name ||= Gitlab::Git.ref_name(data[:ref])
|
|
end
|
|
|
|
def tag_name
|
|
@tag_name ||= Gitlab::Git.ref_name(data[:ref])
|
|
end
|
|
|
|
# Max 20 commits from push DESC
|
|
def commits
|
|
@commits ||= (data[:commits] || []).reverse
|
|
end
|
|
|
|
def commits_count
|
|
data[:total_commits_count] || commits.count || 0
|
|
end
|
|
|
|
def ref_type
|
|
tag? ? "tag" : "branch"
|
|
end
|
|
|
|
def push_with_commits?
|
|
!commits.empty? && commit_from && commit_to
|
|
end
|
|
|
|
def last_push_to_non_root?
|
|
branch? && project.default_branch != branch_name
|
|
end
|
|
|
|
def target_iid
|
|
target.respond_to?(:iid) ? target.iid : target_id
|
|
end
|
|
|
|
def commit_note?
|
|
target.for_commit?
|
|
end
|
|
|
|
def issue_note?
|
|
note? && target && target.for_issue?
|
|
end
|
|
|
|
def project_snippet_note?
|
|
target.for_snippet?
|
|
end
|
|
|
|
def note_target
|
|
target.noteable
|
|
end
|
|
|
|
def note_target_id
|
|
if commit_note?
|
|
target.commit_id
|
|
else
|
|
target.noteable_id.to_s
|
|
end
|
|
end
|
|
|
|
def note_target_reference
|
|
return unless note_target
|
|
|
|
# Commit#to_reference returns the full SHA, but we want the short one here
|
|
if commit_note?
|
|
note_target.short_id
|
|
else
|
|
note_target.to_reference
|
|
end
|
|
end
|
|
|
|
def note_target_type
|
|
if target.noteable_type.present?
|
|
target.noteable_type.titleize
|
|
else
|
|
"Wall"
|
|
end.downcase
|
|
end
|
|
|
|
def body?
|
|
if push?
|
|
push_with_commits? || rm_ref?
|
|
elsif note?
|
|
true
|
|
else
|
|
target.respond_to? :title
|
|
end
|
|
end
|
|
|
|
def reset_project_activity
|
|
return unless project
|
|
|
|
# Don't even bother obtaining a lock if the last update happened less than
|
|
# 60 minutes ago.
|
|
return if recent_update?
|
|
|
|
return unless try_obtain_lease
|
|
|
|
project.update_column(:last_activity_at, created_at)
|
|
end
|
|
|
|
private
|
|
|
|
def recent_update?
|
|
project.last_activity_at > RESET_PROJECT_ACTIVITY_INTERVAL.ago
|
|
end
|
|
|
|
def try_obtain_lease
|
|
Gitlab::ExclusiveLease.
|
|
new("project:update_last_activity_at:#{project.id}",
|
|
timeout: RESET_PROJECT_ACTIVITY_INTERVAL.to_i).
|
|
try_obtain
|
|
end
|
|
end
|