From c599b9593d9963e09b1dea0503bcd1788b4df3e6 Mon Sep 17 00:00:00 2001
From: Kassio Borges <kborges@gitlab.com>
Date: Sat, 22 Apr 2023 15:40:39 +0000
Subject: [PATCH] Do not block project overview when wiki repository fails

Changelog: fixed
---
 app/controllers/concerns/wiki_actions.rb      |  6 ++++
 app/models/wiki.rb                            |  9 +++--
 .../shared/empty_states/_wikis.html.haml      |  3 +-
 app/views/shared/wikis/empty.html.haml        | 10 +++++-
 locale/gitlab.pot                             |  3 ++
 .../projects/wikis_controller_spec.rb         |  2 +-
 .../wiki_actions_shared_examples.rb           | 33 ++++++++++++------
 .../models/wiki_shared_examples.rb            | 34 +++++++++++++++++++
 8 files changed, 85 insertions(+), 15 deletions(-)

diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb
index 08604a15e8a48..a007abacacc8e 100644
--- a/app/controllers/concerns/wiki_actions.rb
+++ b/app/controllers/concerns/wiki_actions.rb
@@ -55,6 +55,12 @@ module WikiActions
 
       render 'shared/wikis/git_error'
     end
+
+    rescue_from Gitlab::Git::Repository::NoRepository do
+      @error = _('Could not access the Wiki Repository at this time.')
+
+      render 'shared/wikis/empty'
+    end
   end
 
   def new
diff --git a/app/models/wiki.rb b/app/models/wiki.rb
index da8be7304e8ba..39d22ea0e0759 100644
--- a/app/models/wiki.rb
+++ b/app/models/wiki.rb
@@ -6,6 +6,7 @@ class Wiki
   include Repositories::CanHousekeepRepository
   include Gitlab::Utils::StrongMemoize
   include GlobalID::Identification
+  include Gitlab::Git::WrapsGitalyErrors
 
   extend ActiveModel::Naming
 
@@ -185,6 +186,8 @@ def create_wiki_repository
 
   def has_home_page?
     !!find_page(HOMEPAGE)
+  rescue StandardError
+    false
   end
 
   def empty?
@@ -413,7 +416,7 @@ def cleanup
   end
 
   def capture_git_error(action, &block)
-    yield block
+    wrapped_gitaly_errors(&block)
   rescue Gitlab::Git::Index::IndexError,
     Gitlab::Git::CommitError,
     Gitlab::Git::PreReceiveError,
@@ -491,7 +494,9 @@ def find_matched_file(title, version)
     escaped_path = RE2::Regexp.escape(sluggified_title(title))
     path_regexp = Gitlab::EncodingHelper.encode_utf8_no_detect("(?i)^#{escaped_path}\\.(#{file_extension_regexp})$")
 
-    matched_files = repository.search_files_by_regexp(path_regexp, version, limit: 1)
+    matched_files = capture_git_error(:find) do
+      repository.search_files_by_regexp(path_regexp, version, limit: 1)
+    end
     return if matched_files.blank?
 
     Gitlab::EncodingHelper.encode_utf8_no_detect(matched_files.first)
diff --git a/app/views/shared/empty_states/_wikis.html.haml b/app/views/shared/empty_states/_wikis.html.haml
index 8304a2f18a0a6..57f1c9d381e7a 100644
--- a/app/views/shared/empty_states/_wikis.html.haml
+++ b/app/views/shared/empty_states/_wikis.html.haml
@@ -1,7 +1,8 @@
 - layout_path = 'shared/empty_states/wikis_layout'
 - messages = wiki_empty_state_messages(@wiki)
+- hide_create = local_assigns[:hide_create]
 
-- if can?(current_user, :create_wiki, @wiki.container)
+- if !hide_create && can?(current_user, :create_wiki, @wiki.container)
   - create_path = wiki_page_path(@wiki, params[:id], view: 'create')
   - create_link = link_to s_('WikiEmpty|Create your first page'), create_path, class: 'btn gl-button btn-confirm', title: s_('WikiEmpty|Create your first page'), data: { qa_selector: 'create_first_page_link' }
 
diff --git a/app/views/shared/wikis/empty.html.haml b/app/views/shared/wikis/empty.html.haml
index c52ead74b4ceb..d30a37aaa3ebc 100644
--- a/app/views/shared/wikis/empty.html.haml
+++ b/app/views/shared/wikis/empty.html.haml
@@ -2,4 +2,12 @@
 - @right_sidebar = false
 - add_page_specific_style 'page_bundles/wiki'
 
-= render 'shared/empty_states/wikis'
+- if @error.present?
+  = render Pajamas::AlertComponent.new(alert_options: { id: 'error_explanation', class: 'gl-mb-3'},
+    dismissible: false,
+    variant: :danger) do |c|
+    = c.body do
+      %ul.gl-pl-4
+        = @error
+
+= render 'shared/empty_states/wikis', hide_create: @error.present?
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a2affaeb776aa..546e454e4403a 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -12041,6 +12041,9 @@ msgstr ""
 msgid "CorpusManagement|Total Size: %{totalSize}"
 msgstr ""
 
