Commit 23639b80 authored by Vijay Hawoldar's avatar Vijay Hawoldar

Handle snippet file path errors in services

Invalid snippet file names will raise an error in Gitaly,
this error should be relayed (in a formatted manner) to the
user so they may rectify the issue
parent 4907a8d6
...@@ -65,11 +65,12 @@ module SnippetsActions ...@@ -65,11 +65,12 @@ module SnippetsActions
params[:line_ending] == 'raw' ? content : content.gsub(/\r\n/, "\n") params[:line_ending] == 'raw' ? content : content.gsub(/\r\n/, "\n")
end end
def check_repository_error def handle_repository_error(action)
repository_errors = Array(snippet.errors.delete(:repository)) errors = Array(snippet.errors.delete(:repository))
flash.now[:alert] = repository_errors.first if repository_errors.present? flash.now[:alert] = errors.first if errors.present?
recaptcha_check_with_fallback(repository_errors.empty?) { render :edit }
recaptcha_check_with_fallback(errors.empty?) { render action }
end end
def redirect_if_binary def redirect_if_binary
......
...@@ -52,15 +52,8 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -52,15 +52,8 @@ class Projects::SnippetsController < Projects::ApplicationController
create_params = snippet_params.merge(spammable_params) create_params = snippet_params.merge(spammable_params)
service_response = Snippets::CreateService.new(project, current_user, create_params).execute service_response = Snippets::CreateService.new(project, current_user, create_params).execute
@snippet = service_response.payload[:snippet] @snippet = service_response.payload[:snippet]
repository_operation_error = service_response.error? && !@snippet.persisted? && @snippet.valid?
if repository_operation_error handle_repository_error(:new)
flash.now[:alert] = service_response.message
render :new
else
recaptcha_check_with_fallback { render :new }
end
end end
def update def update
...@@ -69,7 +62,7 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -69,7 +62,7 @@ class Projects::SnippetsController < Projects::ApplicationController
service_response = Snippets::UpdateService.new(project, current_user, update_params).execute(@snippet) service_response = Snippets::UpdateService.new(project, current_user, update_params).execute(@snippet)
@snippet = service_response.payload[:snippet] @snippet = service_response.payload[:snippet]
check_repository_error handle_repository_error(:edit)
end end
def show def show
......
...@@ -52,12 +52,9 @@ class SnippetsController < ApplicationController ...@@ -52,12 +52,9 @@ class SnippetsController < ApplicationController
create_params = snippet_params.merge(spammable_params) create_params = snippet_params.merge(spammable_params)
service_response = Snippets::CreateService.new(nil, current_user, create_params).execute service_response = Snippets::CreateService.new(nil, current_user, create_params).execute
@snippet = service_response.payload[:snippet] @snippet = service_response.payload[:snippet]
repository_operation_error = service_response.error? && !@snippet.persisted? && @snippet.valid?
if repository_operation_error if service_response.error? && @snippet.errors[:repository].present?
flash.now[:alert] = service_response.message handle_repository_error(:new)
render :new
else else
move_temporary_files if @snippet.valid? && params[:files] move_temporary_files if @snippet.valid? && params[:files]
...@@ -71,7 +68,7 @@ class SnippetsController < ApplicationController ...@@ -71,7 +68,7 @@ class SnippetsController < ApplicationController
service_response = Snippets::UpdateService.new(nil, current_user, update_params).execute(@snippet) service_response = Snippets::UpdateService.new(nil, current_user, update_params).execute(@snippet)
@snippet = service_response.payload[:snippet] @snippet = service_response.payload[:snippet]
check_repository_error handle_repository_error(:edit)
end end
def show def show
......
...@@ -44,6 +44,9 @@ class SnippetRepository < ApplicationRecord ...@@ -44,6 +44,9 @@ class SnippetRepository < ApplicationRecord
Gitlab::Git::PreReceiveError, Gitlab::Git::PreReceiveError,
Gitlab::Git::CommandError, Gitlab::Git::CommandError,
ArgumentError => error ArgumentError => error
logger.error(message: "Snippet git error. Reason: #{error.message}", snippet: snippet.id)
raise commit_error_exception(error) raise commit_error_exception(error)
end end
...@@ -91,7 +94,7 @@ class SnippetRepository < ApplicationRecord ...@@ -91,7 +94,7 @@ class SnippetRepository < ApplicationRecord
def commit_error_exception(err) def commit_error_exception(err)
if invalid_path_error?(err) if invalid_path_error?(err)
InvalidPathError.new('Invalid Path') # To avoid returning the message with the path included InvalidPathError.new('Invalid file name') # To avoid returning the message with the path included
elsif invalid_signature_error?(err) elsif invalid_signature_error?(err)
InvalidSignatureError.new(err.message) InvalidSignatureError.new(err.message)
else else
......
...@@ -11,5 +11,22 @@ module Snippets ...@@ -11,5 +11,22 @@ module Snippets
payload: { snippet: snippet } payload: { snippet: snippet }
) )
end end
def add_snippet_repository_error(snippet:, error:)
message = repository_error_message(error)
snippet.errors.add(:repository, message)
end
def repository_error_message(error)
message = self.is_a?(Snippets::CreateService) ? _("Error creating the snippet") : _("Error updating the snippet")
# We only want to include additional error detail in the message
# if the error is not a CommitError because we cannot guarantee the message
# will be user-friendly
message += " - #{error.message}" unless error.instance_of?(SnippetRepository::CommitError)
message
end
end end
end end
...@@ -60,7 +60,7 @@ module Snippets ...@@ -60,7 +60,7 @@ module Snippets
@snippet = @snippet.dup @snippet = @snippet.dup
end end
@snippet.errors.add(:base, e.message) add_snippet_repository_error(snippet: @snippet, error: e)
false false
end end
......
...@@ -54,7 +54,8 @@ module Snippets ...@@ -54,7 +54,8 @@ module Snippets
snippet.save snippet.save
end end
snippet.errors.add(:repository, 'Error updating the snippet') add_snippet_repository_error(snippet: snippet, error: e)
log_error(e.message) log_error(e.message)
# If the commit action failed we remove it because # If the commit action failed we remove it because
......
---
title: Modify Snippet git path errors to be more helpful
merge_request: 31333
author:
type: changed
...@@ -8,8 +8,6 @@ msgid "" ...@@ -8,8 +8,6 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gitlab 1.0.0\n" "Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-04-24 17:33-0400\n"
"PO-Revision-Date: 2020-04-24 17:33-0400\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n" "Language: \n"
...@@ -8449,6 +8447,9 @@ msgstr "" ...@@ -8449,6 +8447,9 @@ msgstr ""
msgid "Error creating new iteration" msgid "Error creating new iteration"
msgstr "" msgstr ""
msgid "Error creating the snippet"
msgstr ""
msgid "Error deleting %{issuableType}" msgid "Error deleting %{issuableType}"
msgstr "" msgstr ""
...@@ -8569,6 +8570,9 @@ msgstr "" ...@@ -8569,6 +8570,9 @@ msgstr ""
msgid "Error updating status of to-do item." msgid "Error updating status of to-do item."
msgstr "" msgstr ""
msgid "Error updating the snippet"
msgstr ""
msgid "Error uploading file" msgid "Error uploading file"
msgstr "" msgstr ""
......
...@@ -99,7 +99,7 @@ shared_examples_for 'snippet editor' do ...@@ -99,7 +99,7 @@ shared_examples_for 'snippet editor' do
end end
context 'when the git operation fails' do context 'when the git operation fails' do
let(:error) { 'This is a git error' } let(:error) { 'Error creating the snippet' }
before do before do
allow_next_instance_of(Snippets::CreateService) do |instance| allow_next_instance_of(Snippets::CreateService) do |instance|
......
...@@ -42,7 +42,7 @@ describe 'Projects > Snippets > User updates a snippet', :js do ...@@ -42,7 +42,7 @@ describe 'Projects > Snippets > User updates a snippet', :js do
context 'when the git operation fails' do context 'when the git operation fails' do
before do before do
allow_next_instance_of(Snippets::UpdateService) do |instance| allow_next_instance_of(Snippets::UpdateService) do |instance|
allow(instance).to receive(:create_commit).and_raise(StandardError) allow(instance).to receive(:create_commit).and_raise(StandardError, 'Error Message')
end end
fill_in('project_snippet_title', with: 'Snippet new title') fill_in('project_snippet_title', with: 'Snippet new title')
...@@ -52,7 +52,7 @@ describe 'Projects > Snippets > User updates a snippet', :js do ...@@ -52,7 +52,7 @@ describe 'Projects > Snippets > User updates a snippet', :js do
end end
it 'renders edit page and displays the error' do it 'renders edit page and displays the error' do
expect(page.find('.flash-container span').text).to eq('Error updating the snippet') expect(page.find('.flash-container span').text).to eq('Error updating the snippet - Error Message')
expect(page).to have_content('Edit Snippet') expect(page).to have_content('Edit Snippet')
end end
end end
......
...@@ -79,7 +79,7 @@ shared_examples_for 'snippet editor' do ...@@ -79,7 +79,7 @@ shared_examples_for 'snippet editor' do
end end
context 'when the git operation fails' do context 'when the git operation fails' do
let(:error) { 'This is a git error' } let(:error) { 'Error creating the snippet' }
before do before do
allow_next_instance_of(Snippets::CreateService) do |instance| allow_next_instance_of(Snippets::CreateService) do |instance|
......
...@@ -73,7 +73,7 @@ describe 'User edits snippet', :js do ...@@ -73,7 +73,7 @@ describe 'User edits snippet', :js do
context 'when the git operation fails' do context 'when the git operation fails' do
before do before do
allow_next_instance_of(Snippets::UpdateService) do |instance| allow_next_instance_of(Snippets::UpdateService) do |instance|
allow(instance).to receive(:create_commit).and_raise(StandardError) allow(instance).to receive(:create_commit).and_raise(StandardError, 'Error Message')
end end
fill_in 'personal_snippet_title', with: 'New Snippet Title' fill_in 'personal_snippet_title', with: 'New Snippet Title'
...@@ -83,7 +83,7 @@ describe 'User edits snippet', :js do ...@@ -83,7 +83,7 @@ describe 'User edits snippet', :js do
end end
it 'renders edit page and displays the error' do it 'renders edit page and displays the error' do
expect(page.find('.flash-container span').text).to eq('Error updating the snippet') expect(page.find('.flash-container span').text).to eq('Error updating the snippet - Error Message')
expect(page).to have_content('Edit Snippet') expect(page).to have_content('Edit Snippet')
end end
end end
......
...@@ -169,8 +169,8 @@ describe Snippets::CreateService do ...@@ -169,8 +169,8 @@ describe Snippets::CreateService do
expect { subject }.not_to change { Snippet.count } expect { subject }.not_to change { Snippet.count }
end end
it 'returns the error' do it 'returns a generic creation error' do
expect(snippet.errors.full_messages).to include('Repository could not be created') expect(snippet.errors[:repository]).to eq ['Error creating the snippet - Repository could not be created']
end end
it 'does not return a snippet with an id' do it 'does not return a snippet with an id' do
...@@ -178,6 +178,14 @@ describe Snippets::CreateService do ...@@ -178,6 +178,14 @@ describe Snippets::CreateService do
end end
end end
context 'when repository creation fails with invalid file name' do
let(:extra_opts) { { file_name: 'invalid://file/name/here' } }
it 'returns an appropriate error' do
expect(snippet.errors[:repository]).to eq ['Error creating the snippet - Invalid file name']
end
end
context 'when the commit action fails' do context 'when the commit action fails' do
before do before do
allow_next_instance_of(SnippetRepository) do |instance| allow_next_instance_of(SnippetRepository) do |instance|
...@@ -209,11 +217,11 @@ describe Snippets::CreateService do ...@@ -209,11 +217,11 @@ describe Snippets::CreateService do
subject subject
end end
it 'returns the error' do it 'returns a generic error' do
response = subject response = subject
expect(response).to be_error expect(response).to be_error
expect(response.payload[:snippet].errors.full_messages).to eq ['foobar'] expect(response.payload[:snippet].errors[:repository]).to eq ['Error creating the snippet']
end end
end end
......
...@@ -121,7 +121,7 @@ describe Snippets::UpdateService do ...@@ -121,7 +121,7 @@ describe Snippets::UpdateService do
response = subject response = subject
expect(response).to be_error expect(response).to be_error
expect(response.payload[:snippet].errors[:repository].to_sentence).to eq 'Error updating the snippet' expect(response.payload[:snippet].errors[:repository].to_sentence).to eq 'Error updating the snippet - Repository could not be created'
end end
it 'does not try to commit file' do it 'does not try to commit file' do
......
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