Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-11-19 15:09:13 +00:00
parent fa6c2426a5
commit a2f3b3e5cf
46 changed files with 532 additions and 43 deletions

View File

@ -7,6 +7,7 @@ const PERSISTENT_USER_CALLOUTS = [
'.js-buy-pipeline-minutes-notification-callout', '.js-buy-pipeline-minutes-notification-callout',
'.js-token-expiry-callout', '.js-token-expiry-callout',
'.js-registration-enabled-callout', '.js-registration-enabled-callout',
'.js-new-user-signups-cap-reached',
]; ];
const initCallouts = () => { const initCallouts = () => {

View File

@ -5,7 +5,7 @@ class Admin::CohortsController < Admin::ApplicationController
track_unique_visits :index, target_id: 'i_analytics_cohorts' track_unique_visits :index, target_id: 'i_analytics_cohorts'
feature_category :instance_statistics feature_category :devops_reports
def index def index
if Gitlab::CurrentSettings.usage_ping_enabled if Gitlab::CurrentSettings.usage_ping_enabled

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
class Admin::InstanceReviewController < Admin::ApplicationController class Admin::InstanceReviewController < Admin::ApplicationController
feature_category :instance_statistics feature_category :devops_reports
def index def index
redirect_to("#{::Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL}/instance_review?#{instance_review_params}") redirect_to("#{::Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL}/instance_review?#{instance_review_params}")

View File

@ -7,7 +7,7 @@ class Admin::InstanceStatisticsController < Admin::ApplicationController
track_unique_visits :index, target_id: 'i_analytics_instance_statistics' track_unique_visits :index, target_id: 'i_analytics_instance_statistics'
feature_category :instance_statistics feature_category :devops_reports
def index def index
end end

View File

@ -8,6 +8,8 @@ module WikiActions
include RedisTracking include RedisTracking
extend ActiveSupport::Concern extend ActiveSupport::Concern
RESCUE_GIT_TIMEOUTS_IN = %w[show edit history diff pages].freeze
included do included do
before_action { respond_to :html } before_action { respond_to :html }
@ -38,6 +40,12 @@ module WikiActions
feature: :track_unique_wiki_page_views, feature_default_enabled: true feature: :track_unique_wiki_page_views, feature_default_enabled: true
helper_method :view_file_button, :diff_file_html_data helper_method :view_file_button, :diff_file_html_data
rescue_from ::Gitlab::Git::CommandTimedOut do |exc|
raise exc unless RESCUE_GIT_TIMEOUTS_IN.include?(action_name)
render 'shared/wikis/git_error'
end
end end
def new def new
@ -46,11 +54,7 @@ module WikiActions
# rubocop:disable Gitlab/ModuleWithInstanceVariables # rubocop:disable Gitlab/ModuleWithInstanceVariables
def pages def pages
@wiki_pages = Kaminari.paginate_array( @wiki_entries = WikiDirectory.group_pages(wiki_pages)
wiki.list_pages(sort: params[:sort], direction: params[:direction])
).page(params[:page])
@wiki_entries = WikiDirectory.group_pages(@wiki_pages)
render 'shared/wikis/pages' render 'shared/wikis/pages'
end end
@ -225,9 +229,19 @@ module WikiActions
unless @sidebar_page # Fallback to default sidebar unless @sidebar_page # Fallback to default sidebar
@sidebar_wiki_entries, @sidebar_limited = wiki.sidebar_entries @sidebar_wiki_entries, @sidebar_limited = wiki.sidebar_entries
end end
rescue ::Gitlab::Git::CommandTimedOut => e
@sidebar_error = e
end end
# rubocop:enable Gitlab/ModuleWithInstanceVariables # rubocop:enable Gitlab/ModuleWithInstanceVariables
def wiki_pages
strong_memoize(:wiki_pages) do
Kaminari.paginate_array(
wiki.list_pages(sort: params[:sort], direction: params[:direction])
).page(params[:page])
end
end
def wiki_params def wiki_params
params.require(:wiki).permit(:title, :content, :format, :message, :last_commit_sha) params.require(:wiki).permit(:title, :content, :format, :message, :last_commit_sha)
end end

View File

@ -3,7 +3,7 @@
class Projects::AlertManagementController < Projects::ApplicationController class Projects::AlertManagementController < Projects::ApplicationController
before_action :authorize_read_alert_management_alert! before_action :authorize_read_alert_management_alert!
feature_category :alert_management feature_category :incident_management
def index def index
end end

View File

@ -10,7 +10,7 @@ module Projects
prepend_before_action :repository, :project_without_auth prepend_before_action :repository, :project_without_auth
feature_category :alert_management feature_category :incident_management
def create def create
token = extract_alert_manager_token(request) token = extract_alert_manager_token(request)

View File

@ -16,7 +16,7 @@ module Projects
before_action :authorize_read_prometheus_alerts!, except: [:notify] before_action :authorize_read_prometheus_alerts!, except: [:notify]
before_action :alert, only: [:update, :show, :destroy, :metrics_dashboard] before_action :alert, only: [:update, :show, :destroy, :metrics_dashboard]
feature_category :alert_management feature_category :incident_management
def index def index
render json: serialize_as_json(alerts) render json: serialize_as_json(alerts)

View File

@ -26,7 +26,8 @@ class UserCallout < ApplicationRecord
suggest_pipeline: 22, suggest_pipeline: 22,
customize_homepage: 23, customize_homepage: 23,
feature_flags_new_version: 24, feature_flags_new_version: 24,
registration_enabled_callout: 25 registration_enabled_callout: 25,
new_user_signups_cap_reached: 26 # EE-only
} }
validates :user, presence: true validates :user, presence: true

