diff options
Diffstat (limited to 'app/services/alert_management/alerts/update_service.rb')
-rw-r--r-- | app/services/alert_management/alerts/update_service.rb | 123 |
1 files changed, 103 insertions, 20 deletions
diff --git a/app/services/alert_management/alerts/update_service.rb b/app/services/alert_management/alerts/update_service.rb index ffabbb37289..0b7216cd9f8 100644 --- a/app/services/alert_management/alerts/update_service.rb +++ b/app/services/alert_management/alerts/update_service.rb @@ -12,17 +12,20 @@ module AlertManagement @alert = alert @current_user = current_user @params = params + @param_errors = [] end def execute return error_no_permissions unless allowed? - return error_no_updates if params.empty? - filter_assignees + filter_params + return error_invalid_params if param_errors.any? + + # Save old assignees for system notes old_assignees = alert.assignees.to_a if alert.update(params) - process_assignement(old_assignees) + handle_changes(old_assignees: old_assignees) success else @@ -32,16 +35,13 @@ module AlertManagement private - attr_reader :alert, :current_user, :params + attr_reader :alert, :current_user, :params, :param_errors + delegate :resolved?, to: :alert def allowed? current_user&.can?(:update_alert_management_alert, alert) end - def assignee_todo_allowed? - assignee&.can?(:read_alert_management_alert, alert) - end - def todo_service strong_memoize(:todo_service) do TodoService.new @@ -60,39 +60,122 @@ module AlertManagement error(_('You have no permissions')) end - def error_no_updates - error(_('Please provide attributes to update')) + def error_invalid_params + error(param_errors.to_sentence) + end + + def add_param_error(message) + param_errors << message + end + + def filter_params + param_errors << _('Please provide attributes to update') if params.empty? + + filter_status + filter_assignees + filter_duplicate + end + + def handle_changes(old_assignees:) + handle_assignement(old_assignees) if params[:assignees] + handle_status_change if params[:status_event] end # ----- Assignee-related behavior ------ def filter_assignees return if params[:assignees].nil? - params[:assignees] = Array(assignee) + # Always take first assignee while multiple are not currently supported + params[:assignees] = Array(params[:assignees].first) + + param_errors << _('Assignee has no permissions') if unauthorized_assignees? end - def assignee - strong_memoize(:assignee) do - # Take first assignee while multiple are not currently supported - params[:assignees]&.first - end + def unauthorized_assignees? + params[:assignees]&.any? { |user| !user.can?(:read_alert_management_alert, alert) } end - def process_assignement(old_assignees) + def handle_assignement(old_assignees) assign_todo add_assignee_system_note(old_assignees) end def assign_todo - # Remove check in follow-up issue https://gitlab.com/gitlab-org/gitlab/-/issues/222672 - return unless assignee_todo_allowed? - todo_service.assign_alert(alert, current_user) end def add_assignee_system_note(old_assignees) SystemNoteService.change_issuable_assignees(alert, alert.project, current_user, old_assignees) end + + # ------ Status-related behavior ------- + def filter_status + return unless params[:status] + + status_event = AlertManagement::Alert::STATUS_EVENTS[status_key] + + unless status_event + param_errors << _('Invalid status') + return + end + + params[:status_event] = status_event + end + + def status_key + strong_memoize(:status_key) do + status = params.delete(:status) + AlertManagement::Alert::STATUSES.key(status) + end + end + + def handle_status_change + add_status_change_system_note + resolve_todos if resolved? + end + + def add_status_change_system_note + SystemNoteService.change_alert_status(alert, current_user) + end + + def resolve_todos + todo_service.resolve_todos_for_target(alert, current_user) + end + + def filter_duplicate + # Only need to check if changing to an open status + return unless params[:status_event] && AlertManagement::Alert::OPEN_STATUSES.include?(status_key) + + param_errors << unresolved_alert_error if duplicate_alert? + end + + def duplicate_alert? + return if alert.fingerprint.blank? + + open_alerts.any? && open_alerts.exclude?(alert) + end + + def open_alerts + strong_memoize(:open_alerts) do + AlertManagement::Alert.for_fingerprint(alert.project, alert.fingerprint).open + end + end + + def unresolved_alert_error + _('An %{link_start}alert%{link_end} with the same fingerprint is already open. ' \ + 'To change the status of this alert, resolve the linked alert.' + ) % open_alert_url_params + end + + def open_alert_url_params + open_alert = open_alerts.first + alert_path = Gitlab::Routing.url_helpers.details_project_alert_management_path(alert.project, open_alert) + + { + link_start: '<a href="%{url}">'.html_safe % { url: alert_path }, + link_end: '</a>'.html_safe + } + end end end end |