+msgid "Could not access the Wiki Repository at this time."
+msgstr ""
+
 msgid "Could not add admins as members"
 msgstr ""
 
diff --git a/spec/controllers/projects/wikis_controller_spec.rb b/spec/controllers/projects/wikis_controller_spec.rb
index 7243588681dd4..353cd62686f15 100644
--- a/spec/controllers/projects/wikis_controller_spec.rb
+++ b/spec/controllers/projects/wikis_controller_spec.rb
@@ -2,7 +2,7 @@
 
 require 'spec_helper'
 
-RSpec.describe Projects::WikisController do
+RSpec.describe Projects::WikisController, feature_category: :wiki do
   it_behaves_like 'wiki controller actions' do
     let(:container) { create(:project, :public, namespace: user.namespace) }
     let(:routing_params) { { namespace_id: container.namespace, project_id: container } }
diff --git a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
index 5d77ed5fdfc80..768b54dc73e6f 100644
--- a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
@@ -15,20 +15,33 @@
     sign_in(user)
   end
 
-  shared_examples 'recovers from git timeout' do
+  shared_examples 'recovers from git errors' do
     let(:method_name) { :page }
 
-    context 'when we encounter git command errors' do
+    context 'when we encounter CommandTimedOut error' do
       it 'renders the appropriate template', :aggregate_failures do
-        expect(controller).to receive(method_name) do
-          raise ::Gitlab::Git::CommandTimedOut, 'Deadline Exceeded'
-        end
+        expect(controller)
+          .to receive(method_name)
+          .and_raise(::Gitlab::Git::CommandTimedOut, 'Deadline Exceeded')
 
         request
 
         expect(response).to render_template('shared/wikis/git_error')
       end
     end
+
+    context 'when we encounter a NoRepository error' do
+      it 'renders the appropriate template', :aggregate_failures do
+        expect(controller)
+          .to receive(method_name)
+          .and_raise(Gitlab::Git::Repository::NoRepository)
+
+        request
+
+        expect(response).to render_template('shared/wikis/empty')
+        expect(assigns(:error)).to eq('Could not access the Wiki Repository at this time.')
+      end
+    end
   end
 
   describe 'GET #new' do
@@ -65,7 +78,7 @@
       get :pages, params: routing_params.merge(id: wiki_title)
     end
 
-    it_behaves_like 'recovers from git timeout' do
+    it_behaves_like 'recovers from git errors' do
       subject(:request) { get :pages, params: routing_params.merge(id: wiki_title) }
 
       let(:method_name) { :wiki_pages }
@@ -122,7 +135,7 @@
       end
     end
 
-    it_behaves_like 'recovers from git timeout' do
+    it_behaves_like 'recovers from git errors' do
       subject(:request) { get :history, params: routing_params.merge(id: wiki_title) }
 
       let(:allow_read_wiki)   { true }
@@ -170,7 +183,7 @@
       end
     end
 
-    it_behaves_like 'recovers from git timeout' do
+    it_behaves_like 'recovers from git errors' do
       subject(:request) { get :diff, params: routing_params.merge(id: wiki_title, version_id: wiki.repository.commit.id) }
     end
   end
@@ -185,7 +198,7 @@
     context 'when page exists' do
       let(:id) { wiki_title }
 
-      it_behaves_like 'recovers from git timeout'
+      it_behaves_like 'recovers from git errors'
 
       it 'renders the page' do
         request
@@ -366,7 +379,7 @@
     subject(:request) { get(:edit, params: routing_params.merge(id: id_param)) }
 
     it_behaves_like 'edit action'
-    it_behaves_like 'recovers from git timeout'
+    it_behaves_like 'recovers from git errors'
 
     context 'when page content encoding is valid' do
       render_views
diff --git a/spec/support/shared_examples/models/wiki_shared_examples.rb b/spec/support/shared_examples/models/wiki_shared_examples.rb
index b0a63a1332e90..017e51ecd242f 100644
--- a/spec/support/shared_examples/models/wiki_shared_examples.rb
+++ b/spec/support/shared_examples/models/wiki_shared_examples.rb
@@ -94,6 +94,40 @@
     end
   end
 
+  describe '#has_home_page?' do
+    context 'when home page exists' do
+      before do
+        wiki.repository.create_file(
+          user,
+          'home.md',
+          'home file',
+          branch_name: wiki.default_branch,
+          message: "created home page",
+          author_email: user.email,
+          author_name: user.name
+        )
+      end
+
+      it 'returns true' do
+        expect(wiki.has_home_page?).to eq(true)
+      end
+
+      it 'returns false when #find_page raise an error' do
+        allow(wiki)
+          .to receive(:find_page)
+          .and_raise(StandardError)
+
+        expect(wiki.has_home_page?).to eq(false)
+      end
+    end
+
+    context 'when home page does not exist' do
+      it 'returns false' do
+        expect(wiki.has_home_page?).to eq(false)
+      end
+    end
+  end
+
   describe '#to_global_id' do
     it 'returns a global ID' do
       expect(wiki.to_global_id.to_s).to eq("gid://gitlab/#{wiki.class.name}/#{wiki.id}")
-- 
GitLab