View File

@ -17,6 +17,7 @@
= render_account_recovery_regular_check = render_account_recovery_regular_check
= render_if_exists "layouts/header/ee_subscribable_banner" = render_if_exists "layouts/header/ee_subscribable_banner"
= render_if_exists "shared/namespace_storage_limit_alert" = render_if_exists "shared/namespace_storage_limit_alert"
= render_if_exists "shared/new_user_signups_cap_reached_alert"
= yield :customize_homepage_banner = yield :customize_homepage_banner
- unless @hide_breadcrumbs - unless @hide_breadcrumbs
= render "layouts/nav/breadcrumbs" = render "layouts/nav/breadcrumbs"

View File

@ -1,4 +1,4 @@
- pretty_name = html_escape(@project&.full_name) || html_escape_once(_('&lt;project name&gt;')).html_safe - pretty_name = @project&.full_name ? html_escape(@project&.full_name) : '<' + _('project name') + '>'
- run_actions_text = html_escape(s_("ProjectService|Perform common operations on GitLab project: %{project_name}")) % { project_name: pretty_name } - run_actions_text = html_escape(s_("ProjectService|Perform common operations on GitLab project: %{project_name}")) % { project_name: pretty_name }
%p= s_("ProjectService|To set up this service:") %p= s_("ProjectService|To set up this service:")

View File

@ -1,4 +1,4 @@
- pretty_name = @project&.full_name || _('&lt;project name&gt;') - pretty_name = @project&.full_name ? html_escape(@project&.full_name) : '<' + _('project name') + '>'
- run_actions_text = html_escape_once(s_("ProjectService|Perform common operations on GitLab project: %{project_name}") % { project_name: pretty_name }) - run_actions_text = html_escape_once(s_("ProjectService|Perform common operations on GitLab project: %{project_name}") % { project_name: pretty_name })
.info-well .info-well

View File

