diff options
author | Yorick Peterse <yorickpeterse@gmail.com> | 2018-03-07 12:55:18 +0000 |
---|---|---|
committer | Yorick Peterse <yorickpeterse@gmail.com> | 2018-03-07 12:55:18 +0000 |
commit | 0bae567369e51c9a8590101633adf435066ac18d (patch) | |
tree | 010a32e78504fb08048ca344d42226f5a43672e8 /app | |
parent | 001a28d04203feac77840b90024b769b442130b2 (diff) | |
parent | 9f7767079ef061e8ddce32976b1e4a5bef1822d7 (diff) | |
download | gitlab-ce-0bae567369e51c9a8590101633adf435066ac18d.tar.gz |
Merge branch '43460-track-projects-a-user-contributed-to' into 'master'
Keep track of projects a user interacted with
Closes #43460
See merge request gitlab-org/gitlab-ce!17327
Diffstat (limited to 'app')
-rw-r--r-- | app/models/event.rb | 8 | ||||
-rw-r--r-- | app/models/user_interacted_project.rb | 59 |
2 files changed, 67 insertions, 0 deletions
diff --git a/app/models/event.rb b/app/models/event.rb index be0fc7efa9a..17a198d52c7 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -65,6 +65,7 @@ class Event < ActiveRecord::Base # Callbacks after_create :reset_project_activity after_create :set_last_repository_updated_at, if: :push? + after_create :track_user_interacted_projects # Scopes scope :recent, -> { reorder(id: :desc) } @@ -389,4 +390,11 @@ class Event < ActiveRecord::Base Project.unscoped.where(id: project_id) .update_all(last_repository_updated_at: created_at) end + + def track_user_interacted_projects + # Note the call to .available? is due to earlier migrations + # that would otherwise conflict with the call to .track + # (because the table does not exist yet). + UserInteractedProject.track(self) if UserInteractedProject.available? + end end diff --git a/app/models/user_interacted_project.rb b/app/models/user_interacted_project.rb new file mode 100644 index 00000000000..dd55a6acb79 --- /dev/null +++ b/app/models/user_interacted_project.rb @@ -0,0 +1,59 @@ +class UserInteractedProject < ActiveRecord::Base + belongs_to :user + belongs_to :project + + validates :project_id, presence: true + validates :user_id, presence: true + + CACHE_EXPIRY_TIME = 1.day + + # Schema version required for this model + REQUIRED_SCHEMA_VERSION = 20180223120443 + + class << self + def track(event) + # For events without a project, we simply don't care. + # An example of this is the creation of a snippet (which + # is not related to any project). + return unless event.project_id + + attributes = { + project_id: event.project_id, + user_id: event.author_id + } + + cached_exists?(attributes) do + transaction(requires_new: true) do + begin + where(attributes).select(1).first || create!(attributes) + true # not caching the whole record here for now + rescue ActiveRecord::RecordNotUnique + # Note, above queries are not atomic and prone + # to race conditions (similar like #find_or_create!). + # In the case where we hit this, the record we want + # already exists - shortcut and return. + true + end + end + end + end + + # Check if we can safely call .track (table exists) + def available? + @available_flag ||= ActiveRecord::Migrator.current_version >= REQUIRED_SCHEMA_VERSION # rubocop:disable Gitlab/PredicateMemoization + end + + # Flushes cached information about schema + def reset_column_information + @available_flag = nil + super + end + + private + + def cached_exists?(project_id:, user_id:, &block) + cache_key = "user_interacted_projects:#{project_id}:#{user_id}" + Rails.cache.fetch(cache_key, expires_in: CACHE_EXPIRY_TIME, &block) + end + end +end |