diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb index a404a64cfe9998b025fa61dff29c41bf15f14069..cf935f30ce36664f10784fe36d21761314552126 100644 --- a/app/services/issues/move_service.rb +++ b/app/services/issues/move_service.rb @@ -86,10 +86,13 @@ module Issues :snippets, :commits, :commit_ranges] cross_project_mentionables.each do |type| - references.public_send(type).each do |mentionable| - new_content.gsub!(mentionable.to_reference, - mentionable.to_reference(@project_new)) - end + referables = references.public_send(type) + + context = { objects: referables, project: @project_new, + pipeline: :reference_unfold } + + new_content = Banzai.render_result(new_content, context) + new_content = new_content[:output].to_s end new_content diff --git a/lib/banzai/filter/reference_unfold_filter.rb b/lib/banzai/filter/reference_unfold_filter.rb new file mode 100644 index 0000000000000000000000000000000000000000..a6145261651bceb4e3d4511f3622c73eda4c90c0 --- /dev/null +++ b/lib/banzai/filter/reference_unfold_filter.rb @@ -0,0 +1,46 @@ +module Banzai + module Filter + ## + # Filter than unfolds local references. + # + # Replaces all local references with project cross reference version + # in all objects passed to this filter in context. + # + # Requires objects array with each element implementing `Referable`. + # + class ReferenceUnfoldFilter < ReferenceFilter + def initialize(*) + super + + @objects = context[:objects] + @project = context[:project] + + unless @objects.all? { |object| object.respond_to?(:to_reference) } + raise StandardError, "No `to_reference` method implemented in one of the objects !" + end + + unless @project.kind_of?(Project) + raise StandardError, 'No valid project passed in context!' + end + end + + def call + @objects.each do |object| + pattern = /#{Regexp.escape(object.to_reference)}/ + replace_text_nodes_matching(pattern) do |content| + content.gsub(pattern, object.to_reference(@project)) + end + end + + doc + end + + private + + def validate + needs :project + needs :objects + end + end + end +end diff --git a/lib/banzai/pipeline/reference_unfold_pipeline.rb b/lib/banzai/pipeline/reference_unfold_pipeline.rb new file mode 100644 index 0000000000000000000000000000000000000000..5b555d7c2d79198ce59b6f29d9093fece7fc6a45 --- /dev/null +++ b/lib/banzai/pipeline/reference_unfold_pipeline.rb @@ -0,0 +1,9 @@ +module Banzai + module Pipeline + class ReferenceUnfoldPipeline < BasePipeline + def self.filters + [Filter::ReferenceUnfoldFilter] + end + end + end +end diff --git a/spec/lib/banzai/pipeline/reference_unfold_pipeline_spec.rb b/spec/lib/banzai/pipeline/reference_unfold_pipeline_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..af9d6f4ad0d1c383fba8253ba7110c480cc16da5 --- /dev/null +++ b/spec/lib/banzai/pipeline/reference_unfold_pipeline_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe Banzai::Pipeline::ReferenceUnfoldPipeline do + let(:text) { 'some text' } + let(:project) { create(:project) } + let(:objects) { [] } + + let(:result) do + described_class.to_html(text, project: project, objects: objects) + end + + context 'invalid initializers' do + subject { -> { result } } + + context 'project context is invalid' do + let(:project) { nil } + it { is_expected.to raise_error StandardError, /No valid project/ } + end + + context 'objects context is invalid' do + let(:objects) { ['issue'] } + it { is_expected.to raise_error StandardError, /No `to_reference` method/ } + end + end + + context 'multiple issues and merge requests referenced' do + subject { result } + + let(:main_project) { create(:project) } + + let(:issue_first) { create(:issue, project: main_project) } + let(:issue_second) { create(:issue, project: main_project) } + let(:merge_request) { create(:merge_request, source_project: main_project) } + + let(:objects) { [issue_first, issue_second, merge_request] } + + context 'plain text description' do + let(:text) { 'Description that references #1, #2 and !1' } + + it { is_expected.to include issue_first.to_reference(project) } + it { is_expected.to include issue_second.to_reference(project) } + it { is_expected.to include merge_request.to_reference(project) } + end + + context 'description with ignored elements' do + let(:text) do + <<-EOF + Hi. This references #1, but not `#2` + <pre>and not !1</pre> + EOF + end + + + it { is_expected.to include issue_first.to_reference(project) } + it { is_expected.to_not include issue_second.to_reference(project) } + it { is_expected.to_not include merge_request.to_reference(project) } + end + end +end diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index cd051175612c86009273342791822b2bac438cc8..d516bd4830acc3661561dad8732db2b83c49c592 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -11,7 +11,7 @@ describe Issues::MoveService, services: true do let(:old_issue) do create(:issue, title: title, description: description, - project: old_project, author: author) + project: old_project, author: author) end let(:move_service) do @@ -133,7 +133,7 @@ describe Issues::MoveService, services: true do before do create(:merge_request, source_project: old_project) create(:note, noteable: old_issue, project: old_project, author: author, - note: 'Note with reference to merge request !1') + note: 'Note with reference to merge request !1') end include_context 'issue move executed'