From 871cf24acc579b8950f4ddf3e1ccd1e124d2e8ec Mon Sep 17 00:00:00 2001
From: Nataliia Radina <nradina@gitlab.com>
Date: Wed, 21 Dec 2022 22:12:01 +0000
Subject: [PATCH] Add custom error for gitaly unavailable

Changelog: changed
---
 .../repository/components/tree_content.vue    | 17 +++++++++++---
 .../javascripts/repository/constants.js       |  6 +++++
 locale/gitlab.pot                             |  3 +++
 .../components/tree_content_spec.js           | 23 +++++++++++++++++--
 spec/frontend/repository/mock_data.js         |  9 ++++++++
 5 files changed, 53 insertions(+), 5 deletions(-)

diff --git a/app/assets/javascripts/repository/components/tree_content.vue b/app/assets/javascripts/repository/components/tree_content.vue
index 4a8f83458f485..f6d6004ba967a 100644
--- a/app/assets/javascripts/repository/components/tree_content.vue
+++ b/app/assets/javascripts/repository/components/tree_content.vue
@@ -2,12 +2,13 @@
 import paginatedTreeQuery from 'shared_queries/repository/paginated_tree.query.graphql';
 import { createAlert } from '~/flash';
 import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import { __ } from '~/locale';
 import {
   TREE_PAGE_SIZE,
   TREE_INITIAL_FETCH_COUNT,
   TREE_PAGE_LIMIT,
   COMMIT_BATCH_SIZE,
+  GITALY_UNAVAILABLE_CODE,
+  i18n,
 } from '../constants';
 import getRefMixin from '../mixins/get_ref';
 import projectPathQuery from '../queries/project_path.query.graphql';
@@ -17,6 +18,7 @@ import FilePreview from './preview/index.vue';
 import FileTable from './table/index.vue';
 
 export default {
+  i18n,
   components: {
     FileTable,
     FilePreview,
@@ -142,10 +144,19 @@ export default {
           }
         })
         .catch((error) => {
+          let gitalyUnavailableError;
+          if (error.graphQLErrors) {
+            gitalyUnavailableError = error.graphQLErrors.find(
+              (e) => e?.extensions?.code === GITALY_UNAVAILABLE_CODE,
+            );
+          }
+          const message = gitalyUnavailableError
+            ? this.$options.i18n.gitalyError
+            : this.$options.i18n.generalError;
           createAlert({
-            message: __('An error occurred while fetching folder content.'),
+            message,
+            captureError: true,
           });
-          throw error;
         });
     },
     normalizeData(key, data) {
diff --git a/app/assets/javascripts/repository/constants.js b/app/assets/javascripts/repository/constants.js
index e194bddcc5616..5098053c4f763 100644
--- a/app/assets/javascripts/repository/constants.js
+++ b/app/assets/javascripts/repository/constants.js
@@ -1,5 +1,6 @@
 import { __ } from '~/locale';
 
+export const GITALY_UNAVAILABLE_CODE = 'unavailable';
 export const TREE_PAGE_LIMIT = 1000; // the maximum amount of items per page
 export const TREE_PAGE_SIZE = 100; // the amount of items to be fetched per (batch) request
 export const TREE_INITIAL_FETCH_COUNT = TREE_PAGE_LIMIT / TREE_PAGE_SIZE; // the amount of (batch) requests to make
@@ -100,3 +101,8 @@ export const LEGACY_FILE_TYPES = [
   'cargo_toml',
   'go_mod',
 ];
+
+export const i18n = {
+  generalError: __('An error occurred while fetching folder content.'),
+  gitalyError: __('Error: Gitaly is unavailable. Contact your administrator.'),
+};
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8c78bdbee9046..d9d2ddad4582e 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -16068,6 +16068,9 @@ msgstr ""
 msgid "Error: Couldn't load some or all of the changes."
 msgstr ""
 
+msgid "Error: Gitaly is unavailable. Contact your administrator."
+msgstr ""
+
 msgid "Error: No AWS credentials were supplied"
 msgstr ""
 
diff --git a/spec/frontend/repository/components/tree_content_spec.js b/spec/frontend/repository/components/tree_content_spec.js
index 6eea66f1a7d12..f694c8e916638 100644
--- a/spec/frontend/repository/components/tree_content_spec.js
+++ b/spec/frontend/repository/components/tree_content_spec.js
@@ -5,19 +5,25 @@ import FilePreview from '~/repository/components/preview/index.vue';
 import FileTable from '~/repository/components/table/index.vue';
 import TreeContent from 'jh_else_ce/repository/components/tree_content.vue';
 import { loadCommits, isRequested, resetRequestedCommits } from '~/repository/commits_service';
+import waitForPromises from 'helpers/wait_for_promises';
+import { createAlert } from '~/flash';
+import { i18n } from '~/repository/constants';
+import { graphQLErrors } from '../mock_data';
 
 jest.mock('~/repository/commits_service', () => ({
   loadCommits: jest.fn(() => Promise.resolve()),
   isRequested: jest.fn(),
   resetRequestedCommits: jest.fn(),
 }));
+jest.mock('~/flash');
 
 let vm;
 let $apollo;
+const mockResponse = jest.fn().mockReturnValue(Promise.resolve({ data: {} }));
 
-function factory(path, data = () => ({})) {
+function factory(path, appoloMockResponse = mockResponse) {
   $apollo = {
-    query: jest.fn().mockReturnValue(Promise.resolve({ data: data() })),
+    query: appoloMockResponse,
   };
 
   vm = shallowMount(TreeContent, {
@@ -222,4 +228,17 @@ describe('Repository table component', () => {
       expect(loadCommits.mock.calls).toEqual([['', path, '', 0]]);
     });
   });
+
+  describe('error handling', () => {
+    const gitalyError = { graphQLErrors };
+    it.each`
+      error          | message
+      ${gitalyError} | ${i18n.gitalyError}
+      ${'Error'}     | ${i18n.generalError}
+    `('should show an expected error', async ({ error, message }) => {
+      factory('/', jest.fn().mockRejectedValue(error));
+      await waitForPromises();
+      expect(createAlert).toHaveBeenCalledWith({ message, captureError: true });
+    });
+  });
 });
diff --git a/spec/frontend/repository/mock_data.js b/spec/frontend/repository/mock_data.js
index c1b5f89c37f3b..28dd19c29e6cf 100644
--- a/spec/frontend/repository/mock_data.js
+++ b/spec/frontend/repository/mock_data.js
@@ -108,3 +108,12 @@ export const blobControlsDataMock = {
     },
   },
 };
+
+export const graphQLErrors = [
+  {
+    message: '14:failed to connect to all addresses.',
+    locations: [{ line: 16, column: 7 }],
+    path: ['project', 'repository', 'paginatedTree'],
+    extensions: { code: 'unavailable', gitaly_code: 14, service: 'git' },
+  },
+];
-- 
GitLab