Commit 9e63bc43 authored by Jan Provaznik's avatar Jan Provaznik

Add epic confidentiality attribute

It adds confidentiality attribute to Epic model and also
adds validations which assure that confidential epic
can have only confidential issues.
parent 972e5e68
# frozen_string_literal: true
class AddConfidentialAttributeToEpics < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:epics, :confidential, :boolean, default: false)
end
def down
remove_column(:epics, :confidential)
end
end
......@@ -2271,6 +2271,7 @@ CREATE TABLE public.epics (
state_id smallint DEFAULT 1 NOT NULL,
start_date_sourcing_epic_id integer,
due_date_sourcing_epic_id integer,
confidential boolean DEFAULT false NOT NULL,
external_key character varying(255)
);
......@@ -12925,6 +12926,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200326144443
20200326145443
20200330074719
20200330121000
20200330132913
20200331220930
\.
......
......@@ -55,6 +55,8 @@ module EE
validates :group, presence: true
validate :validate_parent, on: :create
validate :validate_confidential_issues_and_subepics
validate :validate_confidential_parent
alias_attribute :parent_ids, :parent_id
alias_method :issuing_parent, :group
......@@ -67,6 +69,7 @@ module EE
scope :in_issues, -> (issues) { joins(:epic_issues).where(epic_issues: { issue_id: issues }).distinct }
scope :has_parent, -> { where.not(parent_id: nil) }
scope :iid_starts_with, -> (query) { where("CAST(iid AS VARCHAR) LIKE ?", "#{sanitize_sql_like(query)}%") }
scope :public_only, -> { where(confidential: false) }
scope :within_timeframe, -> (start_date, end_date) do
where('start_date is not NULL or end_date is not NULL')
......@@ -385,5 +388,25 @@ module EE
hierarchy.base_and_ancestors(hierarchy_order: :asc)
end
def validate_confidential_issues_and_subepics
return unless confidential?
if issues.public_only.any?
errors.add :confidential, _('Cannot make epic confidential if it contains not-confidential issues')
end
if children.public_only.any?
errors.add :confidential, _('Cannot make epic confidential if it contains not-confidential sub-epics')
end
end
def validate_confidential_parent
return unless parent
if !confidential? && parent.confidential?
errors.add :confidential, _('Not-confidential epic cannot be assigned to a confidential parent epic')
end
end
end
end
......@@ -45,6 +45,7 @@ module EE
has_many :related_vulnerabilities, through: :vulnerability_links, source: :vulnerability
validates :weight, allow_nil: true, numericality: { greater_than_or_equal_to: 0 }
validate :validate_confidential_epic
after_create :update_generic_alert_title, if: :generic_alert_with_default_title?
end
......@@ -223,5 +224,13 @@ module EE
project.alerts_service_activated? &&
author == ::User.alert_bot
end
def validate_confidential_epic
return unless epic
if !confidential? && epic.confidential?
errors.add :issue, _('Cannot set confidential epic for not-confidential issue')
end
end
end
end
......@@ -15,4 +15,16 @@ class EpicIssue < ApplicationRecord
scope :in_epic, ->(epic_id) { where(epic_id: epic_id) }
scope :related_issues_for_batches, ->(epic_ids) { select(:id, :relative_position).where(epic_id: epic_ids) }
validate :validate_confidential_epic
private
def validate_confidential_epic
return unless epic && issue
if epic.confidential? && !issue.confidential?
errors.add :issue, _('Cannot set confidential epic for not-confidential issue')
end
end
end
---
title: Added confidential column to epics table
merge_request: 28428
author:
type: added
......@@ -15,6 +15,10 @@ FactoryBot.define do
due_date_is_fixed { true }
end
trait :confidential do
confidential { true }
end
trait :opened do
state { :opened }
end
......
......@@ -3,6 +3,29 @@
require 'spec_helper'
describe EpicIssue do
describe 'validations' do
let(:epic) { build(:epic) }
let(:confidential_epic) { build(:epic, :confidential) }
let(:issue) { build(:issue) }
let(:confidential_issue) { build(:issue, :confidential) }
it 'is valid to add not-confidential issue to not-confidential epic' do
expect(build(:epic_issue, epic: epic, issue: issue)).to be_valid
end
it 'is valid to add confidential issue to confidential epic' do
expect(build(:epic_issue, epic: confidential_epic, issue: confidential_issue)).to be_valid
end
it 'is valid to add confidential issue to not-confidential epic' do
expect(build(:epic_issue, epic: epic, issue: confidential_issue)).to be_valid
end
it 'is not valid to add not-confidential issue to confidential epic' do
expect(build(:epic_issue, epic: confidential_epic, issue: issue)).not_to be_valid
end
end
context "relative positioning" do
it_behaves_like "a class that supports relative positioning" do
let(:epic) { create(:epic) }
......
......@@ -19,6 +19,17 @@ describe Epic do
it { is_expected.to have_many(:user_mentions).class_name("EpicUserMention") }
end
describe 'scopes' do
describe '.public_only' do
it 'only returns public epics' do
public_epic = create(:epic)
create(:epic, confidential: true)
expect(described_class.public_only).to eq([public_epic])
end
end
end
describe 'validations' do
subject { build(:epic) }
......@@ -37,6 +48,41 @@ describe Epic do
expect(epic).not_to be_valid
end
it 'is valid if epic is confidential and has only confidential issues' do
issue = create(:issue, :confidential)
epic = create(:epic_issue, issue: issue).epic
epic.confidential = true
expect(epic).to be_valid
end
it 'is not valid if epic is confidential and has not-confidential issues' do
epic = create(:epic_issue).epic
epic.confidential = true
expect(epic).not_to be_valid
end
it 'is valid if epic is confidential and has only confidential subepics' do
epic = create(:epic, group: group)
create(:epic, :confidential, parent: epic, group: group)
epic.confidential = true
expect(epic).to be_valid
end
it 'is not valid if epic is confidential and has not-confidential subepics' do
epic = create(:epic, group: group)
create(:epic, parent: epic, group: group)
epic.confidential = true
expect(epic).not_to be_valid
end
end
describe 'modules' do
......
......@@ -105,9 +105,9 @@ describe Issue do
end
describe 'validations' do
subject { build(:issue) }
describe 'weight' do
subject { build(:issue) }
it 'is not valid when negative number' do
subject.weight = -1
......@@ -125,6 +125,26 @@ describe Issue do
expect(subject).to be_valid
end
end
describe 'confidential' do
subject { build(:issue, :confidential) }
it 'is valid when changing to not-confidential and is associated with not-confidential epic' do
subject.epic = build(:epic)
subject.confidential = false
expect(subject).to be_valid
end
it 'is not valid when changing to not-confidential and is associated with confidential epic' do
subject.epic = build(:epic, :confidential)
subject.confidential = false
expect(subject).not_to be_valid
end
end
end
describe 'relations' do
......
......@@ -50,7 +50,7 @@ describe 'Add an issue to an Epic' do
expect(response['errors']).to be_empty
expect(response['epicIssue']['iid']).to eq(issue.iid.to_s)
expect(response['epicIssue']['epic']['iid']).to eq(epic.iid.to_s)
expect(issue.epic).to eq(epic)
expect(issue.reload.epic).to eq(epic)
end
end
......
......@@ -239,6 +239,7 @@ describe EpicIssues::CreateService do
before do
group.add_developer(user)
create(:epic_issue, epic: epic, issue: issue)
issue.reload
end
let(:another_epic) { create(:epic, group: group) }
......
......@@ -3353,6 +3353,12 @@ msgstr ""
msgid "Cannot create the abuse report. This user has been blocked."
msgstr ""
msgid "Cannot make epic confidential if it contains not-confidential issues"
msgstr ""
msgid "Cannot make epic confidential if it contains not-confidential sub-epics"
msgstr ""
msgid "Cannot merge"
msgstr ""
......@@ -3371,6 +3377,9 @@ msgstr ""
msgid "Cannot refer to a group milestone by an internal id!"
msgstr ""
msgid "Cannot set confidential epic for not-confidential issue"
msgstr ""
msgid "Cannot show preview. For previews on sketch files, they must have the file format introduced by Sketch version 43 and above."
msgstr ""
......@@ -13598,6 +13607,9 @@ msgstr ""
msgid "Not started"
msgstr ""
msgid "Not-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
msgid "Note"
msgstr ""
......
......@@ -845,6 +845,7 @@ Epic:
- due_date_sourcing_epic_id
- health_status
- external_key
- confidential
EpicIssue:
- id
- relative_position
......
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