diff options
54 files changed, 443 insertions, 73 deletions
diff --git a/app/assets/javascripts/deprecated_notes.js b/app/assets/javascripts/deprecated_notes.js index 32a91506ab4..0008c3504ce 100644 --- a/app/assets/javascripts/deprecated_notes.js +++ b/app/assets/javascripts/deprecated_notes.js @@ -1408,7 +1408,7 @@ export default class Notes { * Load notes ids */ static loadNotesIds(note_ids) { - const $notesList = $('.main-notes-list').children(); + const $notesList = $('.main-notes-list li[id^=note_]'); for (const $noteItem of $notesList) { if (Notes.isNodeTypeElement($noteItem)) { const noteId = parseInt($noteItem.id.split('_')[1], 10); diff --git a/app/assets/javascripts/lib/utils/error_message.js b/app/assets/javascripts/lib/utils/error_message.js new file mode 100644 index 00000000000..4cea4257e7b --- /dev/null +++ b/app/assets/javascripts/lib/utils/error_message.js @@ -0,0 +1,20 @@ +export const USER_FACING_ERROR_MESSAGE_PREFIX = 'UF:'; + +const getMessageFromError = (error = '') => { + return error.message || error; +}; + +export const parseErrorMessage = (error = '') => { + const messageString = getMessageFromError(error); + + if (messageString.startsWith(USER_FACING_ERROR_MESSAGE_PREFIX)) { + return { + message: messageString.replace(USER_FACING_ERROR_MESSAGE_PREFIX, '').trim(), + userFacing: true, + }; + } + return { + message: messageString, + userFacing: false, + }; +}; diff --git a/app/assets/javascripts/persistent_user_callouts.js b/app/assets/javascripts/persistent_user_callouts.js index a16e93d0541..3130fe42c3c 100644 --- a/app/assets/javascripts/persistent_user_callouts.js +++ b/app/assets/javascripts/persistent_user_callouts.js @@ -23,6 +23,7 @@ const PERSISTENT_USER_CALLOUTS = [ '.js-geo-enable-hashed-storage-callout', '.js-geo-migrate-hashed-storage-callout', '.js-unlimited-members-during-trial-alert', + '.js-branch-rules-info-callout', ]; const initCallouts = () => { diff --git a/app/assets/javascripts/security_configuration/components/app.vue b/app/assets/javascripts/security_configuration/components/app.vue index e96f71981e5..ccfaa678201 100644 --- a/app/assets/javascripts/security_configuration/components/app.vue +++ b/app/assets/javascripts/security_configuration/components/app.vue @@ -1,6 +1,7 @@ <script> import { GlTab, GlTabs, GlSprintf, GlLink, GlAlert } from '@gitlab/ui'; import { __, s__ } from '~/locale'; +import { parseErrorMessage } from '~/lib/utils/error_message'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue'; import SectionLayout from '~/vue_shared/security_configuration/components/section_layout.vue'; @@ -33,6 +34,9 @@ export const i18n = { 'SecurityConfiguration|Enable security training to help your developers learn how to fix vulnerabilities. Developers can view security training from selected educational providers, relevant to the detected vulnerability.', ), securityTrainingDoc: s__('SecurityConfiguration|Learn more about vulnerability training'), + genericErrorText: s__( + `SecurityConfiguration|Something went wrong. Please refresh the page, or try again later.`, + ), }; export default { @@ -124,8 +128,9 @@ export default { dismissedProjects.add(this.projectFullPath); this.autoDevopsEnabledAlertDismissedProjects = Array.from(dismissedProjects); }, - onError(message) { - this.errorMessage = message; + onError(error) { + const { message, userFacing } = parseErrorMessage(error); + this.errorMessage = userFacing ? message : i18n.genericErrorText; }, dismissAlert() { this.errorMessage = ''; diff --git a/app/controllers/admin/cohorts_controller.rb b/app/controllers/admin/cohorts_controller.rb index ce3d769f35e..3948d3635fe 100644 --- a/app/controllers/admin/cohorts_controller.rb +++ b/app/controllers/admin/cohorts_controller.rb @@ -7,7 +7,7 @@ class Admin::CohortsController < Admin::ApplicationController urgency :low - track_custom_event :index, + track_event :index, name: 'i_analytics_cohorts', action: 'perform_analytics_usage_action', label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', diff --git a/app/controllers/admin/dev_ops_report_controller.rb b/app/controllers/admin/dev_ops_report_controller.rb index 71ee19ddf39..2e47dfcb0db 100644 --- a/app/controllers/admin/dev_ops_report_controller.rb +++ b/app/controllers/admin/dev_ops_report_controller.rb @@ -5,7 +5,7 @@ class Admin::DevOpsReportController < Admin::ApplicationController helper_method :show_adoption? - track_custom_event :show, + track_event :show, name: 'i_analytics_dev_ops_score', action: 'perform_analytics_usage_action', label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', diff --git a/app/controllers/admin/usage_trends_controller.rb b/app/controllers/admin/usage_trends_controller.rb index 082b38ac3a8..f88028535c1 100644 --- a/app/controllers/admin/usage_trends_controller.rb +++ b/app/controllers/admin/usage_trends_controller.rb @@ -3,7 +3,7 @@ class Admin::UsageTrendsController < Admin::ApplicationController include ProductAnalyticsTracking - track_custom_event :index, + track_event :index, name: 'i_analytics_instance_statistics', action: 'perform_analytics_usage_action', label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', diff --git a/app/controllers/concerns/product_analytics_tracking.rb b/app/controllers/concerns/product_analytics_tracking.rb index 6a286a1ca26..5ed2b2a82eb 100644 --- a/app/controllers/concerns/product_analytics_tracking.rb +++ b/app/controllers/concerns/product_analytics_tracking.rb @@ -30,45 +30,23 @@ module ProductAnalyticsTracking ].freeze class_methods do - # TODO: Remove once all the events are migrated to #track_custom_event - # during https://gitlab.com/groups/gitlab-org/-/epics/8641 - def track_event(*controller_actions, name:, conditions: nil, destinations: [:redis_hll], &block) + def track_event(*controller_actions, name:, action: nil, label: nil, conditions: nil, destinations: [:redis_hll], &block) custom_conditions = [:trackable_html_request?, *conditions] after_action only: controller_actions, if: custom_conditions do - route_events_to(destinations, name, &block) - end - end - - def track_custom_event(*controller_actions, name:, action:, label:, conditions: nil, destinations: [:redis_hll], &block) - custom_conditions = [:trackable_html_request?, *conditions] - - after_action only: controller_actions, if: custom_conditions do - route_custom_events_to(destinations, name, action, label, &block) + route_events_to(destinations, name, action, label, &block) end end end private - def route_events_to(destinations, name, &block) - track_unique_redis_hll_event(name, &block) if destinations.include?(:redis_hll) - - return unless destinations.include?(:snowplow) && event_enabled?(name) - - Gitlab::Tracking.event( - self.class.to_s, - name, - namespace: tracking_namespace_source, - user: current_user, - context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: name).to_context] - ) - end - - def route_custom_events_to(destinations, name, action, label, &block) + def route_events_to(destinations, name, action, label, &block) track_unique_redis_hll_event(name, &block) if destinations.include?(:redis_hll) return unless destinations.include?(:snowplow) && event_enabled?(name) + raise "action is required when destination is snowplow" unless action + raise "label is required when destination is snowplow" unless label optional_arguments = { namespace: tracking_namespace_source, diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 79c27904542..3413aeb6f8a 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -37,7 +37,7 @@ class Projects::BlobController < Projects::ApplicationController before_action :validate_diff_params, only: :diff before_action :set_last_commit_sha, only: [:edit, :update] - track_custom_event :create, :update, + track_event :create, :update, name: 'g_edit_by_sfe', action: 'perform_sfe_action', label: 'usage_activity_by_stage_monthly.create.action_monthly_active_users_sfe_edit', diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index 6e19c0e45bf..dbed5adf2e8 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -11,7 +11,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController before_action :authorize_read_cycle_analytics! before_action :load_value_stream, only: :show - track_custom_event :show, + track_event :show, name: 'p_analytics_valuestream', action: 'perform_analytics_usage_action', label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index d072381933a..e73e2a38149 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -9,7 +9,7 @@ class Projects::GraphsController < Projects::ApplicationController before_action :assign_ref_vars before_action :authorize_read_repository_graphs! - track_custom_event :charts, + track_event :charts, name: 'p_analytics_repo', action: 'perform_analytics_usage_action', label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index a80aa9f4ac7..6fdd4906613 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -29,7 +29,7 @@ class Projects::PipelinesController < Projects::ApplicationController around_action :allow_gitaly_ref_name_caching, only: [:index, :show] - track_custom_event :charts, + track_event :charts, name: 'p_analytics_pipelines', action: 'perform_analytics_usage_action', label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 549b6286d96..688c56e56e0 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -10,7 +10,7 @@ class SearchController < ApplicationController RESCUE_FROM_TIMEOUT_ACTIONS = [:count, :show, :autocomplete, :aggregations].freeze CODE_SEARCH_LITERALS = %w[blob: extension: path: filename:].freeze - track_custom_event :show, + track_event :show, name: 'i_search_total', label: 'redis_hll_counters.search.search_total_unique_counts_monthly', action: 'executed', diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 4593f5e5925..4ca2bc8b1b5 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -665,7 +665,7 @@ module Types if project.repository.empty? raise Gitlab::Graphql::Errors::MutationError, - _(format('You must %s before using Security features.', add_file_docs_link.html_safe)).html_safe + Gitlab::Utils::ErrorMessage.to_user_facing(_(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/helpers/users/callouts_helper.rb b/app/helpers/users/callouts_helper.rb index 0ad1cbdff3e..af3ac495164 100644 --- a/app/helpers/users/callouts_helper.rb +++ b/app/helpers/users/callouts_helper.rb @@ -15,6 +15,7 @@ module Users REGISTRATION_ENABLED_CALLOUT_ALLOWED_CONTROLLER_PATHS = [/^root/, /^dashboard\S*/, /^admin\S*/].freeze WEB_HOOK_DISABLED = 'web_hook_disabled' ULTIMATE_FEATURE_REMOVAL_BANNER = 'ultimate_feature_removal_banner' + BRANCH_RULES_INFO_CALLOUT = 'branch_rules_info_callout' def show_gke_cluster_integration_callout?(project) active_nav_link?(controller: sidebar_operations_paths) && @@ -74,6 +75,10 @@ module Users !user_dismissed?(PAGES_MOVED_CALLOUT) end + def show_branch_rules_info? + !user_dismissed?(BRANCH_RULES_INFO_CALLOUT) + end + def ultimate_feature_removal_banner_dismissed?(project) return false unless project diff --git a/app/models/users/callout.rb b/app/models/users/callout.rb index 3f9353214ee..70c31f0a8ec 100644 --- a/app/models/users/callout.rb +++ b/app/models/users/callout.rb @@ -65,7 +65,8 @@ module Users new_top_level_group_alert: 61, artifacts_management_page_feedback_banner: 62, vscode_web_ide: 63, - vscode_web_ide_callout: 64 + vscode_web_ide_callout: 64, + branch_rules_info_callout: 65 } validates :feature_name, diff --git a/app/services/security/ci_configuration/base_create_service.rb b/app/services/security/ci_configuration/base_create_service.rb index c5fbabf487c..0534925aaec 100644 --- a/app/services/security/ci_configuration/base_create_service.rb +++ b/app/services/security/ci_configuration/base_create_service.rb @@ -19,7 +19,8 @@ module Security target: '_blank', rel: 'noopener noreferrer' raise Gitlab::Graphql::Errors::MutationError, - _(format('You must %s before using Security features.', docs_link.html_safe)).html_safe + Gitlab::Utils::ErrorMessage.to_user_facing( + _(format('You must %s before using Security features.', docs_link.html_safe)).html_safe) end project.repository.add_branch(current_user, branch_name, project.default_branch) diff --git a/app/views/admin/application_settings/appearances/show.html.haml b/app/views/admin/application_settings/appearances/show.html.haml index 1e55190d53b..cd255d961f4 100644 --- a/app/views/admin/application_settings/appearances/show.html.haml +++ b/app/views/admin/application_settings/appearances/show.html.haml @@ -1,5 +1,4 @@ - page_title _("Appearance") -- @content_class = "limit-container-width" unless fluid_layout - add_page_specific_style 'page_bundles/settings' = render 'form' diff --git a/app/views/admin/application_settings/ci_cd.html.haml b/app/views/admin/application_settings/ci_cd.html.haml index bd0ce766f81..a7119af7ece 100644 --- a/app/views/admin/application_settings/ci_cd.html.haml +++ b/app/views/admin/application_settings/ci_cd.html.haml @@ -1,7 +1,6 @@ - breadcrumb_title _("CI/CD") - page_title _("CI/CD") - add_page_specific_style 'page_bundles/settings' -- @content_class = "limit-container-width" unless fluid_layout %section.settings.no-animate#js-ci-cd-variables{ class: ('expanded' if expanded_by_default?) } .settings-header diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml index a4af1913d22..a7c80abdbc9 100644 --- a/app/views/admin/application_settings/general.html.haml +++ b/app/views/admin/application_settings/general.html.haml @@ -1,7 +1,6 @@ - breadcrumb_title _("General") - page_title _("General") - add_page_specific_style 'page_bundles/settings' -- @content_class = "limit-container-width" unless fluid_layout %section.settings.as-visibility-access.no-animate#js-visibility-settings{ class: ('expanded' if expanded_by_default?), data: { testid: 'admin-visibility-access-settings' } } .settings-header diff --git a/app/views/admin/application_settings/integrations.html.haml b/app/views/admin/application_settings/integrations.html.haml index fd1ad5cd304..396e6f3e7d6 100644 --- a/app/views/admin/application_settings/integrations.html.haml +++ b/app/views/admin/application_settings/integrations.html.haml @@ -1,7 +1,6 @@ - breadcrumb_title s_('Integrations|Instance-level integration management') - page_title s_('Integrations|Instance-level integration management') - add_page_specific_style 'page_bundles/settings' -- @content_class = 'limit-container-width' unless fluid_layout %h3= s_('Integrations|Instance-level integration management') diff --git a/app/views/admin/application_settings/metrics_and_profiling.html.haml b/app/views/admin/application_settings/metrics_and_profiling.html.haml index b5981578866..711b2c97d65 100644 --- a/app/views/admin/application_settings/metrics_and_profiling.html.haml +++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml @@ -3,7 +3,6 @@ - breadcrumb_title _("Metrics and profiling") - page_title _("Metrics and profiling") - add_page_specific_style 'page_bundles/settings' -- @content_class = "limit-container-width" unless fluid_layout %section.settings.as-prometheus.no-animate#js-prometheus-settings{ class: ('expanded' if expanded_by_default?) } .settings-header diff --git a/app/views/admin/application_settings/network.html.haml b/app/views/admin/application_settings/network.html.haml index b20fc703f18..32b10fd36e8 100644 --- a/app/views/admin/application_settings/network.html.haml +++ b/app/views/admin/application_settings/network.html.haml @@ -1,7 +1,6 @@ - breadcrumb_title _("Network") - page_title _("Network") - add_page_specific_style 'page_bundles/settings' -- @content_class = "limit-container-width" unless fluid_layout %section.settings.as-performance.no-animate#js-performance-settings{ class: ('expanded' if expanded_by_default?) } .settings-header diff --git a/app/views/admin/application_settings/preferences.html.haml b/app/views/admin/application_settings/preferences.html.haml index dd6666542ca..3843fc8e863 100644 --- a/app/views/admin/application_settings/preferences.html.haml +++ b/app/views/admin/application_settings/preferences.html.haml @@ -1,7 +1,6 @@ - breadcrumb_title _("Preferences") - page_title _("Preferences") - add_page_specific_style 'page_bundles/settings' -- @content_class = "limit-container-width" unless fluid_layout %section.settings.as-email.no-animate#js-email-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'email_content' } } .settings-header diff --git a/app/views/admin/application_settings/reporting.html.haml b/app/views/admin/application_settings/reporting.html.haml index 3d803e95cd0..0046275c7d1 100644 --- a/app/views/admin/application_settings/reporting.html.haml +++ b/app/views/admin/application_settings/reporting.html.haml @@ -1,7 +1,6 @@ - breadcrumb_title _("Reporting") - page_title _("Reporting") - add_page_specific_style 'page_bundles/settings' -- @content_class = "limit-container-width" unless fluid_layout %section.settings.as-spam.no-animate#js-spam-settings{ class: ('expanded' if expanded_by_default?) } .settings-header diff --git a/app/views/admin/application_settings/repository.html.haml b/app/views/admin/application_settings/repository.html.haml index 50798ad476c..518b40a0326 100644 --- a/app/views/admin/application_settings/repository.html.haml +++ b/app/views/admin/application_settings/repository.html.haml @@ -1,7 +1,6 @@ - breadcrumb_title _("Repository") - page_title _("Repository") - add_page_specific_style 'page_bundles/settings' -- @content_class = "limit-container-width" unless fluid_layout %section.settings.as-default-branch-name.no-animate#js-default-branch-name{ class: ('expanded' if expanded_by_default?) } .settings-header diff --git a/app/views/admin/application_settings/service_usage_data.html.haml b/app/views/admin/application_settings/service_usage_data.html.haml index d6860cc08ac..af646d79c29 100644 --- a/app/views/admin/application_settings/service_usage_data.html.haml +++ b/app/views/admin/application_settings/service_usage_data.html.haml @@ -3,7 +3,6 @@ - breadcrumb_title name - page_title name - add_page_specific_style 'page_bundles/settings' -- @content_class = "limit-container-width" unless fluid_layout - payload_class = 'js-service-ping-payload' %section.js-search-settings-section diff --git a/app/views/admin/health_check/show.html.haml b/app/views/admin/health_check/show.html.haml index 98427cb6419..e7aa4f38634 100644 --- a/app/views/admin/health_check/show.html.haml +++ b/app/views/admin/health_check/show.html.haml @@ -8,9 +8,8 @@ #{ s_('HealthCheck|Access token is') } %code#health-check-token= Gitlab::CurrentSettings.health_check_access_token .gl-mt-3 - = button_to _("Reset health check access token"), reset_health_check_token_admin_application_settings_path, - method: :put, class: 'gl-button btn btn-default', - data: { confirm: _('Are you sure you want to reset the health check token?') } + = render Pajamas::ButtonComponent.new(href: reset_health_check_token_admin_application_settings_path, method: :put, button_options: { data: { confirm: _('Are you sure you want to reset the health check token?') } }) do + = _("Reset health check access token") %p.light #{ _('Health information can be retrieved from the following endpoints. More information is available') } = link_to s_('More information is available|here'), help_page_path('user/admin_area/monitoring/health_check') diff --git a/app/views/projects/branches/_branch_rules_info.haml b/app/views/projects/branches/_branch_rules_info.haml new file mode 100644 index 00000000000..15bee31c596 --- /dev/null +++ b/app/views/projects/branches/_branch_rules_info.haml @@ -0,0 +1,12 @@ +- return unless show_branch_rules_info? += render Pajamas::AlertComponent.new(variant: :info, + title: s_("Branches|See all branch-related settings together with branch rules"), + alert_options: { class: 'js-branch-rules-info-callout gl-mb-6 gl-mt-4', data: { feature_id: Users::CalloutsHelper::BRANCH_RULES_INFO_CALLOUT, dismiss_endpoint: callouts_path, defer_links: 'true' } }) do |c| + = c.body do + = s_("Branches|You can now find an overview of settings for protected branches, merge request approvals, status checks, and security approvals conveniently in one spot.") + + = c.actions do + = render Pajamas::ButtonComponent.new(variant: :confirm, href: project_settings_repository_path(@project, anchor: 'js-branch-rules'), button_options: { class: 'deferred-link gl-alert-action' }) do + = s_("Branches|View branch rules") + = render Pajamas::ButtonComponent.new(button_options: { class: 'js-close'}) do + = _('Dismiss') diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index f43d19e2542..518292effd8 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -1,6 +1,8 @@ - add_page_specific_style 'page_bundles/branches' - page_title _('Branches') - add_to_breadcrumbs(_('Repository'), project_tree_path(@project)) +- is_branch_rules_available = (can? current_user, :maintainer_access, @project) && Feature.enabled?(:branch_rules, @project) +- can_push_code = (can? current_user, :push_code, @project) -# Possible values for variables passed down from the projects/branches_controller.rb -# @@ -22,16 +24,24 @@ sorted_by: @sort } } - - if can? current_user, :push_code, @project + - if can_push_code .js-delete-merged-branches{ data: { default_branch: @project.repository.root_ref, form_path: project_merged_branches_path(@project) } } + - if is_branch_rules_available + = link_to project_settings_repository_path(@project, anchor: 'js-branch-rules'), class: 'gl-button btn btn-default' do + = s_('Branches|View branch rules') + + - if can_push_code = link_to new_project_branch_path(@project), class: 'gl-button btn btn-confirm' do = s_('Branches|New branch') = render_if_exists 'projects/commits/mirror_status' +- if is_branch_rules_available + = render 'branch_rules_info' + .js-branch-list{ data: { diverging_counts_endpoint: diverging_commit_counts_namespace_project_branches_path(@project.namespace, @project, format: :json), default_branch: @project.default_branch } } - if @gitaly_unavailable diff --git a/data/removals/15_10/15_10-non-public-artifacts.yml b/data/removals/15_10/15_10-non-public-artifacts.yml new file mode 100644 index 00000000000..bea672d2f22 --- /dev/null +++ b/data/removals/15_10/15_10-non-public-artifacts.yml @@ -0,0 +1,25 @@ +# +# REQUIRED FIELDS +# +- title: "`artifacts:public` CI/CD keyword refactored" # (required) Clearly explain the change. For example, "The `confidential` field for a `Note` is removed" or "CI/CD job names are limited to 250 characters." + announcement_milestone: "15.10" # (required) The milestone when this feature was deprecated. + announcement_date: "2023-03-22" # (required) The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post. + removal_milestone: "15.8" # (required) The milestone when this feature is being removed. + removal_date: "2023-01-22" # (required) This should almost always be the 22nd of a month (YYYY-MM-DD), the date of the milestone release when this feature will be removed. + breaking_change: true # (required) Change to false if this is not a breaking change. + reporter: jocelynjane # (required) GitLab username of the person reporting the removal + stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/322454 # (required) Link to the deprecation issue in GitLab + body: | # (required) Do not modify this line, instead modify the lines below. + The [`artifacts:public` CI/CD keyword](https://docs.gitlab.com/ee/ci/yaml/#artifactspublic) was discovered to be not working properly since GitLab 15.8 and needed to be refactored. This feature is disabled on GitLab.com, and disabled by default on self-managed instances. If an administrator for an instance running GitLab 15.8 or 15.9 enabled this feature via the `non_public_artifacts` feature flag, it is likely that artifacts created with the `public:false` setting are being treated as `public:true`. + + If you have projects that use this setting, you should delete artifacts that must not be public, or [change the visibility](https://docs.gitlab.com/ee/user/public_access.html#change-project-visibility) of affected projects to private, before updating to GitLab 15.8 or later. + + In GitLab 15.10, this feature's code was refactored. On instances with this feature enabled, new artifacts created with `public:false` are now working as expected, though still disabled by default. Avoid testing this feature with production data until it is enabled by default and made generally available. +# +# OPTIONAL FIELDS +# + tiers: Free # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate] + documentation_url: https://docs.gitlab.com/ee/ci/yaml/#artifactspublic # (optional) This is a link to the current documentation page + image_url: # (optional) This is a link to a thumbnail image depicting the feature + video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index cb9f225f03f..d5b73291666 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -24376,6 +24376,7 @@ Name of the feature that the callout is for. | ----- | ----------- | | <a id="usercalloutfeaturenameenumactive_user_count_threshold"></a>`ACTIVE_USER_COUNT_THRESHOLD` | Callout feature name for active_user_count_threshold. | | <a id="usercalloutfeaturenameenumartifacts_management_page_feedback_banner"></a>`ARTIFACTS_MANAGEMENT_PAGE_FEEDBACK_BANNER` | Callout feature name for artifacts_management_page_feedback_banner. | +| <a id="usercalloutfeaturenameenumbranch_rules_info_callout"></a>`BRANCH_RULES_INFO_CALLOUT` | Callout feature name for branch_rules_info_callout. | | <a id="usercalloutfeaturenameenumbuy_pipeline_minutes_notification_dot"></a>`BUY_PIPELINE_MINUTES_NOTIFICATION_DOT` | Callout feature name for buy_pipeline_minutes_notification_dot. | | <a id="usercalloutfeaturenameenumcanary_deployment"></a>`CANARY_DEPLOYMENT` | Callout feature name for canary_deployment. | | <a id="usercalloutfeaturenameenumci_deprecation_warning_for_types_keyword"></a>`CI_DEPRECATION_WARNING_FOR_TYPES_KEYWORD` | Callout feature name for ci_deprecation_warning_for_types_keyword. | diff --git a/doc/api/packages.md b/doc/api/packages.md index 32b21ce354d..86eaf3028cf 100644 --- a/doc/api/packages.md +++ b/doc/api/packages.md @@ -352,6 +352,9 @@ Can return the following status codes: - `204 No Content`, if the package was deleted successfully. - `404 Not Found`, if the package was not found. +If [request forwarding](../user/packages/package_registry/supported_functionality.md#forwarding-requests) is enabled, +deleting a package can introduce a [dependency confusion risk](../user/packages/package_registry/supported_functionality.md#deleting-packages). + ## Delete a package file > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32107) in GitLab 13.12. diff --git a/doc/ci/testing/unit_test_report_examples.md b/doc/ci/testing/unit_test_report_examples.md index 41dfc8fc60b..c63e225a2a7 100644 --- a/doc/ci/testing/unit_test_report_examples.md +++ b/doc/ci/testing/unit_test_report_examples.md @@ -274,3 +274,24 @@ phpunit: reports: junit: report.xml ``` + +## Rust + +This example uses [cargo2junit](https://crates.io/crates/cargo2junit), +which is installed in the current directory. +To retrieve JSON output from `cargo test`, you must enable the nightly compiler. + +```yaml +run unittests: + image: rust:latest + stage: test + before_script: + - cargo install --root . cargo2junit + script: + - cargo test -- -Z unstable-options --format json --report-time | bin/cargo2junit > report.xml + artifacts: + when: always + reports: + junit: + - report.xml +``` diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md index 006d0a01abb..828ef21ba9a 100644 --- a/doc/development/api_styleguide.md +++ b/doc/development/api_styleguide.md @@ -333,6 +333,17 @@ expect(response).to match_response_schema('merge_requests') Also see [verifying N+1 performance](#verifying-with-tests) in tests. +## Error handling + +When throwing an error with a message that is meant to be user-facing, you should +use the error message utility function contained in `lib/gitlab/utils/error_message.rb`. +It adds a prefix to the error message, making it distinguishable from non-user-facing error messages. +Please make sure that the Frontend is aware of the prefix usage and is using the according utils. + +```ruby +Gitlab::Utils::ErrorMessage.to_user_facing('Example user-facing error-message') +``` + ## Include a changelog entry All client-facing changes **must** include a [changelog entry](changelog.md). diff --git a/doc/development/fe_guide/style/javascript.md b/doc/development/fe_guide/style/javascript.md index 3e3a79dd7bb..b35ffdd8669 100644 --- a/doc/development/fe_guide/style/javascript.md +++ b/doc/development/fe_guide/style/javascript.md @@ -329,3 +329,22 @@ Only export the constants as a collection (array, or object) when there is a nee // good, if the constants need to be iterated over export const VARIANTS = [VARIANT_WARNING, VARIANT_ERROR]; ``` + +## Error handling + +When catching a server-side error you should use the error message +utility function contained in `app/assets/javascripts/lib/utils/error_message.js`. +This utility parses the received error message and checks for a prefix that indicates +whether the message is meant to be user-facing or not. The utility returns +an object with the message, and a boolean indicating whether the message is meant to be user-facing or not. Please make sure that the Backend is aware of the utils usage and is adding the prefix +to the error message accordingly. + +```javascript +import { parseErrorMessage } from '~/lib/utils/error_message'; + +onError(error) { + const { message, userFacing } = parseErrorMessage(error); + + const errorMessage = userFacing ? message : genericErrorText; +} +``` diff --git a/doc/development/service_ping/implement.md b/doc/development/service_ping/implement.md index 40fbeeaa7f0..5bfb81c1d00 100644 --- a/doc/development/service_ping/implement.md +++ b/doc/development/service_ping/implement.md @@ -351,16 +351,16 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd/) and [P - In the controller using the `ProductAnalyticsTracking` module and the following format: ```ruby - track_custom_event(*controller_actions, name:, action:, label:, conditions: nil, destinations: [:redis_hll], &block) + track_event(*controller_actions, name:, action:, label:, conditions: nil, destinations: [:redis_hll], &block) ``` Arguments: - `controller_actions`: the controller actions to track. - `name`: the event name. + - `action`: required if destination is `:snowplow. Action name for the triggered event. See [event schema](../snowplow/index.md#event-schema) for more details. + - `label`: required if destination is `:snowplow. Label for the triggered event. See [event schema](../snowplow/index.md#event-schema) for more details. - `conditions`: optional custom conditions. Uses the same format as Rails callbacks. - - `action`: optional action name for the triggered event. See [event schema](../snowplow/index.md#event-schema) for more details. - - `label`: optional label for the triggered event. See [event schema](../snowplow/index.md#event-schema) for more details. - `destinations`: optional list of destinations. Currently supports `:redis_hll` and `:snowplow`. Default: `:redis_hll`. - `&block`: optional block that computes and returns the `custom_id` that we want to track. This overrides the `visitor_id`. @@ -372,7 +372,7 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd/) and [P include ProductAnalyticsTracking skip_before_action :authenticate_user!, only: :show - track_custom_event :index, :show, + track_event :index, :show, name: 'users_visiting_projects', action: 'user_perform_visit', label: 'redis_hll_counters.users_visiting_project_monthly', diff --git a/doc/integration/partner_marketplace.md b/doc/integration/partner_marketplace.md index 2cc306cdf44..5ed131145f4 100644 --- a/doc/integration/partner_marketplace.md +++ b/doc/integration/partner_marketplace.md @@ -118,3 +118,43 @@ curl \ --url "https://customers.staging.gitlab.com/api/v1/marketplace/subscriptions/:external_subscription_id" \ --header "Authorization: Bearer NHb_VhZhPOnBTSNfBSzmCmt28lLDWb2xtwr_c3DL148" ``` + +## Create a new customer subscription + +To create a new customer subscription from a GitLab Partner client application, + +- Make an authorized POST request to the +[`/api/v1/marketplace/subscriptions`](https://customers.staging.gitlab.com/openapi_docs/marketplace#/marketplace/post_api_v1_marketplace_subscriptions) +endpoint in the Customers Portal with the following parameters in JSON format: + +| Parameter | Type | Required | Description | +|--------------------------|--------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------| +| `externalSubscriptionId` | string | yes | ID of the subscription on the GitLab Partner system. | +| `tradingPartnerId` | string | yes | ID of the GitLab Partner account on the Customers Portal. | +| `customer` | object | yes | Information about the customer. Must include company name. Contact must include `firstName`, `lastName` and `email`. Address must include `country`. | +| `orderLines` | array | yes | Specifies the product purchased. Must include `quantity` and `productId`. | + +If the request is successful, the response body includes the newly created subscription number. For an example of a full request body, +see the [Marketplace interactive API documentation](https://customers.staging.gitlab.com/openapi_docs/marketplace). + +If the subscription creation is unsuccessful, the response body includes an error message with details about the cause of the error. + +## Check the status of a subscription + +To get the status of a given subscription, + +- Make an authorized GET request to the +[`/api/v1/marketplace/subscriptions/{external_subscription_id}`](https://customers.staging.gitlab.com/openapi_docs/marketplace#/marketplace/get_api_v1_marketplace_subscriptions__external_subscription_id_) +endpoint in the Customers Portal. + +The request must include the GitLab partner system ID of the subscription to fetch the status for. + +If the request is successful, the response body contains the status of the subscription provision. The status can be: + +- Creating +- Created +- Failed +- Provisioned + +If the subscription cannot be found using the passed `external_subscription_id`, the response has +a 404 Not Found status. diff --git a/doc/update/removals.md b/doc/update/removals.md index 110a3ee29e8..248d03cbbcb 100644 --- a/doc/update/removals.md +++ b/doc/update/removals.md @@ -55,6 +55,18 @@ If you want to preserve this functionality, you can follow one of these two path 1. Fork the [GitLab Auto Deploy Helm chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app) into the `chart/` path within your project 1. Set `AUTO_DEPLOY_IMAGE_VERSION` and `DAST_AUTO_DEPLOY_IMAGE_VERSION` to the most recent version of the image that included the CiliumNetworkPolicy +### `artifacts:public` CI/CD keyword refactored + +WARNING: +This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). +Review the details carefully before upgrading. + +The [`artifacts:public` CI/CD keyword](https://docs.gitlab.com/ee/ci/yaml/#artifactspublic) was discovered to be not working properly since GitLab 15.8 and needed to be refactored. This feature is disabled on GitLab.com, and disabled by default on self-managed instances. If an administrator for an instance running GitLab 15.8 or 15.9 enabled this feature via the `non_public_artifacts` feature flag, it is likely that artifacts created with the `public:false` setting are being treated as `public:true`. + +If you have projects that use this setting, you should delete artifacts that must not be public, or [change the visibility](https://docs.gitlab.com/ee/user/public_access.html#change-project-visibility) of affected projects to private, before updating to GitLab 15.8 or later. + +In GitLab 15.10, this feature's code was refactored. On instances with this feature enabled, new artifacts created with `public:false` are now working as expected, though still disabled by default. Avoid testing this feature with production data until it is enabled by default and made generally available. + ## Removed in 15.7 ### File Type variable expansion in `.gitlab-ci.yml` diff --git a/doc/user/packages/package_registry/reduce_package_registry_storage.md b/doc/user/packages/package_registry/reduce_package_registry_storage.md index 673196ebad5..020cad5ac14 100644 --- a/doc/user/packages/package_registry/reduce_package_registry_storage.md +++ b/doc/user/packages/package_registry/reduce_package_registry_storage.md @@ -35,6 +35,9 @@ To delete a package in the UI, from your group or project: The package is permanently deleted. +If [request forwarding](supported_functionality.md#forwarding-requests) is enabled, +deleting a package can introduce a [dependency confusion risk](supported_functionality.md#deleting-packages). + ## Delete assets associated with a package To delete package assets, you must have suitable [permissions](../../permissions.md). diff --git a/doc/user/packages/package_registry/supported_functionality.md b/doc/user/packages/package_registry/supported_functionality.md index e56ae88872a..0c7813beae0 100644 --- a/doc/user/packages/package_registry/supported_functionality.md +++ b/doc/user/packages/package_registry/supported_functionality.md @@ -53,10 +53,10 @@ Requests for packages not found in your GitLab project are forwarded to the publ | Package type | Supports request forwarding | |-----------------------------------------------------|-----------------------------| -| [Maven](../maven_repository/index.md) | Y | -| [npm](../npm_registry/index.md) | Y | +| [Maven](../maven_repository/index.md) | [Yes (disabled by default)](../../admin_area/settings/continuous_integration.md#maven-forwarding) | +| [npm](../npm_registry/index.md) | [Yes](../../admin_area/settings/continuous_integration.md#npm-forwarding) | | [NuGet](../nuget_repository/index.md) | N | -| [PyPI](../pypi_repository/index.md) | Y | +| [PyPI](../pypi_repository/index.md) | [Yes](../../admin_area/settings/continuous_integration.md#pypi-forwarding) | | [Generic packages](../generic_packages/index.md) | N | | [Terraform](../terraform_module_registry/index.md) | N | | [Composer](../composer_repository/index.md) | N | @@ -66,6 +66,23 @@ Requests for packages not found in your GitLab project are forwarded to the publ | [Go](../go_proxy/index.md) | N | | [Ruby gems](../rubygems_registry/index.md) | N | +### Deleting packages + +When package requests are forwarded to a public registry, deleting packages can +be a [dependency confusion vulnerability](https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610). + +If a system tries to pull a deleted package, the request is forwarded to the public +registry. If a package with the same name and version is found in the public registry, that package +is pulled instead. There is a risk that the package pulled from the registry might not be +what is expected, and could even be malicious. + +To reduce the associated security risks, before deleting a package you can: + +- Verify the package is not being actively used. +- Disable request forwarding: + - Instance administrators can disable forwarding in the [**Continuous Integration** section](../../admin_area/settings/continuous_integration.md#package-registry-configuration) of the Admin Area. + - Group owners can disable forwarding in the **Packages and Registries** section of the group settings. + ## Allow or prevent duplicates **(FREE)** By default, the GitLab package registry either allows or prevents duplicates based on the default of that specific package manager format. diff --git a/doc/user/project/protected_tags.md b/doc/user/project/protected_tags.md index aa0852565fd..1256599c521 100644 --- a/doc/user/project/protected_tags.md +++ b/doc/user/project/protected_tags.md @@ -86,6 +86,32 @@ To prevent this problem: Users can still create branches, but not tags, with the protected names. +## Allow deploy keys to create protected tags + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/325415) in GitLab 15.11. + +You can permit the owner of a [deploy key](deploy_keys/index.md) to create protected tags. +The deploy key works, even if the user isn't a member of the related project. However, the owner of the deploy +key must have at least read access to the project. + +Prerequisites: + +- The deploy key must be enabled for your project. A project deploy key is enabled by default when + it is created. However, a public deploy key must be + [granted](deploy_keys/index.md#grant-project-access-to-a-public-deploy-key) access to the + project. +- The deploy key must have [write access](deploy_keys/index.md#permissions) to your project + repository. + +To allow a deploy key to create a protected tag: + +1. On the top bar, select **Main menu > Projects** and find your project. +1. On the left sidebar, select **Settings > Repository**. +1. Expand **Protected tags**. +1. From the **Tag** dropdown list, select the tag you want to protect. +1. From the **Allowed to create** list, select the deploy key. +1. Select **Protect**. + ## Delete a protected tag You can manually delete protected tags with the GitLab API, or the diff --git a/lib/gitlab/utils/error_message.rb b/lib/gitlab/utils/error_message.rb new file mode 100644 index 00000000000..e9c6f8a5847 --- /dev/null +++ b/lib/gitlab/utils/error_message.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Gitlab + module Utils + module ErrorMessage + extend self + + def to_user_facing(message) + "UF: #{message}" + end + end + end +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index d37b657ec8f..56e1df81c06 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -7498,6 +7498,9 @@ msgstr "" msgid "Branches|Plese type the following to confirm: %{codeStart}delete%{codeEnd}." msgstr "" +msgid "Branches|See all branch-related settings together with branch rules" +msgstr "" + msgid "Branches|Show active branches" msgstr "" @@ -7543,6 +7546,9 @@ msgstr "" msgid "Branches|Unable to load branches" msgstr "" +msgid "Branches|View branch rules" +msgstr "" + msgid "Branches|Yes, delete branch" msgstr "" @@ -7552,6 +7558,9 @@ msgstr "" msgid "Branches|You are about to %{strongStart}delete all branches%{strongEnd} that were merged into %{codeStart}%{defaultBranch}%{codeEnd}." msgstr "" +msgid "Branches|You can now find an overview of settings for protected branches, merge request approvals, status checks, and security approvals conveniently in one spot." +msgstr "" + msgid "Branches|You're about to permanently delete the branch %{branchName}." msgstr "" @@ -38848,6 +38857,9 @@ msgstr "" msgid "SecurityConfiguration|Security training" msgstr "" +msgid "SecurityConfiguration|Something went wrong. Please refresh the page, or try again later." +msgstr "" + msgid "SecurityConfiguration|The status of the tools only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}." msgstr "" diff --git a/spec/controllers/concerns/product_analytics_tracking_spec.rb b/spec/controllers/concerns/product_analytics_tracking_spec.rb index ed730d5c7f6..b0074b52aa2 100644 --- a/spec/controllers/concerns/product_analytics_tracking_spec.rb +++ b/spec/controllers/concerns/product_analytics_tracking_spec.rb @@ -8,7 +8,11 @@ RSpec.describe ProductAnalyticsTracking, :snowplow, feature_category: :product_a let(:user) { create(:user) } let(:event_name) { 'an_event' } + let(:event_action) { 'an_action' } + let(:event_label) { 'a_label' } + let!(:group) { create(:group) } + let_it_be(:project) { create(:project) } before do allow(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event) @@ -23,6 +27,8 @@ RSpec.describe ProductAnalyticsTracking, :snowplow, feature_category: :product_a :index, :show, name: 'an_event', + action: 'an_action', + label: 'a_label', destinations: [:redis_hll, :snowplow], conditions: [:custom_condition_one?, :custom_condition_two?] ) { |controller| controller.get_custom_id } @@ -49,6 +55,10 @@ RSpec.describe ProductAnalyticsTracking, :snowplow, feature_category: :product_a Group.first end + def tracking_project_source + Project.first + end + def custom_condition_one? true end @@ -69,7 +79,10 @@ RSpec.describe ProductAnalyticsTracking, :snowplow, feature_category: :product_a expect_snowplow_event( category: anything, - action: event_name, + action: event_action, + property: event_name, + label: event_label, + project: project, namespace: group, user: user, context: [context] diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb index de71a48d2dc..23a9ab74a7a 100644 --- a/spec/features/admin/admin_health_check_spec.rb +++ b/spec/features/admin/admin_health_check_spec.rb @@ -2,8 +2,9 @@ require 'spec_helper' -RSpec.describe "Admin Health Check", feature_category: :continuous_verification do +RSpec.describe "Admin Health Check", :js, feature_category: :continuous_verification do include StubENV + include Spec::Support::Helpers::ModalHelpers let_it_be(:admin) { create(:admin) } before do @@ -30,7 +31,8 @@ RSpec.describe "Admin Health Check", feature_category: :continuous_verification describe 'reload access token' do it 'changes the access token' do orig_token = Gitlab::CurrentSettings.health_check_access_token - click_button 'Reset health check access token' + click_link 'Reset health check access token' + accept_gl_confirm('Are you sure you want to reset the health check token?') expect(page).to have_content('New health check access token has been generated!') expect(find('#health-check-token').text).not_to eq orig_token diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb index fc7833809b3..e1f1a63565c 100644 --- a/spec/features/projects/branches_spec.rb +++ b/spec/features/projects/branches_spec.rb @@ -201,6 +201,12 @@ RSpec.describe 'Branches', feature_category: :projects do end end + describe 'Link to branch rules' do + it 'does not have possibility to navigate to branch rules', :js do + expect(page).not_to have_content(s_("Branches|View branch rules")) + end + end + context 'on project with 0 branch' do let(:project) { create(:project, :public, :empty_repo) } let(:repository) { project.repository } @@ -239,6 +245,17 @@ RSpec.describe 'Branches', feature_category: :projects do expect(page).not_to have_content 'Merge request' end end + + describe 'Navigate to branch rules from branches page' do + it 'shows repository settings page with Branch rules section expanded' do + visit project_branches_path(project) + + view_branch_rules + + expect(page).to have_content( + _('Define rules for who can push, merge, and the required approvals for each branch.')) + end + end end end @@ -353,4 +370,11 @@ RSpec.describe 'Branches', feature_category: :projects do click_button 'Yes, delete branch' end end + + def view_branch_rules + page.within('.nav-controls') do + click_link s_("Branches|View branch rules") + end + wait_for_requests + end end diff --git a/spec/frontend/lib/utils/error_message_spec.js b/spec/frontend/lib/utils/error_message_spec.js new file mode 100644 index 00000000000..17b5168c32f --- /dev/null +++ b/spec/frontend/lib/utils/error_message_spec.js @@ -0,0 +1,65 @@ +import { parseErrorMessage, USER_FACING_ERROR_MESSAGE_PREFIX } from '~/lib/utils/error_message'; + +const defaultErrorMessage = 'Something caused this error'; +const userFacingErrorMessage = 'User facing error message'; +const nonUserFacingErrorMessage = 'NonUser facing error message'; +const genericErrorMessage = 'Some error message'; + +describe('error message', () => { + describe('when given an errormessage object', () => { + const errorMessageObject = { + options: { + cause: defaultErrorMessage, + }, + filename: 'error.js', + linenumber: 7, + }; + + it('returns the correct values for userfacing errors', () => { + const userFacingObject = errorMessageObject; + userFacingObject.message = `${USER_FACING_ERROR_MESSAGE_PREFIX} ${userFacingErrorMessage}`; + + expect(parseErrorMessage(userFacingObject)).toEqual({ + message: userFacingErrorMessage, + userFacing: true, + }); + }); + + it('returns the correct values for non userfacing errors', () => { + const nonUserFacingObject = errorMessageObject; + nonUserFacingObject.message = nonUserFacingErrorMessage; + + expect(parseErrorMessage(nonUserFacingObject)).toEqual({ + message: nonUserFacingErrorMessage, + userFacing: false, + }); + }); + }); + + describe('when given an errormessage string', () => { + it('returns the correct values for userfacing errors', () => { + expect( + parseErrorMessage(`${USER_FACING_ERROR_MESSAGE_PREFIX} ${genericErrorMessage}`), + ).toEqual({ + message: genericErrorMessage, + userFacing: true, + }); + }); + + it('returns the correct values for non userfacing errors', () => { + expect(parseErrorMessage(genericErrorMessage)).toEqual({ + message: genericErrorMessage, + userFacing: false, + }); + }); + }); + + describe('when given nothing', () => { + it('returns an empty error message', () => { + expect(parseErrorMessage()).toEqual({ + message: '', + userFacing: false, + }); + }); + }); +}); diff --git a/spec/frontend/security_configuration/components/app_spec.js b/spec/frontend/security_configuration/components/app_spec.js index 5ef387adf39..0ca350f9ed7 100644 --- a/spec/frontend/security_configuration/components/app_spec.js +++ b/spec/frontend/security_configuration/components/app_spec.js @@ -26,6 +26,8 @@ import { REPORT_TYPE_LICENSE_COMPLIANCE, REPORT_TYPE_SAST, } from '~/vue_shared/security_reports/constants'; +import { USER_FACING_ERROR_MESSAGE_PREFIX } from '~/lib/utils/error_message'; +import { manageViaMRErrorMessage } from '../constants'; const upgradePath = '/upgrade'; const autoDevopsHelpPagePath = '/autoDevopsHelpPagePath'; @@ -200,18 +202,21 @@ describe('App component', () => { }); }); - describe('when error occurs', () => { + describe('when user facing error occurs', () => { it('should show Alert with error Message', async () => { expect(findManageViaMRErrorAlert().exists()).toBe(false); - findFeatureCards().at(1).vm.$emit('error', 'There was a manage via MR error'); + // Prefixed with USER_FACING_ERROR_MESSAGE_PREFIX as used in lib/gitlab/utils/error_message.rb to indicate a user facing error + findFeatureCards() + .at(1) + .vm.$emit('error', `${USER_FACING_ERROR_MESSAGE_PREFIX} ${manageViaMRErrorMessage}`); await nextTick(); expect(findManageViaMRErrorAlert().exists()).toBe(true); - expect(findManageViaMRErrorAlert().text()).toEqual('There was a manage via MR error'); + expect(findManageViaMRErrorAlert().text()).toEqual(manageViaMRErrorMessage); }); it('should hide Alert when it is dismissed', async () => { - findFeatureCards().at(1).vm.$emit('error', 'There was a manage via MR error'); + findFeatureCards().at(1).vm.$emit('error', manageViaMRErrorMessage); await nextTick(); expect(findManageViaMRErrorAlert().exists()).toBe(true); @@ -221,6 +226,17 @@ describe('App component', () => { expect(findManageViaMRErrorAlert().exists()).toBe(false); }); }); + + describe('when non-user facing error occurs', () => { + it('should show Alert with generic error Message', async () => { + expect(findManageViaMRErrorAlert().exists()).toBe(false); + findFeatureCards().at(1).vm.$emit('error', manageViaMRErrorMessage); + + await nextTick(); + expect(findManageViaMRErrorAlert().exists()).toBe(true); + expect(findManageViaMRErrorAlert().text()).toEqual(i18n.genericErrorText); + }); + }); }); describe('Auto DevOps hint alert', () => { diff --git a/spec/frontend/security_configuration/components/feature_card_spec.js b/spec/frontend/security_configuration/components/feature_card_spec.js index 7c91c13c6d7..23edd8a69de 100644 --- a/spec/frontend/security_configuration/components/feature_card_spec.js +++ b/spec/frontend/security_configuration/components/feature_card_spec.js @@ -5,6 +5,7 @@ import FeatureCard from '~/security_configuration/components/feature_card.vue'; import FeatureCardBadge from '~/security_configuration/components/feature_card_badge.vue'; import ManageViaMr from '~/vue_shared/security_configuration/components/manage_via_mr.vue'; import { REPORT_TYPE_SAST } from '~/vue_shared/security_reports/constants'; +import { manageViaMRErrorMessage } from '../constants'; import { makeFeature } from './utils'; describe('FeatureCard component', () => { @@ -106,8 +107,8 @@ describe('FeatureCard component', () => { }); it('should catch and emit manage-via-mr-error', () => { - findManageViaMr().vm.$emit('error', 'There was a manage via MR error'); - expect(wrapper.emitted('error')).toEqual([['There was a manage via MR error']]); + findManageViaMr().vm.$emit('error', manageViaMRErrorMessage); + expect(wrapper.emitted('error')).toEqual([[manageViaMRErrorMessage]]); }); }); diff --git a/spec/frontend/security_configuration/constants.js b/spec/frontend/security_configuration/constants.js new file mode 100644 index 00000000000..d31036a2534 --- /dev/null +++ b/spec/frontend/security_configuration/constants.js @@ -0,0 +1 @@ +export const manageViaMRErrorMessage = 'There was a manage via MR error'; diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index 7f26190830e..0bfca9a290b 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -291,7 +291,7 @@ RSpec.describe GitlabSchema.types['Project'] do let_it_be(:project) { create(:project_empty_repo) } it 'raises an error' do - expect(subject['errors'][0]['message']).to eq('You must <a target="_blank" rel="noopener noreferrer" ' \ + expect(subject['errors'][0]['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.') diff --git a/spec/lib/gitlab/utils/error_message_spec.rb b/spec/lib/gitlab/utils/error_message_spec.rb new file mode 100644 index 00000000000..2c2d16656e8 --- /dev/null +++ b/spec/lib/gitlab/utils/error_message_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe Gitlab::Utils::ErrorMessage, feature_category: :error_tracking do + let(:klass) do + Class.new do + include Gitlab::Utils::ErrorMessage + end + end + + subject(:object) { klass.new } + + describe 'error message' do + subject { object.to_user_facing(string) } + + let(:string) { 'Error Message' } + + it "returns input prefixed with UF:" do + is_expected.to eq 'UF: Error Message' + end + 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 8bfe57f4549..9fcdd296ebe 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 @@ -168,7 +168,7 @@ RSpec.shared_examples_for 'services security ci configuration create service' do 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('You must <a target="_blank" rel="noopener noreferrer" ' \ + 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.') |