@ -0,0 +1,6 @@
.gl-alert.gl-alert-info
= sprite_icon('information-o', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
%button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
= sprite_icon('close', css_class: 'gl-icon')
.gl-alert-body
= body

View File

@ -10,7 +10,7 @@
= s_('Webhooks|Use this token to validate received payloads. It will be sent with the request in the X-Gitlab-Token HTTP header.') = s_('Webhooks|Use this token to validate received payloads. It will be sent with the request in the X-Gitlab-Token HTTP header.')
.form-group .form-group
= form.label :url, s_('Webhooks|Trigger'), class: 'label-bold' = form.label :url, s_('Webhooks|Trigger'), class: 'label-bold'
%ul.list-unstyled.prepend-left-20 %ul.list-unstyled.gl-ml-6
%li %li
= form.check_box :push_events, class: 'form-check-input' = form.check_box :push_events, class: 'form-check-input'
= form.label :push_events, class: 'list-label form-check-label ml-1' do = form.label :push_events, class: 'list-label form-check-label ml-1' do

View File

@ -10,11 +10,14 @@
= sprite_icon('download', css_class: 'gl-mr-2') = sprite_icon('download', css_class: 'gl-mr-2')
%span= _("Clone repository") %span= _("Clone repository")
- if @sidebar_error.present?
= render 'shared/alert_info', body: s_('Wiki|The sidebar failed to load. You can reload the page to try again.')
.blocks-container .blocks-container
.block.block-first.w-100 .block.block-first.w-100
- if @sidebar_page - if @sidebar_page
= render_wiki_content(@sidebar_page) = render_wiki_content(@sidebar_page)
- else - elsif @sidebar_wiki_entries
%ul.wiki-pages %ul.wiki-pages
= render @sidebar_wiki_entries, context: 'sidebar' = render @sidebar_wiki_entries, context: 'sidebar'
.block.w-100 .block.w-100

View File

@ -0,0 +1,14 @@
- if @page
- wiki_page_title @page
- add_page_specific_style 'page_bundles/wiki'
- git_access_url = wiki_path(@wiki, action: :git_access)
.wiki-page-header.top-area.gl-flex-direction-column.gl-lg-flex-direction-row
.gl-mt-5.gl-mb-3
.gl-display-flex.gl-justify-content-space-between
%h2.gl-mt-0.gl-mb-5{ data: { qa_selector: 'wiki_page_title', testid: 'wiki_page_title' } }= @page ? @page.human_title : _('Failed to retrieve page')
.js-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki_page_content' } }
= _('The page could not be displayed because it timed out.')
= html_escape(_('You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}')) % { linkStart: "<a href=\"#{git_access_url}\">".html_safe, linkEnd: '</a>'.html_safe, cloneIcon: sprite_icon('download', css_class: 'gl-mr-2').html_safe }

View File

@ -124,7 +124,7 @@
:idempotent: :idempotent:
:tags: [] :tags: []
- :name: cronjob:analytics_instance_statistics_count_job_trigger - :name: cronjob:analytics_instance_statistics_count_job_trigger
:feature_category: :instance_statistics :feature_category: :devops_reports
:has_external_dependencies: :has_external_dependencies:
:urgency: :low :urgency: :low
:resource_boundary: :unknown :resource_boundary: :unknown
@ -1329,7 +1329,7 @@
:idempotent: true :idempotent: true
:tags: [] :tags: []
- :name: analytics_instance_statistics_counter_job - :name: analytics_instance_statistics_counter_job
:feature_category: :instance_statistics :feature_category: :devops_reports
:has_external_dependencies: :has_external_dependencies:
:urgency: :low :urgency: :low
:resource_boundary: :unknown :resource_boundary: :unknown

View File

@ -8,7 +8,7 @@ module Analytics
DEFAULT_DELAY = 3.minutes.freeze DEFAULT_DELAY = 3.minutes.freeze
feature_category :instance_statistics feature_category :devops_reports
urgency :low urgency :low
idempotent! idempotent!

View File

@ -5,7 +5,7 @@ module Analytics
class CounterJobWorker class CounterJobWorker
include ApplicationWorker include ApplicationWorker
feature_category :instance_statistics feature_category :devops_reports
urgency :low urgency :low
idempotent! idempotent!

View File

@ -0,0 +1,5 @@
---
title: Add index for API Fuzzing usage data
merge_request: 47692
author:
type: performance

View File

@ -0,0 +1,5 @@
---
title: Add `increment_counter` to Usage Ping API
merge_request: 47309
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Catch wiki timeouts when rendering pages
merge_request: 46627
author:
type: fixed

View File

@ -9,7 +9,6 @@
--- ---
- accessibility_testing - accessibility_testing
- advanced_deployments - advanced_deployments
- alert_management
- analysis - analysis
- api - api
- attack_emulation - attack_emulation
@ -21,7 +20,7 @@
- boards - boards
- chatops - chatops
- cloud_native_installation - cloud_native_installation
- cluster_cost_optimization - cluster_cost_management
- code_analytics - code_analytics
- code_quality - code_quality
- code_review - code_review
@ -47,6 +46,7 @@
- epics - epics
- error_tracking - error_tracking
- feature_flags - feature_flags
- five_minute_production_app
- foundations - foundations
- fuzz_testing - fuzz_testing
- gdk - gdk
@ -59,9 +59,10 @@
- helm_chart_registry - helm_chart_registry
- importers - importers
- incident_management - incident_management
- infrastructure
- infrastructure_as_code - infrastructure_as_code
- insider_threat
- insights - insights
- instance_statistics
- integrations - integrations
- interactive_application_security_testing - interactive_application_security_testing
- internationalization - internationalization
@ -75,8 +76,11 @@
- load_testing - load_testing
- logging - logging
- malware_scanning - malware_scanning
- memory
- merge_trains - merge_trains
- metrics - metrics
- mlops
- mobile_signing_deployment
- navigation - navigation
- omnibus_package - omnibus_package
- package_registry - package_registry
@ -114,7 +118,7 @@
- tracing - tracing
- usability_testing - usability_testing
- users - users
- value_stream_analytics - value_stream_management
- vulnerability_database - vulnerability_database
- vulnerability_management - vulnerability_management
- web_firewall - web_firewall

View File

@ -0,0 +1,8 @@
---
name: usage_data_static_site_editor_commits
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47309
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284082
milestone: '13.6'
type: development
group: group::static_site_editor
default_enabled: false

View File

@ -0,0 +1,8 @@
---
name: usage_data_static_site_editor_merge_requests
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47309
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284083
milestone: '13.6'
type: development
group: group::static_site_editor
default_enabled: false

View File

@ -288,6 +288,8 @@
- 1 - 1
- - repository_update_remote_mirror - - repository_update_remote_mirror
- 1 - 1
- - requirements_management_import_requirements_csv
- 1
- - requirements_management_process_requirements_reports - - requirements_management_process_requirements_reports
- 1 - 1
- - security_scans - - security_scans

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
class UpdateIndexSecureForApiFuzzingTelemetry < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
OLD_SECURE_INDEX_NAME = 'index_secure_ci_builds_on_user_id_created_at_parser_features'
NEW_SECURE_INDEX_NAME = 'index_secure_ci_builds_on_user_id_name_created_at'
def up
add_concurrent_index(:ci_builds,
[:user_id, :name, :created_at],
where: "(((type)::text = 'Ci::Build'::text) AND ((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('license_scanning'::character varying)::text, ('sast'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('apifuzzer_fuzz'::character varying)::text, ('apifuzzer_fuzz_dnd'::character varying)::text, ('secret_detection'::character varying)::text])))",
name: NEW_SECURE_INDEX_NAME)
remove_concurrent_index_by_name :ci_builds, OLD_SECURE_INDEX_NAME
end
def down
add_concurrent_index(:ci_builds,
[:user_id, :created_at],
where: "(((type)::text = 'Ci::Build'::text) AND ((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('license_scanning'::character varying)::text, ('sast'::character varying)::text, ('secret_detection'::character varying)::text])))",
name: OLD_SECURE_INDEX_NAME)
remove_concurrent_index_by_name :ci_builds, NEW_SECURE_INDEX_NAME
end
end

View File

@ -0,0 +1 @@
9f2c60df8e89f89d721f7f7917048eb914fa7c7726ec42dcb772ff7a90c54a9c

View File

@ -21945,7 +21945,7 @@ CREATE UNIQUE INDEX index_scim_identities_on_user_id_and_group_id ON scim_identi
CREATE UNIQUE INDEX index_scim_oauth_access_tokens_on_group_id_and_token_encrypted ON scim_oauth_access_tokens USING btree (group_id, token_encrypted); CREATE UNIQUE INDEX index_scim_oauth_access_tokens_on_group_id_and_token_encrypted ON scim_oauth_access_tokens USING btree (group_id, token_encrypted);
CREATE INDEX index_secure_ci_builds_on_user_id_created_at_parser_features ON ci_builds USING btree (user_id, created_at) WHERE (((type)::text = 'Ci::Build'::text) AND ((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('license_scanning'::character varying)::text, ('sast'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('secret_detection'::character varying)::text]))); CREATE INDEX index_secure_ci_builds_on_user_id_name_created_at ON ci_builds USING btree (user_id, name, created_at) WHERE (((type)::text = 'Ci::Build'::text) AND ((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('license_scanning'::character varying)::text, ('sast'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('apifuzzer_fuzz'::character varying)::text, ('apifuzzer_fuzz_dnd'::character varying)::text, ('secret_detection'::character varying)::text])));
CREATE INDEX index_security_ci_builds_on_name_and_id_parser_features ON ci_builds USING btree (name, id) WHERE (((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('secret_detection'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('license_scanning'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text)); CREATE INDEX index_security_ci_builds_on_name_and_id_parser_features ON ci_builds USING btree (name, id) WHERE (((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('secret_detection'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('license_scanning'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text));

View File

@ -536,6 +536,15 @@ module API
) )
end end
def increment_counter(event_name)
feature_name = "usage_data_#{event_name}"
return unless Feature.enabled?(feature_name)
Gitlab::UsageDataCounters.count(event_name)
rescue => error
Gitlab::AppLogger.warn("Redis tracking event failed for event: #{event_name}, message: #{error.message}")
end
# @param event_name [String] the event name # @param event_name [String] the event name
# @param values [Array|String] the values counted # @param values [Array|String] the values counted
def increment_unique_values(event_name, values) def increment_unique_values(event_name, values)

View File

@ -4,7 +4,7 @@ module API
class Statistics < ::API::Base class Statistics < ::API::Base
before { authenticated_as_admin! } before { authenticated_as_admin! }
feature_category :instance_statistics feature_category :devops_reports
COUNTED_ITEMS = [Project, User, Group, ForkNetworkMember, ForkNetwork, Issue, COUNTED_ITEMS = [Project, User, Group, ForkNetworkMember, ForkNetwork, Issue,
MergeRequest, Note, Snippet, Key, Milestone].freeze MergeRequest, Note, Snippet, Key, Milestone].freeze

View File

@ -20,6 +20,18 @@ module API
requires :event, type: String, desc: 'The event name that should be tracked' requires :event, type: String, desc: 'The event name that should be tracked'
end end
post 'increment_counter' do
event_name = params[:event]
increment_counter(event_name)
status :ok
end
params do
requires :event, type: String, desc: 'The event name that should be tracked'
end
post 'increment_unique_users' do post 'increment_unique_users' do
event_name = params[:event] event_name = params[:event]

View File

@ -17,6 +17,7 @@ module Gitlab
CommitError = Class.new(BaseError) CommitError = Class.new(BaseError)
OSError = Class.new(BaseError) OSError = Class.new(BaseError)
UnknownRef = Class.new(BaseError) UnknownRef = Class.new(BaseError)
CommandTimedOut = Class.new(CommandError)
class << self class << self
include Gitlab::EncodingHelper include Gitlab::EncodingHelper

View File

@ -9,6 +9,8 @@ module Gitlab
raise Gitlab::Git::Repository::NoRepository.new(e) raise Gitlab::Git::Repository::NoRepository.new(e)
rescue GRPC::InvalidArgument => e rescue GRPC::InvalidArgument => e
raise ArgumentError.new(e) raise ArgumentError.new(e)
rescue GRPC::DeadlineExceeded => e
raise Gitlab::Git::CommandTimedOut.new(e)
rescue GRPC::BadStatus => e rescue GRPC::BadStatus => e
raise Gitlab::Git::CommandError.new(e) raise Gitlab::Git::CommandError.new(e)
end end

View File

@ -296,20 +296,7 @@ module Gitlab
# @return [Array<#totals>] An array of objects that respond to `#totals` # @return [Array<#totals>] An array of objects that respond to `#totals`
def usage_data_counters def usage_data_counters
[ Gitlab::UsageDataCounters.counters
Gitlab::UsageDataCounters::WikiPageCounter,
Gitlab::UsageDataCounters::WebIdeCounter,
Gitlab::UsageDataCounters::NoteCounter,
Gitlab::UsageDataCounters::SnippetCounter,
Gitlab::UsageDataCounters::SearchCounter,
Gitlab::UsageDataCounters::CycleAnalyticsCounter,
Gitlab::UsageDataCounters::ProductivityAnalyticsCounter,
Gitlab::UsageDataCounters::SourceCodeCounter,
Gitlab::UsageDataCounters::MergeRequestCounter,
Gitlab::UsageDataCounters::DesignsCounter,
Gitlab::UsageDataCounters::KubernetesAgentCounter,
Gitlab::UsageDataCounters::StaticSiteEditorCounter
]
end end
def components_usage_data def components_usage_data

View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
module Gitlab
module UsageDataCounters
COUNTERS = [
WikiPageCounter,
WebIdeCounter,
NoteCounter,
SnippetCounter,
SearchCounter,
CycleAnalyticsCounter,
ProductivityAnalyticsCounter,
SourceCodeCounter,
MergeRequestCounter,
DesignsCounter,
KubernetesAgentCounter,
StaticSiteEditorCounter
].freeze
UsageDataCounterError = Class.new(StandardError)
UnknownEvent = Class.new(UsageDataCounterError)
class << self
def counters
self::COUNTERS
end
def count(event_name)
counters.each do |counter|
event = counter.fetch_supported_event(event_name)
return counter.count(event) if event
end
raise UnknownEvent, "Cannot find counter for event #{event_name}"
end
end
end
end

View File

@ -29,6 +29,12 @@ module Gitlab::UsageDataCounters
known_events.map { |event| [counter_key(event), -1] }.to_h known_events.map { |event| [counter_key(event), -1] }.to_h
end end
def fetch_supported_event(event_name)
return if prefix.present? && !event_name.start_with?(prefix)
known_events.find { |event| counter_key(event) == event_name.to_sym }
end
private private
def require_known_event(event) def require_known_event(event)

View File

@ -4,6 +4,7 @@ module Gitlab
module UsageDataCounters module UsageDataCounters
class SearchCounter < BaseCounter class SearchCounter < BaseCounter
KNOWN_EVENTS = %w[all_searches navbar_searches].freeze KNOWN_EVENTS = %w[all_searches navbar_searches].freeze
PREFIX = nil
class << self class << self
def redis_key(event) def redis_key(event)

View File

@ -906,9 +906,6 @@ msgstr ""
msgid "&lt;no scopes selected&gt;" msgid "&lt;no scopes selected&gt;"
msgstr "" msgstr ""
msgid "&lt;project name&gt;"
msgstr ""
msgid "'%{data}' at %{location} does not match format: %{format}" msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr "" msgstr ""
@ -2272,6 +2269,15 @@ msgstr ""
msgid "Administration" msgid "Administration"
msgstr "" msgstr ""
msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
msgstr ""
msgid "Admin|View pending user approvals"
msgstr ""
msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced" msgid "Advanced"
msgstr "" msgstr ""
@ -11444,6 +11450,9 @@ msgstr ""
msgid "Failed to reset key. Please try again." msgid "Failed to reset key. Please try again."
msgstr "" msgstr ""
msgid "Failed to retrieve page"
msgstr ""
msgid "Failed to save merge conflicts resolutions. Please try again!" msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr "" msgstr ""
@ -27028,6 +27037,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file" msgid "The number of times an upload record could not find its file"
msgstr "" msgstr ""
msgid "The page could not be displayed because it timed out."
msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues" msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr "" msgstr ""
@ -30707,6 +30719,9 @@ msgstr ""
msgid "Wiki|Pages" msgid "Wiki|Pages"
msgstr "" msgstr ""
msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
msgstr ""
msgid "Wiki|Title" msgid "Wiki|Title"
msgstr "" msgstr ""
@ -31007,6 +31022,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}." msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr "" msgstr ""
msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
msgstr ""
msgid "You cannot access the raw file. Please wait a minute." msgid "You cannot access the raw file. Please wait a minute."
msgstr "" msgstr ""
@ -32777,6 +32795,9 @@ msgstr ""
msgid "project members" msgid "project members"
msgstr "" msgstr ""
msgid "project name"
msgstr ""
msgid "projects" msgid "projects"
msgstr "" msgstr ""

View File

@ -20,6 +20,7 @@ UsageData/LargeTable:
- :Gitlab::Runtime - :Gitlab::Runtime
- :Gitaly::Server - :Gitaly::Server
- :Gitlab::UsageData - :Gitlab::UsageData
- :Gitlab::UsageDataCounters
- :License - :License
- :Rails - :Rails
- :Time - :Time

View File

@ -0,0 +1,34 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::UsageDataCounters::BaseCounter do
describe '.fetch_supported_event' do
subject { described_class.fetch_supported_event(event_name) }
let(:event_name) { 'generic_event' }
let(:prefix) { 'generic' }
let(:known_events) { %w[event another_event] }
before do
allow(described_class).to receive(:prefix) { prefix }
allow(described_class).to receive(:known_events) { known_events }
end
it 'returns the matching event' do
is_expected.to eq 'event'
end
context 'when event is unknown' do
let(:event_name) { 'generic_unknown_event' }
it { is_expected.to be_nil }
end
context 'when prefix does not match the event name' do
let(:prefix) { 'special' }
it { is_expected.to be_nil }
end
end
end

View File

@ -20,4 +20,12 @@ RSpec.describe Gitlab::UsageDataCounters::SearchCounter, :clean_gitlab_redis_sha
context 'navbar_searches counter' do context 'navbar_searches counter' do
it_behaves_like 'usage counter with totals', :navbar_searches it_behaves_like 'usage counter with totals', :navbar_searches
end end
describe '.fetch_supported_event' do
subject { described_class.fetch_supported_event(event_name) }
let(:event_name) { 'all_searches' }
it { is_expected.to eq 'all_searches' }
end
end end

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::UsageDataCounters do
describe '.usage_data_counters' do
subject { described_class.counters }
it { is_expected.to all(respond_to :totals) }
it { is_expected.to all(respond_to :fallback_totals) }
end
describe '.count' do
subject { described_class.count(event_name) }
let(:event_name) { 'static_site_editor_views' }
it 'increases a view counter' do
expect(Gitlab::UsageDataCounters::StaticSiteEditorCounter).to receive(:count).with('views')
subject
end
context 'when event_name is not defined' do
let(:event_name) { 'unknown' }
it 'raises an exception' do
expect { subject }.to raise_error(Gitlab::UsageDataCounters::UnknownEvent)
end
end
end
end

View File

@ -5,6 +5,87 @@ require 'spec_helper'
RSpec.describe API::UsageData do RSpec.describe API::UsageData do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
describe 'POST /usage_data/increment_counter' do
let(:endpoint) { '/usage_data/increment_counter' }
let(:known_event) { "#{known_event_prefix}_#{known_event_postfix}" }
let(:known_event_prefix) { "static_site_editor" }
let(:known_event_postfix) { 'commits' }
let(:unknown_event) { 'unknown' }
context 'without CSRF token' do
it 'returns forbidden' do
stub_feature_flags(usage_data_api: true)
allow(Gitlab::RequestForgeryProtection).to receive(:verified?).and_return(false)
post api(endpoint, user), params: { event: known_event }
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'usage_data_api feature not enabled' do
it 'returns not_found' do
stub_feature_flags(usage_data_api: false)
post api(endpoint, user), params: { event: known_event }
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'without authentication' do
it 'returns 401 response' do
post api(endpoint), params: { event: known_event }
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
context 'with authentication' do
before do
stub_feature_flags(usage_data_api: true)
stub_feature_flags("usage_data_#{known_event}" => true)
stub_application_setting(usage_ping_enabled: true)
allow(Gitlab::RequestForgeryProtection).to receive(:verified?).and_return(true)
end
context 'when event is missing from params' do
it 'returns bad request' do
post api(endpoint, user), params: {}
expect(response).to have_gitlab_http_status(:bad_request)
end
end
%w[merge_requests commits].each do |postfix|
context 'with correct params' do
let(:known_event_postfix) { postfix }
it 'returns status ok' do
expect(Gitlab::UsageDataCounters::BaseCounter).to receive(:count).with(known_event_postfix)
post api(endpoint, user), params: { event: known_event }
expect(response).to have_gitlab_http_status(:ok)
end
end
end
context 'with unknown event' do
before do
skip_feature_flags_yaml_validation
end
it 'returns status ok' do
expect(Gitlab::UsageDataCounters::BaseCounter).not_to receive(:count)
post api(endpoint, user), params: { event: unknown_event }
expect(response).to have_gitlab_http_status(:ok)
end
end
end
end
describe 'POST /usage_data/increment_unique_users' do describe 'POST /usage_data/increment_unique_users' do
let(:endpoint) { '/usage_data/increment_unique_users' } let(:endpoint) { '/usage_data/increment_unique_users' }
let(:known_event) { 'g_compliance_dashboard' } let(:known_event) { 'g_compliance_dashboard' }

View File

@ -14,6 +14,22 @@ RSpec.shared_examples 'wiki controller actions' do
sign_in(user) sign_in(user)
end end
shared_examples 'recovers from git timeout' do
let(:method_name) { :page }
context 'when we encounter git command errors' do
it 'renders the appropriate template', :aggregate_failures do
expect(controller).to receive(method_name) do
raise ::Gitlab::Git::CommandTimedOut, 'Deadline Exceeded'
end
request
expect(response).to render_template('shared/wikis/git_error')
end
end
end
describe 'GET #new' do describe 'GET #new' do
subject(:request) { get :new, params: routing_params } subject(:request) { get :new, params: routing_params }
@ -48,6 +64,12 @@ RSpec.shared_examples 'wiki controller actions' do
get :pages, params: routing_params.merge(id: wiki_title) get :pages, params: routing_params.merge(id: wiki_title)
end end
it_behaves_like 'recovers from git timeout' do
subject(:request) { get :pages, params: routing_params.merge(id: wiki_title) }
let(:method_name) { :wiki_pages }
end
it 'assigns the page collections' do it 'assigns the page collections' do
expect(assigns(:wiki_pages)).to contain_exactly(an_instance_of(WikiPage)) expect(assigns(:wiki_pages)).to contain_exactly(an_instance_of(WikiPage))
expect(assigns(:wiki_entries)).to contain_exactly(an_instance_of(WikiPage)) expect(assigns(:wiki_entries)).to contain_exactly(an_instance_of(WikiPage))
@ -99,6 +121,12 @@ RSpec.shared_examples 'wiki controller actions' do
end end
end end
it_behaves_like 'recovers from git timeout' do
subject(:request) { get :history, params: routing_params.merge(id: wiki_title) }
let(:allow_read_wiki) { true }
end
it_behaves_like 'fetching history', :ok do it_behaves_like 'fetching history', :ok do
let(:allow_read_wiki) { true } let(:allow_read_wiki) { true }
@ -139,6 +167,10 @@ RSpec.shared_examples 'wiki controller actions' do
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
end end
it_behaves_like 'recovers from git timeout' do
subject(:request) { get :diff, params: routing_params.merge(id: wiki_title, version_id: wiki.repository.commit.id) }
end
end end
describe 'GET #show' do describe 'GET #show' do
@ -151,6 +183,8 @@ RSpec.shared_examples 'wiki controller actions' do
context 'when page exists' do context 'when page exists' do
let(:id) { wiki_title } let(:id) { wiki_title }
it_behaves_like 'recovers from git timeout'
it 'renders the page' do it 'renders the page' do
request request
@ -161,6 +195,28 @@ RSpec.shared_examples 'wiki controller actions' do
expect(assigns(:sidebar_limited)).to be(false) expect(assigns(:sidebar_limited)).to be(false)
end end
context 'the sidebar fails to load' do
before do
allow(Wiki).to receive(:for_container).and_return(wiki)
wiki.wiki
expect(wiki).to receive(:find_sidebar) do
raise ::Gitlab::Git::CommandTimedOut, 'Deadline Exceeded'
end
end
it 'renders the page, and marks the sidebar as failed' do
request
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template('shared/wikis/_sidebar')
expect(assigns(:page).title).to eq(wiki_title)
expect(assigns(:sidebar_page)).to be_nil
expect(assigns(:sidebar_wiki_entries)).to be_nil
expect(assigns(:sidebar_limited)).to be_nil
expect(assigns(:sidebar_error)).to be_a_kind_of(::Gitlab::Git::CommandError)
end
end
context 'page view tracking' do context 'page view tracking' do
it_behaves_like 'tracking unique hll events', :track_unique_wiki_page_views do it_behaves_like 'tracking unique hll events', :track_unique_wiki_page_views do
let(:target_id) { 'wiki_action' } let(:target_id) { 'wiki_action' }
@ -308,6 +364,7 @@ RSpec.shared_examples 'wiki controller actions' do
subject(:request) { get(:edit, params: routing_params.merge(id: id_param)) } subject(:request) { get(:edit, params: routing_params.merge(id: id_param)) }
it_behaves_like 'edit action' it_behaves_like 'edit action'
it_behaves_like 'recovers from git timeout'
context 'when page content encoding is valid' do context 'when page content encoding is valid' do
render_views render_views

View File

@ -0,0 +1,93 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'shared/wikis/_sidebar.html.haml' do
let_it_be(:project) { create(:project) }
let_it_be(:wiki) { Wiki.for_container(project, project.default_owner) }
before do
assign(:wiki, wiki)
assign(:project, project)
end
it 'includes a link to clone the repository' do
render
expect(rendered).to have_link('Clone repository')
end
context 'the wiki is not a project wiki' do
it 'does not include the clone repository link' do
allow(wiki).to receive(:container).and_return(create(:group))
render
expect(rendered).not_to have_link('Clone repository')
end
end
context 'the sidebar failed to load' do
before do
assign(:sidebar_error, Object.new)
end
it 'reports this to the user' do
render
expect(rendered).to include('The sidebar failed to load')
expect(rendered).to have_css('.gl-alert.gl-alert-info')
end
end
context 'The sidebar comes from a custom page' do
before do
assign(:sidebar_page, double('WikiPage', path: 'sidebar.md', slug: 'sidebar', content: 'Some sidebar content'))
end
it 'does not show an alert' do
render
expect(rendered).not_to include('The sidebar failed to load')
expect(rendered).not_to have_css('.gl-alert.gl-alert-info')
end
it 'renders the wiki content' do
render
expect(rendered).to include('Some sidebar content')
end
end
context 'The sidebar comes a list of wiki pages' do
before do
assign(:sidebar_wiki_entries, create_list(:wiki_page, 3, wiki: wiki))
assign(:sidebar_limited, true)
stub_template "../shared/wikis/_wiki_pages.html.erb" => "Entries: <%= @sidebar_wiki_entries.size %>"
stub_template "../shared/wikis/_wiki_page.html.erb" => 'A WIKI PAGE'
end
it 'does not show an alert' do
render
expect(rendered).not_to include('The sidebar failed to load')
expect(rendered).not_to have_css('.gl-alert.gl-alert-info')
end
it 'renders the wiki content' do
render
expect(rendered).to include('A WIKI PAGE' * 3)
expect(rendered).to have_link('View All Pages')
end
context 'there is no more to see' do
it 'does not invite the user to view more' do
assign(:sidebar_limited, false)
render
expect(rendered).not_to have_link('View All Pages')
end
end
end
end