From c6bdf448dbe6136f26413cd96db7c33a36ade456 Mon Sep 17 00:00:00 2001
From: Ahmed Hemdan <ahemdan@gitlab.com>
Date: Wed, 19 Jul 2023 13:20:20 +0000
Subject: [PATCH] Raise a generic exception in
 CiConfiguration::BaseCreateService

---
 app/graphql/types/project_type.rb             |  2 +-
 .../ci_configuration/base_create_service.rb   | 12 +--
 .../sast/components/app.vue                   |  4 +-
 .../sast/components/app_spec.js               | 75 +++++++------------
 .../security_configuration/sast/constants.js  |  1 -
 spec/graphql/types/project_type_spec.rb       |  2 +-
 .../sast_create_service_spec.rb               |  9 ++-
 .../create_service_shared_examples.rb         | 19 +++--
 8 files changed, 55 insertions(+), 69 deletions(-)

diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 992663b4d987..9d2a57388c71 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -707,7 +707,7 @@ def sast_ci_configuration
 
       if project.repository.empty?
         raise Gitlab::Graphql::Errors::MutationError,
-            Gitlab::Utils::ErrorMessage.to_user_facing(_(format('You must %s before using Security features.', add_file_docs_link.html_safe)).html_safe)
+              _(format('You must %s before using Security features.', add_file_docs_link.html_safe)).html_safe
       end
 
       ::Security::CiConfiguration::SastParserService.new(object).configuration
diff --git a/app/services/security/ci_configuration/base_create_service.rb b/app/services/security/ci_configuration/base_create_service.rb
index b60a949fd4ea..9c3cd6b25fbc 100644
--- a/app/services/security/ci_configuration/base_create_service.rb
+++ b/app/services/security/ci_configuration/base_create_service.rb
@@ -15,12 +15,13 @@ def execute
         if project.repository.empty? && !(@params && @params[:initialize_with_sast])
           docs_link = ActionController::Base.helpers.link_to _('add at least one file to the repository'),
                               Rails.application.routes.url_helpers.help_page_url('user/project/repository/index.md',
-                                                                                 anchor: 'add-files-to-a-repository'),
+                                                                                anchor: 'add-files-to-a-repository'),
                               target: '_blank',
                               rel: 'noopener noreferrer'
-          raise Gitlab::Graphql::Errors::MutationError,
-                Gitlab::Utils::ErrorMessage.to_user_facing(
-                  _(format('You must %s before using Security features.', docs_link.html_safe)).html_safe)
+
+          return ServiceResponse.error(
+            message: _(format('You must %s before using Security features.', docs_link)).html_safe
+          )
         end
 
         project.repository.add_branch(current_user, branch_name, project.default_branch)
@@ -59,8 +60,7 @@ def existing_gitlab_ci_content
         YAML.safe_load(@gitlab_ci_yml) if @gitlab_ci_yml
       rescue Psych::BadAlias
         raise Gitlab::Graphql::Errors::MutationError,
-        Gitlab::Utils::ErrorMessage.to_user_facing(
-          _(".gitlab-ci.yml with aliases/anchors is not supported. Please change the CI configuration manually."))
+          _(".gitlab-ci.yml with aliases/anchors is not supported. Please change the CI configuration manually.")
       rescue Psych::Exception => e
         Gitlab::AppLogger.error("Failed to process existing .gitlab-ci.yml: #{e.message}")
         raise Gitlab::Graphql::Errors::MutationError,
