Resource event model
This commit is contained in:
parent
bd659f70b1
commit
6b2b3d7f49
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# == LabelEventable concern
|
||||
#
|
||||
# Contains functionality related to objects that support adding/removing labels.
|
||||
#
|
||||
# This concern is not used yet, it will be used for:
|
||||
# https://gitlab.com/gitlab-org/gitlab-ce/issues/48483
|
||||
|
||||
module LabelEventable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :resource_label_events
|
||||
end
|
||||
end
|
|
@ -12,6 +12,7 @@ class Issue < ActiveRecord::Base
|
|||
include TimeTrackable
|
||||
include ThrottledTouch
|
||||
include IgnorableColumn
|
||||
include LabelEventable
|
||||
|
||||
ignore_column :assignee_id, :branch_name, :deleted_at
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
include EachBatch
|
||||
include ThrottledTouch
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
include LabelEventable
|
||||
|
||||
ignore_column :locked_at,
|
||||
:ref_fetched,
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This model is not used yet, it will be used for:
|
||||
# https://gitlab.com/gitlab-org/gitlab-ce/issues/48483
|
||||
class ResourceLabelEvent < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
belongs_to :issue
|
||||
belongs_to :merge_request
|
||||
belongs_to :label
|
||||
|
||||
validates :user, presence: true, on: :create
|
||||
validates :label, presence: true, on: :create
|
||||
validate :exactly_one_issuable
|
||||
|
||||
enum action: {
|
||||
add: 1,
|
||||
remove: 2
|
||||
}
|
||||
|
||||
def self.issuable_columns
|
||||
%i(issue_id merge_request_id).freeze
|
||||
end
|
||||
|
||||
def issuable
|
||||
issue || merge_request
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def exactly_one_issuable
|
||||
if self.class.issuable_columns.count { |attr| self[attr] } != 1
|
||||
errors.add(:base, "Exactly one of #{self.class.issuable_columns.join(', ')} is required")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This service is not used yet, it will be used for:
|
||||
# https://gitlab.com/gitlab-org/gitlab-ce/issues/48483
|
||||
module ResourceEvents
|
||||
class ChangeLabelsService
|
||||
attr_reader :resource, :user
|
||||
|
||||
def initialize(resource, user)
|
||||
@resource, @user = resource, user
|
||||
end
|
||||
|
||||
def execute(added_labels: [], removed_labels: [])
|
||||
label_hash = {
|
||||
resource_column(resource) => resource.id,
|
||||
user_id: user.id,
|
||||
created_at: Time.now
|
||||
}
|
||||
|
||||
labels = added_labels.map do |label|
|
||||
label_hash.merge(label_id: label.id, action: ResourceLabelEvent.actions['add'])
|
||||
end
|
||||
labels += removed_labels.map do |label|
|
||||
label_hash.merge(label_id: label.id, action: ResourceLabelEvent.actions['remove'])
|
||||
end
|
||||
|
||||
Gitlab::Database.bulk_insert(ResourceLabelEvent.table_name, labels)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_column(resource)
|
||||
case resource
|
||||
when Issue
|
||||
:issue_id
|
||||
when MergeRequest
|
||||
:merge_request_id
|
||||
else
|
||||
raise ArgumentError, "Unknown resource type #{resource.class.name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add new model for tracking label events.
|
||||
merge_request:
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateResourceLabelEvents < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
create_table :resource_label_events, id: :bigserial do |t|
|
||||
t.integer :action, null: false
|
||||
t.references :issue, null: true, index: true, foreign_key: { on_delete: :cascade }
|
||||
t.references :merge_request, null: true, index: true, foreign_key: { on_delete: :cascade }
|
||||
t.references :label, index: true, foreign_key: { on_delete: :nullify }
|
||||
t.references :user, index: true, foreign_key: { on_delete: :nullify }
|
||||
t.datetime_with_timezone :created_at, null: false
|
||||
end
|
||||
end
|
||||
end
|
20
db/schema.rb
20
db/schema.rb
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20180722103201) do
|
||||
ActiveRecord::Schema.define(version: 20180726172057) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -1787,6 +1787,20 @@ ActiveRecord::Schema.define(version: 20180722103201) do
|
|||
add_index "remote_mirrors", ["last_successful_update_at"], name: "index_remote_mirrors_on_last_successful_update_at", using: :btree
|
||||
add_index "remote_mirrors", ["project_id"], name: "index_remote_mirrors_on_project_id", using: :btree
|
||||
|
||||
create_table "resource_label_events", id: :bigserial, force: :cascade do |t|
|
||||
t.integer "action", null: false
|
||||
t.integer "issue_id"
|
||||
t.integer "merge_request_id"
|
||||
t.integer "label_id"
|
||||
t.integer "user_id"
|
||||
t.datetime_with_timezone "created_at", null: false
|
||||
end
|
||||
|
||||
add_index "resource_label_events", ["issue_id"], name: "index_resource_label_events_on_issue_id", using: :btree
|
||||
add_index "resource_label_events", ["label_id"], name: "index_resource_label_events_on_label_id", using: :btree
|
||||
add_index "resource_label_events", ["merge_request_id"], name: "index_resource_label_events_on_merge_request_id", using: :btree
|
||||
add_index "resource_label_events", ["user_id"], name: "index_resource_label_events_on_user_id", using: :btree
|
||||
|
||||
create_table "routes", force: :cascade do |t|
|
||||
t.integer "source_id", null: false
|
||||
t.string "source_type", null: false
|
||||
|
@ -2337,6 +2351,10 @@ ActiveRecord::Schema.define(version: 20180722103201) do
|
|||
add_foreign_key "push_event_payloads", "events", name: "fk_36c74129da", on_delete: :cascade
|
||||
add_foreign_key "releases", "projects", name: "fk_47fe2a0596", on_delete: :cascade
|
||||
add_foreign_key "remote_mirrors", "projects", on_delete: :cascade
|
||||
add_foreign_key "resource_label_events", "issues", on_delete: :cascade
|
||||
add_foreign_key "resource_label_events", "labels", on_delete: :nullify
|
||||
add_foreign_key "resource_label_events", "merge_requests", on_delete: :cascade
|
||||
add_foreign_key "resource_label_events", "users", on_delete: :nullify
|
||||
add_foreign_key "services", "projects", name: "fk_71cce407f9", on_delete: :cascade
|
||||
add_foreign_key "snippets", "projects", name: "fk_be41fd4bb7", on_delete: :cascade
|
||||
add_foreign_key "subscriptions", "projects", on_delete: :cascade
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :resource_label_event do
|
||||
user { issue.project.creator }
|
||||
action :add
|
||||
label
|
||||
issue
|
||||
end
|
||||
end
|
|
@ -7,6 +7,7 @@ issues:
|
|||
- updated_by
|
||||
- milestone
|
||||
- notes
|
||||
- resource_label_events
|
||||
- label_links
|
||||
- labels
|
||||
- last_edited_by
|
||||
|
@ -76,6 +77,7 @@ merge_requests:
|
|||
- updated_by
|
||||
- milestone
|
||||
- notes
|
||||
- resource_label_events
|
||||
- label_links
|
||||
- labels
|
||||
- last_edited_by
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ResourceLabelEvent, type: :model do
|
||||
subject { build(:resource_label_event) }
|
||||
let(:issue) { create(:issue) }
|
||||
let(:merge_request) { create(:merge_request) }
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:user) }
|
||||
it { is_expected.to belong_to(:issue) }
|
||||
it { is_expected.to belong_to(:merge_request) }
|
||||
it { is_expected.to belong_to(:label) }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
it { is_expected.to be_valid }
|
||||
it { is_expected.to validate_presence_of(:label) }
|
||||
it { is_expected.to validate_presence_of(:user) }
|
||||
|
||||
describe 'Issuable validation' do
|
||||
it 'is invalid if issue_id and merge_request_id are missing' do
|
||||
subject.attributes = { issue: nil, merge_request: nil }
|
||||
|
||||
expect(subject).to be_invalid
|
||||
end
|
||||
|
||||
it 'is invalid if issue_id and merge_request_id are set' do
|
||||
subject.attributes = { issue: issue, merge_request: merge_request }
|
||||
|
||||
expect(subject).to be_invalid
|
||||
end
|
||||
|
||||
it 'is valid if only issue_id is set' do
|
||||
subject.attributes = { issue: issue, merge_request: nil }
|
||||
|
||||
expect(subject).to be_valid
|
||||
end
|
||||
|
||||
it 'is valid if only merge_request_id is set' do
|
||||
subject.attributes = { merge_request: merge_request, issue: nil }
|
||||
|
||||
expect(subject).to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,53 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe ResourceEvents::ChangeLabelsService do
|
||||
set(:project) { create(:project) }
|
||||
set(:author) { create(:user) }
|
||||
let(:resource) { create(:issue, project: project) }
|
||||
|
||||
describe '.change_labels' do
|
||||
subject { described_class.new(resource, author).execute(added_labels: added, removed_labels: removed) }
|
||||
|
||||
let(:labels) { create_list(:label, 2, project: project) }
|
||||
|
||||
def expect_label_event(event, label, action)
|
||||
expect(event.user).to eq(author)
|
||||
expect(event.label).to eq(label)
|
||||
expect(event.action).to eq(action)
|
||||
end
|
||||
|
||||
context 'when adding a label' do
|
||||
let(:added) { [labels[0]] }
|
||||
let(:removed) { [] }
|
||||
|
||||
it 'creates new label event' do
|
||||
expect { subject }.to change { resource.resource_label_events.count }.from(0).to(1)
|
||||
|
||||
expect_label_event(resource.resource_label_events.first, labels[0], 'add')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when removing a label' do
|
||||
let(:added) { [] }
|
||||
let(:removed) { [labels[1]] }
|
||||
|
||||
it 'creates new label event' do
|
||||
expect { subject }.to change { resource.resource_label_events.count }.from(0).to(1)
|
||||
|
||||
expect_label_event(resource.resource_label_events.first, labels[1], 'remove')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when both adding and removing labels' do
|
||||
let(:added) { [labels[0]] }
|
||||
let(:removed) { [labels[1]] }
|
||||
|
||||
it 'creates all label events in a single query' do
|
||||
expect(Gitlab::Database).to receive(:bulk_insert).once.and_call_original
|
||||
expect { subject }.to change { resource.resource_label_events.count }.from(0).to(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue