Commit b633727c authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch '33896-security-dashboard-projects' into 'master'

Add users_security_dashboard_projects table

See merge request gitlab-org/gitlab!18708
parents 1db90a2e 0236fa62
---
title: Create a users_security_dashboard_projects table to store the projects a user has added to their personal security dashboard
merge_request: 18708
author:
type: added
# frozen_string_literal: true
class CreateUsersSecurityDashboardProjects < ActiveRecord::Migration[5.2]
DOWNTIME = false
INDEX_NAME = 'users_security_dashboard_projects_unique_index'
def change
create_table :users_security_dashboard_projects, id: false do |t|
t.references :user, null: false, foreign_key: { on_delete: :cascade }
t.references :project, null: false, index: false, foreign_key: { on_delete: :cascade }
end
add_index :users_security_dashboard_projects, [:project_id, :user_id], name: INDEX_NAME, unique: true
end
end
......@@ -3888,6 +3888,13 @@ ActiveRecord::Schema.define(version: 2019_11_05_094625) do
t.index ["user_id", "project_id"], name: "index_users_ops_dashboard_projects_on_user_id_and_project_id", unique: true
end
create_table "users_security_dashboard_projects", id: false, force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "project_id", null: false
t.index ["project_id", "user_id"], name: "users_security_dashboard_projects_unique_index", unique: true
t.index ["user_id"], name: "index_users_security_dashboard_projects_on_user_id"
end
create_table "users_star_projects", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "user_id", null: false
......@@ -4472,6 +4479,8 @@ ActiveRecord::Schema.define(version: 2019_11_05_094625) do
add_foreign_key "users", "namespaces", column: "managing_group_id", name: "fk_a4b8fefe3e", on_delete: :nullify
add_foreign_key "users_ops_dashboard_projects", "projects", on_delete: :cascade
add_foreign_key "users_ops_dashboard_projects", "users", on_delete: :cascade
add_foreign_key "users_security_dashboard_projects", "projects", on_delete: :cascade
add_foreign_key "users_security_dashboard_projects", "users", on_delete: :cascade
add_foreign_key "users_star_projects", "projects", name: "fk_22cd27ddfc", on_delete: :cascade
add_foreign_key "vulnerabilities", "epics", name: "fk_1d37cddf91", on_delete: :nullify
add_foreign_key "vulnerabilities", "milestones", column: "due_date_sourcing_milestone_id", name: "fk_7c5bb22a22", on_delete: :nullify
......
......@@ -42,6 +42,8 @@ module EE
has_many :users_ops_dashboard_projects
has_many :ops_dashboard_projects, through: :users_ops_dashboard_projects, source: :project
has_many :users_security_dashboard_projects
has_many :security_dashboard_projects, through: :users_security_dashboard_projects, source: :project
has_many :group_saml_identities, -> { where.not(saml_provider_id: nil) }, source: :identities, class_name: "::Identity"
......
# frozen_string_literal: true
class UsersSecurityDashboardProject < ApplicationRecord
SECURITY_DASHBOARD_PROJECTS_LIMIT = 1000
belongs_to :project
belongs_to :user
validates :user, presence: true
validates :project, presence: true
validates :project_id, uniqueness: { scope: [:user_id] }
validate :per_user_projects_limit
private
def per_user_projects_limit
if self.class.where(user: user).count >= SECURITY_DASHBOARD_PROJECTS_LIMIT
errors.add(:project, _('limit of %{project_limit} reached') % { project_limit: SECURITY_DASHBOARD_PROJECTS_LIMIT })
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :users_security_dashboard_project do
user
project
end
end
......@@ -24,6 +24,8 @@ describe User do
it { is_expected.to have_many(:reviews) }
it { is_expected.to have_many(:vulnerability_feedback) }
it { is_expected.to have_many(:path_locks).dependent(:destroy) }
it { is_expected.to have_many(:users_security_dashboard_projects) }
it { is_expected.to have_many(:security_dashboard_projects) }
end
describe 'nested attributes' do
......
# frozen_string_literal: true
require 'spec_helper'
describe UsersSecurityDashboardProject do
subject { build(:users_security_dashboard_project) }
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:user) }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:user) }
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_uniqueness_of(:project_id).scoped_to(:user_id) }
context 'when validating the number of projects a user can add to their dashboard' do
before do
stub_const("UsersSecurityDashboardProject::SECURITY_DASHBOARD_PROJECTS_LIMIT", 1)
end
it 'limits the number of projects per user' do
user = create(:user)
create(:users_security_dashboard_project, user: user)
dashboard_project = build(:users_security_dashboard_project, user: user)
expect(dashboard_project).to be_invalid
expect(dashboard_project.errors.full_messages).to include('Project limit of 1 reached')
end
it 'allows a user to add a project if they are under the limit' do
dashboard_project = build(:users_security_dashboard_project)
expect(dashboard_project).to be_valid
end
end
end
end
......@@ -20300,6 +20300,9 @@ msgstr ""
msgid "leave %{group_name}"
msgstr ""
msgid "limit of %{project_limit} reached"
msgstr ""
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment