Commit 2ebd0d2d authored by Sean McGivern's avatar Sean McGivern

Merge branch 'osw-fetch-latest-version-when-creating-suggestions' into 'master'

Enforce using the latest diff version when creating suggestions

Closes gitlab-ee#9876

See merge request gitlab-org/gitlab-ce!25441
parents a5174cf0 6fc010bb
...@@ -41,6 +41,14 @@ class DiffNote < Note ...@@ -41,6 +41,14 @@ class DiffNote < Note
create_note_diff_file(creation_params) create_note_diff_file(creation_params)
end end
# Returns the diff file from `position`
def latest_diff_file
strong_memoize(:latest_diff_file) do
position.diff_file(project.repository)
end
end
# Returns the diff file from `original_position`
def diff_file def diff_file
strong_memoize(:diff_file) do strong_memoize(:diff_file) do
enqueue_diff_file_creation_job if should_create_diff_file? enqueue_diff_file_creation_job if should_create_diff_file?
......
...@@ -19,11 +19,6 @@ class Suggestion < ApplicationRecord ...@@ -19,11 +19,6 @@ class Suggestion < ApplicationRecord
position.file_path position.file_path
end end
def diff_file
repository = project.repository
position.diff_file(repository)
end
# For now, suggestions only serve as a way to send patches that # For now, suggestions only serve as a way to send patches that
# will change a single line (being able to apply multiple in the same place), # will change a single line (being able to apply multiple in the same place),
# which explains `from_line` and `to_line` being the same line. # which explains `from_line` and `to_line` being the same line.
......
...@@ -15,7 +15,13 @@ module Suggestions ...@@ -15,7 +15,13 @@ module Suggestions
return error('The file has been changed') return error('The file has been changed')
end end
params = file_update_params(suggestion) diff_file = suggestion.note.latest_diff_file
unless diff_file
return error('The file was not found')
end
params = file_update_params(suggestion, diff_file)
result = ::Files::UpdateService.new(suggestion.project, @current_user, params).execute result = ::Files::UpdateService.new(suggestion.project, @current_user, params).execute
if result[:status] == :success if result[:status] == :success
...@@ -38,8 +44,8 @@ module Suggestions ...@@ -38,8 +44,8 @@ module Suggestions
suggestion.position.head_sha == suggestion.noteable.source_branch_sha suggestion.position.head_sha == suggestion.noteable.source_branch_sha
end end
def file_update_params(suggestion) def file_update_params(suggestion, diff_file)
blob = suggestion.diff_file.new_blob blob = diff_file.new_blob
file_path = suggestion.file_path file_path = suggestion.file_path
branch_name = suggestion.branch branch_name = suggestion.branch
file_content = new_file_content(suggestion, blob) file_content = new_file_content(suggestion, blob)
......
...@@ -9,6 +9,10 @@ module Suggestions ...@@ -9,6 +9,10 @@ module Suggestions
def execute def execute
return unless @note.supports_suggestion? return unless @note.supports_suggestion?
diff_file = @note.latest_diff_file
return unless diff_file
suggestions = Banzai::SuggestionsParser.parse(@note.note) suggestions = Banzai::SuggestionsParser.parse(@note.note)
# For single line suggestion we're only looking forward to # For single line suggestion we're only looking forward to
...@@ -20,7 +24,7 @@ module Suggestions ...@@ -20,7 +24,7 @@ module Suggestions
rows = rows =
suggestions.map.with_index do |suggestion, index| suggestions.map.with_index do |suggestion, index|
from_content = changing_lines(comment_line, comment_line) from_content = changing_lines(diff_file, comment_line, comment_line)
# The parsed suggestion doesn't have information about the correct # The parsed suggestion doesn't have information about the correct
# ending characters (we may have a line break, or not), so we take # ending characters (we may have a line break, or not), so we take
...@@ -44,8 +48,8 @@ module Suggestions ...@@ -44,8 +48,8 @@ module Suggestions
private private
def changing_lines(from_line, to_line) def changing_lines(diff_file, from_line, to_line)
@note.diff_file.new_blob_lines_between(from_line, to_line).join diff_file.new_blob_lines_between(from_line, to_line).join
end end
def line_break_chars(line) def line_break_chars(line)
......
---
title: Always fetch MR latest version when creating suggestions
merge_request: 25441
author:
type: fixed
...@@ -362,6 +362,17 @@ describe Suggestions::ApplyService do ...@@ -362,6 +362,17 @@ describe Suggestions::ApplyService do
project.add_maintainer(user) project.add_maintainer(user)
end end
context 'diff file was not found' do
it 'returns error message' do
expect(suggestion.note).to receive(:latest_diff_file) { nil }
result = subject.execute(suggestion)
expect(result).to eq(message: 'The file was not found',
status: :error)
end
end
context 'suggestion was already applied' do context 'suggestion was already applied' do
it 'returns success status' do it 'returns success status' do
result = subject.execute(suggestion) result = subject.execute(suggestion)
......
...@@ -9,14 +9,18 @@ describe Suggestions::CreateService do ...@@ -9,14 +9,18 @@ describe Suggestions::CreateService do
target_project: project_with_repo) target_project: project_with_repo)
end end
let(:position) do def build_position(args = {})
Gitlab::Diff::Position.new(old_path: "files/ruby/popen.rb", default_args = { old_path: "files/ruby/popen.rb",
new_path: "files/ruby/popen.rb", new_path: "files/ruby/popen.rb",
old_line: nil, old_line: nil,
new_line: 14, new_line: 14,
diff_refs: merge_request.diff_refs) diff_refs: merge_request.diff_refs }
Gitlab::Diff::Position.new(default_args.merge(args))
end end
let(:position) { build_position }
let(:markdown) do let(:markdown) do
<<-MARKDOWN.strip_heredoc <<-MARKDOWN.strip_heredoc
```suggestion ```suggestion
...@@ -74,6 +78,21 @@ describe Suggestions::CreateService do ...@@ -74,6 +78,21 @@ describe Suggestions::CreateService do
end end
end end
context 'should not create suggestions' do
let(:note) do
create(:diff_note_on_merge_request, project: project_with_repo,
noteable: merge_request,
position: position,
note: markdown)
end
it 'creates no suggestion when diff file is not found' do
expect(note).to receive(:latest_diff_file) { nil }
expect { subject.execute }.not_to change(Suggestion, :count)
end
end
context 'should create suggestions' do context 'should create suggestions' do
let(:note) do let(:note) do
create(:diff_note_on_merge_request, project: project_with_repo, create(:diff_note_on_merge_request, project: project_with_repo,
...@@ -104,6 +123,22 @@ describe Suggestions::CreateService do ...@@ -104,6 +123,22 @@ describe Suggestions::CreateService do
expect(suggestion_2).to have_attributes(from_content: " vars = {\n", expect(suggestion_2).to have_attributes(from_content: " vars = {\n",
to_content: " xpto\n baz\n") to_content: " xpto\n baz\n")
end end
context 'outdated position note' do
let!(:outdated_diff) { merge_request.merge_request_diff }
let!(:latest_diff) { merge_request.create_merge_request_diff }
let(:outdated_position) { build_position(diff_refs: outdated_diff.diff_refs) }
let(:position) { build_position(diff_refs: latest_diff.diff_refs) }
it 'uses the correct position when creating the suggestion' do
expect(note.position)
.to receive(:diff_file)
.with(project_with_repo.repository)
.and_call_original
subject.execute
end
end
end end
end end
end end
......
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