Commit bc6dda96 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch '198116-project-to_reference-does-not-produce-a-valid-reference' into 'master'

Resolve "Project#to_reference does not produce a valid reference"

Closes #198116

See merge request gitlab-org/gitlab!23452
parents cef5b4f1 56f43f29
...@@ -484,10 +484,10 @@ class Commit ...@@ -484,10 +484,10 @@ class Commit
end end
def commit_reference(from, referable_commit_id, full: false) def commit_reference(from, referable_commit_id, full: false)
reference = project.to_reference(from, full: full) base = project.to_reference_base(from, full: full)
if reference.present? if base.present?
"#{reference}#{self.class.reference_prefix}#{referable_commit_id}" "#{base}#{self.class.reference_prefix}#{referable_commit_id}"
else else
referable_commit_id referable_commit_id
end end
......
...@@ -92,7 +92,7 @@ class CommitRange ...@@ -92,7 +92,7 @@ class CommitRange
alias_method :id, :to_s alias_method :id, :to_s
def to_reference(from = nil, full: false) def to_reference(from = nil, full: false)
project_reference = project.to_reference(from, full: full) project_reference = project.to_reference_base(from, full: full)
if project_reference.present? if project_reference.present?
project_reference + self.class.reference_prefix + self.id project_reference + self.class.reference_prefix + self.id
...@@ -102,7 +102,7 @@ class CommitRange ...@@ -102,7 +102,7 @@ class CommitRange
end end
def reference_link_text(from = nil) def reference_link_text(from = nil)
project_reference = project.to_reference(from) project_reference = project.to_reference_base(from)
reference = ref_from + notation + ref_to reference = ref_from + notation + ref_to
if project_reference.present? if project_reference.present?
......
...@@ -23,6 +23,14 @@ module Referable ...@@ -23,6 +23,14 @@ module Referable
'' ''
end end
# If this referable object can serve as the base for the
# reference of child objects (e.g. projects are the base of
# issues), but it is formatted differently, then you may wish
# to override this method.
def to_reference_base(from = nil, full:)
to_reference(from, full: full)
end
def reference_link_text(from = nil) def reference_link_text(from = nil)
to_reference(from) to_reference(from)
end end
......
...@@ -173,7 +173,7 @@ class Issue < ApplicationRecord ...@@ -173,7 +173,7 @@ class Issue < ApplicationRecord
def to_reference(from = nil, full: false) def to_reference(from = nil, full: false)
reference = "#{self.class.reference_prefix}#{iid}" reference = "#{self.class.reference_prefix}#{iid}"
"#{project.to_reference(from, full: full)}#{reference}" "#{project.to_reference_base(from, full: full)}#{reference}"
end end
def suggested_branch_name def suggested_branch_name
......
...@@ -225,7 +225,7 @@ class Label < ApplicationRecord ...@@ -225,7 +225,7 @@ class Label < ApplicationRecord
reference = "#{self.class.reference_prefix}#{format_reference}" reference = "#{self.class.reference_prefix}#{format_reference}"
if from if from
"#{from.to_reference(target_project, full: full)}#{reference}" "#{from.to_reference_base(target_project, full: full)}#{reference}"
else else
reference reference
end end
......
...@@ -396,7 +396,7 @@ class MergeRequest < ApplicationRecord ...@@ -396,7 +396,7 @@ class MergeRequest < ApplicationRecord
def to_reference(from = nil, full: false) def to_reference(from = nil, full: false)
reference = "#{self.class.reference_prefix}#{iid}" reference = "#{self.class.reference_prefix}#{iid}"
"#{project.to_reference(from, full: full)}#{reference}" "#{project.to_reference_base(from, full: full)}#{reference}"
end end
def commits(limit: nil) def commits(limit: nil)
......
...@@ -228,7 +228,7 @@ class Milestone < ApplicationRecord ...@@ -228,7 +228,7 @@ class Milestone < ApplicationRecord
reference = "#{self.class.reference_prefix}#{format_reference}" reference = "#{self.class.reference_prefix}#{format_reference}"
if project if project
"#{project.to_reference(from, full: full)}#{reference}" "#{project.to_reference_base(from, full: full)}#{reference}"
else else
reference reference
end end
......
...@@ -1068,12 +1068,19 @@ class Project < ApplicationRecord ...@@ -1068,12 +1068,19 @@ class Project < ApplicationRecord
end end
end end
def to_reference_with_postfix # Produce a valid reference (see Referable#to_reference)
"#{to_reference(full: true)}#{self.class.reference_postfix}" #
# NB: For projects, all references are 'full' - i.e. they all include the
# full_path, rather than just the project name. For this reason, we ignore
# the value of `full:` passed to this method, which is part of the Referable
# interface.
def to_reference(from = nil, full: false)
base = to_reference_base(from, full: true)
"#{base}#{self.class.reference_postfix}"
end end
# `from` argument can be a Namespace or Project. # `from` argument can be a Namespace or Project.
def to_reference(from = nil, full: false) def to_reference_base(from = nil, full: false)
if full || cross_namespace_reference?(from) if full || cross_namespace_reference?(from)
full_path full_path
elsif cross_project_reference?(from) elsif cross_project_reference?(from)
......
...@@ -180,7 +180,7 @@ class Snippet < ApplicationRecord ...@@ -180,7 +180,7 @@ class Snippet < ApplicationRecord
reference = "#{self.class.reference_prefix}#{id}" reference = "#{self.class.reference_prefix}#{id}"
if project.present? if project.present?
"#{project.to_reference(from, full: full)}#{reference}" "#{project.to_reference_base(from, full: full)}#{reference}"
else else
reference reference
end end
......
...@@ -141,7 +141,7 @@ describe API::IssueLinks do ...@@ -141,7 +141,7 @@ describe API::IssueLinks do
it 'returns 201 when sending full path of target project' do it 'returns 201 when sending full path of target project' do
post api("/projects/#{project.id}/issues/#{issue.iid}/links", user), post api("/projects/#{project.id}/issues/#{issue.iid}/links", user),
params: { target_project_id: project.to_reference(full: true), target_issue_iid: target_issue.iid } params: { target_project_id: project.full_path, target_issue_iid: target_issue.iid }
expect_link_response expect_link_response
end end
......
...@@ -121,7 +121,7 @@ module Banzai ...@@ -121,7 +121,7 @@ module Banzai
def object_link_text(object, matches) def object_link_text(object, matches)
milestone_link = escape_once(super) milestone_link = escape_once(super)
reference = object.project&.to_reference(project) reference = object.project&.to_reference_base(project)
if reference.present? if reference.present?
"#{milestone_link} <i>in #{reference}</i>".html_safe "#{milestone_link} <i>in #{reference}</i>".html_safe
......
...@@ -104,7 +104,7 @@ module Banzai ...@@ -104,7 +104,7 @@ module Banzai
def link_to_project(project, link_content: nil) def link_to_project(project, link_content: nil)
url = urls.project_url(project, only_path: context[:only_path]) url = urls.project_url(project, only_path: context[:only_path])
data = data_attribute(project: project.id) data = data_attribute(project: project.id)
content = link_content || project.to_reference_with_postfix content = link_content || project.to_reference
link_tag(url, data, content, project.name) link_tag(url, data, content, project.name)
end end
......
...@@ -32,7 +32,7 @@ describe 'issue move to another project' do ...@@ -32,7 +32,7 @@ describe 'issue move to another project' do
let(:new_project) { create(:project) } let(:new_project) { create(:project) }
let(:new_project_search) { create(:project) } let(:new_project_search) { create(:project) }
let(:text) { "Text with #{mr.to_reference}" } let(:text) { "Text with #{mr.to_reference}" }
let(:cross_reference) { old_project.to_reference(new_project) } let(:cross_reference) { old_project.to_reference_base(new_project) }
before do before do
old_project.add_reporter(user) old_project.add_reporter(user)
......
...@@ -229,10 +229,10 @@ describe Banzai::Filter::CommitRangeReferenceFilter do ...@@ -229,10 +229,10 @@ describe Banzai::Filter::CommitRangeReferenceFilter do
end end
it 'ignores invalid commit IDs on the referenced project' do it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}" exp = act = "Fixed #{project2.to_reference_base}@#{commit1.id.reverse}...#{commit2.id}"
expect(reference_filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" exp = act = "Fixed #{project2.to_reference_base}@#{commit1.id}...#{commit2.id.reverse}"
expect(reference_filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
end end
......
...@@ -369,7 +369,7 @@ describe Banzai::Filter::LabelReferenceFilter do ...@@ -369,7 +369,7 @@ describe Banzai::Filter::LabelReferenceFilter do
end end
context 'with project reference' do context 'with project reference' do
let(:reference) { "#{project.to_reference}#{group_label.to_reference(format: :name)}" } let(:reference) { "#{project.to_reference_base}#{group_label.to_reference(format: :name)}" }
it 'links to a valid reference' do it 'links to a valid reference' do
doc = reference_filter("See #{reference}", project: project) doc = reference_filter("See #{reference}", project: project)
...@@ -385,7 +385,7 @@ describe Banzai::Filter::LabelReferenceFilter do ...@@ -385,7 +385,7 @@ describe Banzai::Filter::LabelReferenceFilter do
end end
it 'ignores invalid label names' do it 'ignores invalid label names' do
exp = act = %(Label #{project.to_reference}#{Label.reference_prefix}"#{group_label.name.reverse}") exp = act = %(Label #{project.to_reference_base}#{Label.reference_prefix}"#{group_label.name.reverse}")
expect(reference_filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
......
...@@ -367,15 +367,17 @@ describe Banzai::Filter::MilestoneReferenceFilter do ...@@ -367,15 +367,17 @@ describe Banzai::Filter::MilestoneReferenceFilter do
expect(doc.css('a').first.text).to eq(urls.milestone_url(milestone)) expect(doc.css('a').first.text).to eq(urls.milestone_url(milestone))
end end
it 'does not support cross-project references' do it 'does not support cross-project references', :aggregate_failures do
another_group = create(:group) another_group = create(:group)
another_project = create(:project, :public, group: group) another_project = create(:project, :public, group: group)
project_reference = another_project.to_reference(project) project_reference = another_project.to_reference_base(project)
input_text = "See #{project_reference}#{reference}"
milestone.update!(group: another_group) milestone.update!(group: another_group)
doc = reference_filter("See #{project_reference}#{reference}") doc = reference_filter(input_text)
expect(input_text).to match(Milestone.reference_pattern)
expect(doc.css('a')).to be_empty expect(doc.css('a')).to be_empty
end end
......
...@@ -10,7 +10,7 @@ describe Banzai::Filter::ProjectReferenceFilter do ...@@ -10,7 +10,7 @@ describe Banzai::Filter::ProjectReferenceFilter do
end end
def get_reference(project) def get_reference(project)
project.to_reference_with_postfix project.to_reference
end end
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
......
...@@ -8,7 +8,7 @@ describe Gitlab::Gfm::ReferenceRewriter do ...@@ -8,7 +8,7 @@ describe Gitlab::Gfm::ReferenceRewriter do
let(:new_project) { create(:project, name: 'new-project', group: group) } let(:new_project) { create(:project, name: 'new-project', group: group) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:old_project_ref) { old_project.to_reference(new_project) } let(:old_project_ref) { old_project.to_reference_base(new_project) }
let(:text) { 'some text' } let(:text) { 'some text' }
before do before do
......
...@@ -131,23 +131,19 @@ describe Project do ...@@ -131,23 +131,19 @@ describe Project do
end end
context 'when creating a new project' do context 'when creating a new project' do
it 'automatically creates a CI/CD settings row' do let_it_be(:project) { create(:project) }
project = create(:project)
it 'automatically creates a CI/CD settings row' do
expect(project.ci_cd_settings).to be_an_instance_of(ProjectCiCdSetting) expect(project.ci_cd_settings).to be_an_instance_of(ProjectCiCdSetting)
expect(project.ci_cd_settings).to be_persisted expect(project.ci_cd_settings).to be_persisted
end end
it 'automatically creates a container expiration policy row' do it 'automatically creates a container expiration policy row' do
project = create(:project)
expect(project.container_expiration_policy).to be_an_instance_of(ContainerExpirationPolicy) expect(project.container_expiration_policy).to be_an_instance_of(ContainerExpirationPolicy)
expect(project.container_expiration_policy).to be_persisted expect(project.container_expiration_policy).to be_persisted
end end
it 'automatically creates a Pages metadata row' do it 'automatically creates a Pages metadata row' do
project = create(:project)
expect(project.pages_metadatum).to be_an_instance_of(ProjectPagesMetadatum) expect(project.pages_metadatum).to be_an_instance_of(ProjectPagesMetadatum)
expect(project.pages_metadatum).to be_persisted expect(project.pages_metadatum).to be_persisted
end end
...@@ -532,86 +528,92 @@ describe Project do ...@@ -532,86 +528,92 @@ describe Project do
it { is_expected.to delegate_method(:last_pipeline).to(:commit).with_arguments(allow_nil: true) } it { is_expected.to delegate_method(:last_pipeline).to(:commit).with_arguments(allow_nil: true) }
end end
describe '#to_reference_with_postfix' do describe 'reference methods' do
it 'returns the full path with reference_postfix' do let_it_be(:owner) { create(:user, name: 'Gitlab') }
namespace = create(:namespace, path: 'sample-namespace') let_it_be(:namespace) { create(:namespace, name: 'Sample namespace', path: 'sample-namespace', owner: owner) }
project = create(:project, path: 'sample-project', namespace: namespace) let_it_be(:project) { create(:project, name: 'Sample project', path: 'sample-project', namespace: namespace) }
let_it_be(:group) { create(:group, name: 'Group', path: 'sample-group') }
let_it_be(:another_project) { create(:project, namespace: namespace) }
let_it_be(:another_namespace_project) { create(:project, name: 'another-project') }
expect(project.to_reference_with_postfix).to eq 'sample-namespace/sample-project>' describe '#to_reference' do
it 'returns the path with reference_postfix' do
expect(project.to_reference).to eq("#{project.full_path}>")
end end
it 'returns the path with reference_postfix when arg is self' do
expect(project.to_reference(project)).to eq("#{project.full_path}>")
end end
describe '#to_reference' do it 'returns the full_path with reference_postfix when full' do
let(:owner) { create(:user, name: 'Gitlab') } expect(project.to_reference(full: true)).to eq("#{project.full_path}>")
let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) } end
let(:project) { create(:project, path: 'sample-project', namespace: namespace) }
let(:group) { create(:group, name: 'Group', path: 'sample-group') }
it 'returns the full_path with reference_postfix when cross-project' do
expect(project.to_reference(build_stubbed(:project))).to eq("#{project.full_path}>")
end
end
describe '#to_reference_base' do
context 'when nil argument' do context 'when nil argument' do
it 'returns nil' do it 'returns nil' do
expect(project.to_reference).to be_nil expect(project.to_reference_base).to be_nil
end end
end end
context 'when full is true' do context 'when full is true' do
it 'returns complete path to the project' do it 'returns complete path to the project', :aggregate_failures do
expect(project.to_reference(full: true)).to eq 'sample-namespace/sample-project' be_full_path = eq('sample-namespace/sample-project')
expect(project.to_reference(project, full: true)).to eq 'sample-namespace/sample-project'
expect(project.to_reference(group, full: true)).to eq 'sample-namespace/sample-project' expect(project.to_reference_base(full: true)).to be_full_path
expect(project.to_reference_base(project, full: true)).to be_full_path
expect(project.to_reference_base(group, full: true)).to be_full_path
end end
end end
context 'when same project argument' do context 'when same project argument' do
it 'returns nil' do it 'returns nil' do
expect(project.to_reference(project)).to be_nil expect(project.to_reference_base(project)).to be_nil
end end
end end
context 'when cross namespace project argument' do context 'when cross namespace project argument' do
let(:another_namespace_project) { create(:project, name: 'another-project') }
it 'returns complete path to the project' do it 'returns complete path to the project' do
expect(project.to_reference(another_namespace_project)).to eq 'sample-namespace/sample-project' expect(project.to_reference_base(another_namespace_project)).to eq 'sample-namespace/sample-project'
end end
end end
context 'when same namespace / cross-project argument' do context 'when same namespace / cross-project argument' do
let(:another_project) { create(:project, namespace: namespace) }
it 'returns path to the project' do it 'returns path to the project' do
expect(project.to_reference(another_project)).to eq 'sample-project' expect(project.to_reference_base(another_project)).to eq 'sample-project'
end end
end end
context 'when different namespace / cross-project argument' do context 'when different namespace / cross-project argument with same owner' do
let(:another_namespace) { create(:namespace, path: 'another-namespace', owner: owner) } let(:another_namespace_same_owner) { create(:namespace, path: 'another-namespace', owner: owner) }
let(:another_project) { create(:project, path: 'another-project', namespace: another_namespace) } let(:another_project_same_owner) { create(:project, path: 'another-project', namespace: another_namespace_same_owner) }
it 'returns full path to the project' do it 'returns full path to the project' do
expect(project.to_reference(another_project)).to eq 'sample-namespace/sample-project' expect(project.to_reference_base(another_project_same_owner)).to eq 'sample-namespace/sample-project'
end end
end end
context 'when argument is a namespace' do context 'when argument is a namespace' do
context 'with same project path' do context 'with same project path' do
it 'returns path to the project' do it 'returns path to the project' do
expect(project.to_reference(namespace)).to eq 'sample-project' expect(project.to_reference_base(namespace)).to eq 'sample-project'
end end
end end
context 'with different project path' do context 'with different project path' do
it 'returns full path to the project' do it 'returns full path to the project' do
expect(project.to_reference(group)).to eq 'sample-namespace/sample-project' expect(project.to_reference_base(group)).to eq 'sample-namespace/sample-project'
end end
end end
end end
end end
describe '#to_human_reference' do describe '#to_human_reference' do
let(:owner) { create(:user, name: 'Gitlab') }
let(:namespace) { create(:namespace, name: 'Sample namespace', owner: owner) }
let(:project) { create(:project, name: 'Sample project', namespace: namespace) }
context 'when nil argument' do context 'when nil argument' do
it 'returns nil' do it 'returns nil' do
expect(project.to_human_reference).to be_nil expect(project.to_human_reference).to be_nil
...@@ -625,21 +627,18 @@ describe Project do ...@@ -625,21 +627,18 @@ describe Project do
end end
context 'when cross namespace project argument' do context 'when cross namespace project argument' do
let(:another_namespace_project) { create(:project, name: 'another-project') }
it 'returns complete name with namespace of the project' do it 'returns complete name with namespace of the project' do
expect(project.to_human_reference(another_namespace_project)).to eq 'Gitlab / Sample project' expect(project.to_human_reference(another_namespace_project)).to eq 'Gitlab / Sample project'
end end
end end
context 'when same namespace / cross-project argument' do context 'when same namespace / cross-project argument' do
let(:another_project) { create(:project, namespace: namespace) }
it 'returns name of the project' do it 'returns name of the project' do
expect(project.to_human_reference(another_project)).to eq 'Sample project' expect(project.to_human_reference(another_project)).to eq 'Sample project'
end end
end end
end end
end
describe '#merge_method' do describe '#merge_method' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
......
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