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