Commit 372a263d authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 96db72ecb731
......@@ -20,11 +20,11 @@ class ApplicationController < ActionController::Base
before_action :authenticate_user!, except: [:route_not_found]
before_action :enforce_terms!, if: :should_enforce_terms?
before_action :validate_user_service_ticket!
before_action :check_password_expiration
before_action :check_password_expiration, if: :html_request?
before_action :ldap_security_check
before_action :sentry_context
before_action :default_headers
before_action :add_gon_variables, unless: [:peek_request?, :json_request?]
before_action :add_gon_variables, if: :html_request?
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :require_email, unless: :devise_controller?
before_action :active_user_check, unless: :devise_controller?
......@@ -455,8 +455,8 @@ def set_page_title_header
response.headers['Page-Title'] = URI.escape(page_title('GitLab'))
end
def peek_request?
request.path.start_with?('/-/peek')
def html_request?
request.format.html?
end
def json_request?
......@@ -466,7 +466,7 @@ def json_request?
def should_enforce_terms?
return false unless Gitlab::CurrentSettings.current_application_settings.enforce_terms
!(peek_request? || devise_controller?)
html_request? && !devise_controller?
end
def set_usage_stats_consent_flag
......
......@@ -4,15 +4,18 @@ module ConfirmEmailWarning
extend ActiveSupport::Concern
included do
before_action :set_confirm_warning, if: -> { Feature.enabled?(:soft_email_confirmation) }
before_action :set_confirm_warning, if: :show_confirm_warning?
end
protected
def show_confirm_warning?
html_request? && request.get? && Feature.enabled?(:soft_email_confirmation)
end
def set_confirm_warning
return unless current_user
return if current_user.confirmed?
return if peek_request? || json_request? || !request.get?
email = current_user.unconfirmed_email || current_user.email
......
# frozen_string_literal: true
module UploadsActions
extend ActiveSupport::Concern
include Gitlab::Utils::StrongMemoize
include SendFileUpload
UPLOAD_MOUNTS = %w(avatar attachment file logo header_logo favicon).freeze
included do
prepend_before_action :set_request_format_from_path_extension
end
def create
uploader = UploadService.new(model, params[:file], uploader_class).execute
......@@ -64,6 +69,18 @@ def authorize
private
# From ActionDispatch::Http::MimeNegotiation. We have an initializer that
# monkey-patches this method out (so that repository paths don't guess a
# format based on extension), but we do want this behaviour when serving
# uploads.
def set_request_format_from_path_extension
path = request.headers['action_dispatch.original_path'] || request.headers['PATH_INFO']
if match = path&.match(/\.(\w+)\z/)
request.format = match.captures.first
end
end
def uploader_class
raise NotImplementedError
end
......
......@@ -133,7 +133,7 @@ def metrics_redirect
if environment
redirect_to environment_metrics_path(environment)
else
render :empty
render :empty_metrics
end
end
......
......@@ -15,6 +15,23 @@ def index
end
end
def details
respond_to do |format|
format.html
format.json do
render_issue_detail_json
end
end
end
def stack_trace
respond_to do |format|
format.json do
render_issue_stack_trace_json
end
end
end
def list_projects
respond_to do |format|
format.json do
......@@ -29,10 +46,7 @@ def render_index_json
service = ErrorTracking::ListIssuesService.new(project, current_user)
result = service.execute
unless result[:status] == :success
return render json: { message: result[:message] },
status: result[:http_status] || :bad_request
end
return if handle_errors(result)
render json: {
errors: serialize_errors(result[:issues]),
......@@ -40,6 +54,28 @@ def render_index_json
}
end
def render_issue_detail_json
service = ErrorTracking::IssueDetailsService.new(project, current_user, issue_details_params)
result = service.execute
return if handle_errors(result)
render json: {
error: serialize_detailed_error(result[:issue])
}
end
def render_issue_stack_trace_json
service = ErrorTracking::IssueLatestEventService.new(project, current_user, issue_details_params)
result = service.execute
return if handle_errors(result)
render json: {
error: serialize_error_event(result[:latest_event])
}
end
def render_project_list_json
service = ErrorTracking::ListProjectsService.new(
project,
......@@ -62,10 +98,21 @@ def render_project_list_json
end
end
def handle_errors(result)
unless result[:status] == :success
render json: { message: result[:message] },
status: result[:http_status] || :bad_request
end
end
def list_projects_params
params.require(:error_tracking_setting).permit([:api_host, :token])
end
def issue_details_params
params.permit(:issue_id)
end
def set_polling_interval
Gitlab::PollingInterval.set_header(response, interval: POLLING_INTERVAL)
end
......@@ -76,6 +123,18 @@ def serialize_errors(errors)
.represent(errors)
end
def serialize_detailed_error(error)
ErrorTracking::DetailedErrorSerializer
.new(project: project, user: current_user)
.represent(error)
end
def serialize_error_event(event)
ErrorTracking::ErrorEventSerializer
.new(project: project, user: current_user)
.represent(event)
end
def serialize_projects(projects)
ErrorTracking::ProjectSerializer
.new(project: project, user: current_user)
......
......@@ -20,7 +20,7 @@ class UploadsController < ApplicationController
skip_before_action :authenticate_user!
before_action :upload_mount_satisfied?
before_action :find_model
before_action :model
before_action :authorize_access!, only: [:show]
before_action :authorize_create_access!, only: [:create, :authorize]
before_action :verify_workhorse_api!, only: [:authorize]
......
......@@ -293,7 +293,7 @@ def visible_attributes
:snowplow_collector_hostname,
:snowplow_cookie_domain,
:snowplow_enabled,
:snowplow_site_id,
:snowplow_app_id,
:snowplow_iglu_registry_url,
:push_event_hooks_limit,
:push_event_activities_limit,
......
......@@ -13,4 +13,13 @@ def error_tracking_data(current_user, project)
'illustration-path' => image_path('illustrations/cluster_popover.svg')
}
end
def error_details_data(project, issue)
opts = [project, issue, { format: :json }]
{
'issue-details-path' => details_namespace_project_error_tracking_index_path(*opts),
'issue-stack-trace-path' => stack_trace_namespace_project_error_tracking_index_path(*opts)
}
end
end
......@@ -132,11 +132,12 @@ def defaults
snowplow_collector_hostname: nil,
snowplow_cookie_domain: nil,
snowplow_enabled: false,
snowplow_site_id: nil,
snowplow_app_id: nil,
snowplow_iglu_registry_url: nil,
custom_http_clone_url_root: nil,
pendo_enabled: false,
pendo_url: nil
pendo_url: nil,
productivity_analytics_start_date: Time.now
}
end
......
......@@ -87,10 +87,30 @@ def list_sentry_projects
{ projects: sentry_client.list_projects }
end
def issue_details(opts = {})
with_reactive_cache('issue_details', opts.stringify_keys) do |result|
result
end
end
def issue_latest_event(opts = {})
with_reactive_cache('issue_latest_event', opts.stringify_keys) do |result|
result
end
end
def calculate_reactive_cache(request, opts)
case request
when 'list_issues'
{ issues: sentry_client.list_issues(**opts.symbolize_keys) }
when 'issue_details'
{
issue: sentry_client.issue_details(**opts.symbolize_keys)
}
when 'issue_latest_event'
{
latest_event: sentry_client.issue_latest_event(**opts.symbolize_keys)
}
end
rescue Sentry::Client::Error => e
{ error: e.message, error_type: SENTRY_API_ERROR_TYPE_NON_20X_RESPONSE }
......
# frozen_string_literal: true
module ErrorTracking
class DetailedErrorEntity < Grape::Entity
expose :count,
:culprit,
:external_base_url,
:external_url,
:first_release_last_commit,
:first_release_short_version,
:first_seen,
:frequency,
:id,
:last_release_last_commit,
:last_release_short_version,
:last_seen,
:message,
:project_id,
:project_name,
:project_slug,
:short_id,
:status,
:title,
:type,
:user_count
end
end
# frozen_string_literal: true
module ErrorTracking
class DetailedErrorSerializer < BaseSerializer
entity DetailedErrorEntity
end
end
# frozen_string_literal: true
module ErrorTracking
class ErrorEventEntity < Grape::Entity
expose :issue_id, :date_received, :stack_trace_entries
end
end
# frozen_string_literal: true
module ErrorTracking
class ErrorEventSerializer < BaseSerializer
entity ErrorEventEntity
end
end
# frozen_string_literal: true
module ErrorTracking
class BaseService < ::BaseService
def execute
unauthorized = check_permissions
return unauthorized if unauthorized
begin
response = fetch
rescue Sentry::Client::Error => e
return error(e.message, :bad_request)
rescue Sentry::Client::MissingKeysError => e
return error(e.message, :internal_server_error)
end
errors = parse_errors(response)
return errors if errors
success(parse_response(response))
end
private
def fetch
raise NotImplementedError,
"#{self.class} does not implement #{__method__}"
end
def parse_response(response)
raise NotImplementedError,
"#{self.class} does not implement #{__method__}"
end
def check_permissions
return error('Error Tracking is not enabled') unless enabled?
return error('Access denied', :unauthorized) unless can_read?
end
def parse_errors(response)
return error('Not ready. Try again later', :no_content) unless response
return error(response[:error], http_status_for(response[:error_type])) if response[:error].present?
end
def http_status_for(error_type)
case error_type
when ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_TYPE_MISSING_KEYS
:internal_server_error
else
:bad_request
end
end
def project_error_tracking_setting
project.error_tracking_setting
end
def enabled?
project_error_tracking_setting&.enabled?
end
def can_read?
can?(current_user, :read_sentry_issue, project)
end
end
end
# frozen_string_literal: true
module ErrorTracking
class IssueDetailsService < ErrorTracking::BaseService
private
def fetch
project_error_tracking_setting.issue_details(issue_id: params[:issue_id])
end
def parse_response(response)
{ issue: response[:issue] }
end
end
end
# frozen_string_literal: true
module ErrorTracking
class IssueLatestEventService < ErrorTracking::BaseService
private
def fetch
project_error_tracking_setting.issue_latest_event(issue_id: params[:issue_id])
end
def parse_response(response)
{ latest_event: response[:latest_event] }
end
end
end
# frozen_string_literal: true
module ErrorTracking
class ListIssuesService < ::BaseService
class ListIssuesService < ErrorTracking::BaseService
DEFAULT_ISSUE_STATUS = 'unresolved'
DEFAULT_LIMIT = 20
def execute
return error('Error Tracking is not enabled') unless enabled?
return error('Access denied', :unauthorized) unless can_read?
result = project_error_tracking_setting
.list_sentry_issues(issue_status: issue_status, limit: limit)
# our results are not yet ready
unless result
return error('Not ready. Try again later', :no_content)
end
private
if result[:error].present?
return error(result[:error], http_status_from_error_type(result[:error_type]))
end
def fetch
project_error_tracking_setting.list_sentry_issues(issue_status: issue_status, limit: limit)
end
success(issues: result[:issues])
def parse_response(response)
{ issues: response[:issues] }
end
def external_url
project_error_tracking_setting&.sentry_external_url
end
private
def http_status_from_error_type(error_type)
case error_type
when ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_TYPE_MISSING_KEYS
:internal_server_error
else
:bad_request
end
end
def project_error_tracking_setting
project.error_tracking_setting
end
def issue_status
params[:issue_status] || DEFAULT_ISSUE_STATUS
end
......@@ -50,13 +26,5 @@ def issue_status
def limit
params[:limit] || DEFAULT_LIMIT
end
def enabled?
project_error_tracking_setting&.enabled?
end
def can_read?
can?(current_user, :read_sentry_issue, project)
end
end
end
# frozen_string_literal: true
module ErrorTracking
class ListProjectsService < ::BaseService
class ListProjectsService < ErrorTracking::BaseService
def execute
return error('access denied') unless can_read?
setting = project_error_tracking_setting
unless setting.valid?
return error(setting.errors.full_messages.join(', '), :bad_request)
unless project_error_tracking_setting.valid?
return error(project_error_tracking_setting.errors.full_messages.join(', '), :bad_request)
end
begin
result = setting.list_sentry_projects
rescue Sentry::Client::Error => e
return error(e.message, :bad_request)
rescue Sentry::Client::MissingKeysError => e
return error(e.message, :internal_server_error)
end
success(projects: result[:projects])
super
end
private
def project_error_tracking_setting
(project.error_tracking_setting || project.build_error_tracking_setting).tap do |setting|
setting.api_url = ErrorTracking::ProjectErrorTrackingSetting.build_api_url_from(
api_host: params[:api_host],
organization_slug: 'org',
project_slug: 'proj'
)
setting.token = token(setting)
setting.enabled = true
end
def fetch
project_error_tracking_setting.list_sentry_projects
end
def parse_response(response)
{ projects: response[:projects] }
end
def can_read?
can?(current_user, :read_sentry_issue, project)
def project_error_tracking_setting
@project_error_tracking_setting ||= begin
(super || project.build_error_tracking_setting).tap do |setting|
setting.api_url = ErrorTracking::ProjectErrorTrackingSetting.build_api_url_from(
api_host: params[:api_host],
organization_slug: 'org',
project_slug: 'proj'
)
setting.token = token(setting)
setting.enabled = true
end
end
end
def token(setting)
......
......@@ -21,8 +21,8 @@
= f.label :snowplow_collector_hostname, _('Collector hostname'), class: 'label-light'
= f.text_field :snowplow_collector_hostname, class: 'form-control', placeholder: 'snowplow.example.com'
.form-group
= f.label :snowplow_site_id, _('Site ID'), class: 'label-light'
= f.text_field :snowplow_site_id, class: 'form-control'
= f.label :snowplow_app_id, _('App ID'), class: 'label-light'
= f.text_field :snowplow_app_id, class: 'form-control'
.form-group
= f.label :snowplow_cookie_domain, _('Cookie domain'), class: 'label-light'
= f.text_field :snowplow_cookie_domain, class: 'form-control'
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment