diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/projects/error_tracking_controller.rb | 54 | ||||
-rw-r--r-- | app/models/error_tracking/project_error_tracking_setting.rb | 45 | ||||
-rw-r--r-- | app/policies/project_policy.rb | 1 | ||||
-rw-r--r-- | app/serializers/error_tracking/error_entity.rb | 10 | ||||
-rw-r--r-- | app/serializers/error_tracking/error_serializer.rb | 7 | ||||
-rw-r--r-- | app/services/error_tracking/list_issues_service.rb | 49 | ||||
-rw-r--r-- | app/views/projects/error_tracking/index.html.haml | 1 |
7 files changed, 167 insertions, 0 deletions
diff --git a/app/controllers/projects/error_tracking_controller.rb b/app/controllers/projects/error_tracking_controller.rb new file mode 100644 index 00000000000..4596b6c91f2 --- /dev/null +++ b/app/controllers/projects/error_tracking_controller.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +class Projects::ErrorTrackingController < Projects::ApplicationController + before_action :check_feature_flag! + before_action :authorize_read_sentry_issue! + before_action :push_feature_flag_to_frontend + + POLLING_INTERVAL = 10_000 + + def index + respond_to do |format| + format.html + format.json do + set_polling_interval + render_index_json + end + end + end + + private + + 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 + + render json: { + errors: serialize_errors(result[:issues]), + external_url: service.external_url + } + end + + def set_polling_interval + Gitlab::PollingInterval.set_header(response, interval: POLLING_INTERVAL) + end + + def serialize_errors(errors) + ErrorTracking::ErrorSerializer + .new(project: project, user: current_user) + .represent(errors) + end + + def check_feature_flag! + render_404 unless Feature.enabled?(:error_tracking, project) + end + + def push_feature_flag_to_frontend + push_frontend_feature_flag(:error_tracking, current_user) + end +end diff --git a/app/models/error_tracking/project_error_tracking_setting.rb b/app/models/error_tracking/project_error_tracking_setting.rb index 632c64c2f1c..7f4947ba27a 100644 --- a/app/models/error_tracking/project_error_tracking_setting.rb +++ b/app/models/error_tracking/project_error_tracking_setting.rb @@ -2,13 +2,58 @@ module ErrorTracking class ProjectErrorTrackingSetting < ActiveRecord::Base + include ReactiveCaching + + self.reactive_cache_key = ->(setting) { [setting.class.model_name.singular, setting.project_id] } + belongs_to :project validates :api_url, length: { maximum: 255 }, public_url: true, url: { enforce_sanitization: true } + validate :validate_api_url_path + attr_encrypted :token, mode: :per_attribute_iv, key: Settings.attr_encrypted_db_key_base_truncated, algorithm: 'aes-256-gcm' + + after_save :clear_reactive_cache! + + def sentry_client + Sentry::Client.new(api_url, token) + end + + def sentry_external_url + self.class.extract_sentry_external_url(api_url) + end + + def list_sentry_issues(opts = {}) + with_reactive_cache('list_issues', opts.stringify_keys) do |result| + { issues: result } + end + end + + def calculate_reactive_cache(request, opts) + case request + when 'list_issues' + sentry_client.list_issues(**opts.symbolize_keys) + end + end + + # http://HOST/api/0/projects/ORG/PROJECT + # -> + # http://HOST/ORG/PROJECT + def self.extract_sentry_external_url(url) + url.sub('api/0/projects/', '') + end + + private + + def validate_api_url_path + unless URI(api_url).path.starts_with?('/api/0/projects') + errors.add(:api_url, 'path needs to start with /api/0/projects') + end + rescue URI::InvalidURIError + end end end diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index d70417e710e..12f9f29dcc1 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -200,6 +200,7 @@ class ProjectPolicy < BasePolicy enable :read_environment enable :read_deployment enable :read_merge_request + enable :read_sentry_issue end # We define `:public_user_access` separately because there are cases in gitlab-ee diff --git a/app/serializers/error_tracking/error_entity.rb b/app/serializers/error_tracking/error_entity.rb new file mode 100644 index 00000000000..91388e7c3ad --- /dev/null +++ b/app/serializers/error_tracking/error_entity.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module ErrorTracking + class ErrorEntity < Grape::Entity + expose :id, :title, :type, :user_count, :count, + :first_seen, :last_seen, :message, :culprit, + :external_url, :project_id, :project_name, :project_slug, + :short_id, :status, :frequency + end +end diff --git a/app/serializers/error_tracking/error_serializer.rb b/app/serializers/error_tracking/error_serializer.rb new file mode 100644 index 00000000000..ff9a645eb16 --- /dev/null +++ b/app/serializers/error_tracking/error_serializer.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module ErrorTracking + class ErrorSerializer < BaseSerializer + entity ErrorEntity + end +end diff --git a/app/services/error_tracking/list_issues_service.rb b/app/services/error_tracking/list_issues_service.rb new file mode 100644 index 00000000000..4cc35cfa4a8 --- /dev/null +++ b/app/services/error_tracking/list_issues_service.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module ErrorTracking + class ListIssuesService < ::BaseService + DEFAULT_ISSUE_STATUS = 'unresolved' + DEFAULT_LIMIT = 20 + + def execute + return error('not enabled') unless enabled? + return error('access denied') 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', :no_content) + end + + success(issues: result[:issues]) + end + + def external_url + project_error_tracking_setting&.sentry_external_url + end + + private + + def project_error_tracking_setting + project.error_tracking_setting + end + + def issue_status + params[:issue_status] || DEFAULT_ISSUE_STATUS + end + + 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 diff --git a/app/views/projects/error_tracking/index.html.haml b/app/views/projects/error_tracking/index.html.haml new file mode 100644 index 00000000000..a3e0dc75f6f --- /dev/null +++ b/app/views/projects/error_tracking/index.html.haml @@ -0,0 +1 @@ +- page_title _('Errors') |