diff --git a/ee/app/assets/javascripts/security_configuration/sast/components/app.vue b/ee/app/assets/javascripts/security_configuration/sast/components/app.vue
index 80c2e712035a..9c84d4e34099 100644
--- a/ee/app/assets/javascripts/security_configuration/sast/components/app.vue
+++ b/ee/app/assets/javascripts/security_configuration/sast/components/app.vue
@@ -1,6 +1,5 @@
 <script>
 import { GlAlert, GlLink, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
-import { parseErrorMessage } from '~/lib/utils/error_message';
 import { __, s__ } from '~/locale';
 import DismissibleFeedbackAlert from '~/vue_shared/components/dismissible_feedback_alert.vue';
 import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@@ -80,8 +79,9 @@ export default {
   },
   methods: {
     onError(error) {
+      const { gqlError, networkError } = error;
       this.hasLoadingError = true;
-      this.errorText = parseErrorMessage(error, this.$options.i18n.genericErrorText);
+      this.errorText = networkError ? this.$options.i18n.genericErrorText : gqlError.message;
     },
   },
   feedbackIssue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/225991',
diff --git a/ee/spec/frontend/security_configuration/sast/components/app_spec.js b/ee/spec/frontend/security_configuration/sast/components/app_spec.js
index 1e3eeda3474e..1b5f6b017e2b 100644
--- a/ee/spec/frontend/security_configuration/sast/components/app_spec.js
+++ b/ee/spec/frontend/security_configuration/sast/components/app_spec.js
@@ -1,16 +1,15 @@
 import { GlLink, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
-import { merge } from 'lodash';
 import Vue from 'vue';
 import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
 import ConfigurationPageLayout from 'ee/security_configuration/components/configuration_page_layout.vue';
 import SASTConfigurationApp, { i18n } from 'ee/security_configuration/sast/components/app.vue';
 import ConfigurationForm from 'ee/security_configuration/sast/components/configuration_form.vue';
 import sastCiConfigurationQuery from 'ee/security_configuration/sast/graphql/sast_ci_configuration.query.graphql';
-import createMockApollo from 'helpers/mock_apollo_helper';
 import waitForPromises from 'helpers/wait_for_promises';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
 import { sastCiConfigurationQueryResponse } from '../mock_data';
-import { specificErrorMessage, technicalErrorMessage } from '../constants';
+import { specificErrorMessage } from '../constants';
 
 Vue.use(VueApollo);
 
@@ -22,33 +21,23 @@ describe('SAST Configuration App', () => {
 
   const pendingHandler = () => new Promise(() => {});
   const successHandler = () => sastCiConfigurationQueryResponse;
-  // Prefixed with window.gon.uf_error_prefix as used in lib/gitlab/utils/error_message.rb to indicate a user facing error
-  const failureHandlerSpecific = () => ({
-    errors: [{ message: `${window.gon.uf_error_prefix} ${specificErrorMessage}` }],
-  });
-  const failureHandlerGeneric = () => ({
-    errors: [{ message: technicalErrorMessage }],
-  });
-  const createMockApolloProvider = (handler) =>
-    createMockApollo([[sastCiConfigurationQuery, handler]]);
-
-  const createComponent = (options) => {
-    wrapper = shallowMountExtended(
-      SASTConfigurationApp,
-      merge(
-        {
-          // Use a function reference here so it's lazily initialized, and can
-          // be replaced with other handlers in certain tests without
-          // initialising twice.
-          apolloProvider: () => createMockApolloProvider(successHandler),
-          provide: {
-            sastDocumentationPath,
-            projectPath,
-          },
-        },
-        options,
-      ),
-    );
+  const failureHandler = jest.fn().mockRejectedValue(new Error('Error'));
+  const failureHandlerGraphQL = () => ({ errors: [{ message: specificErrorMessage }] });
+
+  const createMockApolloProvider = (handler) => {
+    return createMockApollo([[sastCiConfigurationQuery, handler]]);
+  };
+
+  const createComponent = ({ options, customMock } = {}) => {
+    wrapper = mountExtended(SASTConfigurationApp, {
+      apolloProvider: customMock || createMockApolloProvider(successHandler),
+      provide: {
+        sastDocumentationPath,
+        projectPath,
+      },
+      options,
+    });
+    return waitForPromises();
   };
 
   const findHeader = () => wrapper.find('header');
@@ -62,7 +51,7 @@ describe('SAST Configuration App', () => {
   describe('feedback alert', () => {
     beforeEach(() => {
       createComponent({
-        stubs: { GlSprintf, ConfigurationPageLayout },
+        options: { stubs: { GlSprintf, ConfigurationPageLayout } },
       });
     });
 
@@ -82,7 +71,7 @@ describe('SAST Configuration App', () => {
   describe('header', () => {
     beforeEach(() => {
       createComponent({
-        stubs: { GlSprintf, ConfigurationPageLayout },
+        options: { stubs: { GlSprintf, ConfigurationPageLayout } },
       });
     });
 
@@ -97,9 +86,7 @@ describe('SAST Configuration App', () => {
 
   describe('when loading', () => {
     beforeEach(() => {
-      createComponent({
-        apolloProvider: createMockApolloProvider(pendingHandler),
-      });
+      createComponent({ customMock: createMockApolloProvider(pendingHandler) });
     });
 
     it('displays a loading spinner', () => {
@@ -115,11 +102,9 @@ describe('SAST Configuration App', () => {
     });
   });
 
-  describe('when loading failed with Error Message including user facing keyword', () => {
+  describe('when loading failed with a GraphQl error', () => {
     beforeEach(() => {
-      createComponent({
-        apolloProvider: createMockApolloProvider(failureHandlerSpecific),
-      });
+      createComponent({ customMock: createMockApolloProvider(failureHandlerGraphQL) });
       return waitForPromises();
     });
 
@@ -135,18 +120,16 @@ describe('SAST Configuration App', () => {
       expect(findErrorAlert().exists()).toBe(true);
     });
 
-    it('shows specific error message without keyword and with link when defined', () => {
+    it('shows a specific error message with link when defined', () => {
       expect(findErrorAlert().exists()).toBe(true);
       expect(findErrorAlert().text()).toContain('some specific error');
       expect(findErrorAlert().html()).toContain('<a href="#" rel="noopener">error</a>');
     });
   });
 
-  describe('when loading failed with Error Message without user facing keyword', () => {
+  describe('when loading failed with a network error', () => {
     beforeEach(() => {
-      createComponent({
-        apolloProvider: createMockApolloProvider(failureHandlerGeneric),
-      });
+      createComponent({ customMock: createMockApolloProvider(failureHandler) });
       return waitForPromises();
     });
 
@@ -158,7 +141,7 @@ describe('SAST Configuration App', () => {
 
   describe('when loaded', () => {
     beforeEach(() => {
-      createComponent();
+      createComponent({ customMock: createMockApolloProvider(successHandler) });
     });
 
     it('does not display a loading spinner', () => {
diff --git a/ee/spec/frontend/security_configuration/sast/constants.js b/ee/spec/frontend/security_configuration/sast/constants.js
index f6432e2cc78d..9f1abc192c59 100644
--- a/ee/spec/frontend/security_configuration/sast/constants.js
+++ b/ee/spec/frontend/security_configuration/sast/constants.js
@@ -1,2 +1 @@
 export const specificErrorMessage = 'some specific <a rel="noopener" href="#">error</a>';
-export const technicalErrorMessage = 'some non-userfacing technical error';
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 262164a08215..bccd4501830e 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -291,7 +291,7 @@
       let_it_be(:project) { create(:project_empty_repo) }
 
       it 'raises an error' do
-        expect(subject['errors'][0]['message']).to eq('UF You must <a target="_blank" rel="noopener noreferrer" ' \
+        expect(subject['errors'][0]['message']).to eq('You must <a target="_blank" rel="noopener noreferrer" ' \
                                                       'href="http://localhost/help/user/project/repository/index.md#' \
                                                       'add-files-to-a-repository">add at least one file to the ' \
                                                       'repository</a> before using Security features.')
diff --git a/spec/services/security/ci_configuration/sast_create_service_spec.rb b/spec/services/security/ci_configuration/sast_create_service_spec.rb
index e80fe1a42fa8..555902fd77c6 100644
--- a/spec/services/security/ci_configuration/sast_create_service_spec.rb
+++ b/spec/services/security/ci_configuration/sast_create_service_spec.rb
@@ -45,8 +45,13 @@
 
       let(:params) { { initialize_with_sast: false } }
 
-      it 'raises an error' do
-        expect { result }.to raise_error(Gitlab::Graphql::Errors::MutationError)
+      it 'returns a ServiceResponse error' do
+        expect(result).to be_kind_of(ServiceResponse)
+        expect(result.status).to eq(:error)
+        expect(result.message).to eq('You must <a target="_blank" rel="noopener noreferrer" ' \
+                                     'href="http://localhost/help/user/project/repository/index.md#' \
+                                     'add-files-to-a-repository">add at least one file to the ' \
+                                     'repository</a> before using Security features.')
       end
     end
 
diff --git a/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb b/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
index 21dc3c2bf701..9a7ecd8827b2 100644
--- a/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
@@ -114,8 +114,8 @@
         it 'fails with error' do
           expect(project).to receive(:ci_config_for).and_return(unsupported_yaml)
 
-          expect { result }.to raise_error(Gitlab::Graphql::Errors::MutationError, Gitlab::Utils::ErrorMessage.to_user_facing(
-            _(".gitlab-ci.yml with aliases/anchors is not supported. Please change the CI configuration manually.")))
+          expect { result }.to raise_error(Gitlab::Graphql::Errors::MutationError,
+            _(".gitlab-ci.yml with aliases/anchors is not supported. Please change the CI configuration manually."))
         end
       end
 
@@ -166,14 +166,13 @@
         let(:params) { nil }
         let_it_be(:project) { create(:project_empty_repo) }
 
-        it 'returns an error' do
-          expect { result }.to raise_error { |error|
-            expect(error).to be_a(Gitlab::Graphql::Errors::MutationError)
-            expect(error.message).to eq('UF You must <a target="_blank" rel="noopener noreferrer" ' \
-                                        'href="http://localhost/help/user/project/repository/index.md' \
-                                        '#add-files-to-a-repository">add at least one file to the repository' \
-                                        '</a> before using Security features.')
-          }
+        it 'returns a ServiceResponse error' do
+          expect(result).to be_kind_of(ServiceResponse)
+          expect(result.status).to eq(:error)
+          expect(result.message).to eq('You must <a target="_blank" rel="noopener noreferrer" ' \
+                                       'href="http://localhost/help/user/project/repository/index.md' \
+                                       '#add-files-to-a-repository">add at least one file to the repository' \
+                                       '</a> before using Security features.')
         end
       end
     end
-- 
GitLab