diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-14 15:09:05 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-14 15:09:05 +0000 |
commit | 66bd1f0fdcaf84fa3412c70d7962b49eb8a48fde (patch) | |
tree | 23f451b4e60a6e28bcc15043d7756bb27dcc2970 /app | |
parent | 49089d4fb1f5c17328ac61c955d95a68c6d4d545 (diff) | |
download | gitlab-ce-66bd1f0fdcaf84fa3412c70d7962b49eb8a48fde.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
30 files changed, 415 insertions, 19 deletions
diff --git a/app/assets/javascripts/reports/codequality_report/store/actions.js b/app/assets/javascripts/reports/codequality_report/store/actions.js new file mode 100644 index 00000000000..bf84d27b5ea --- /dev/null +++ b/app/assets/javascripts/reports/codequality_report/store/actions.js @@ -0,0 +1,30 @@ +import axios from '~/lib/utils/axios_utils'; +import * as types from './mutation_types'; +import { parseCodeclimateMetrics, doCodeClimateComparison } from './utils/codequality_comparison'; + +export const setPaths = ({ commit }, paths) => commit(types.SET_PATHS, paths); + +export const fetchReports = ({ state, dispatch, commit }) => { + commit(types.REQUEST_REPORTS); + + if (!state.basePath) { + return dispatch('receiveReportsError'); + } + return Promise.all([axios.get(state.headPath), axios.get(state.basePath)]) + .then(results => + doCodeClimateComparison( + parseCodeclimateMetrics(results[0].data, state.headBlobPath), + parseCodeclimateMetrics(results[1].data, state.baseBlobPath), + ), + ) + .then(data => dispatch('receiveReportsSuccess', data)) + .catch(() => dispatch('receiveReportsError')); +}; + +export const receiveReportsSuccess = ({ commit }, data) => { + commit(types.RECEIVE_REPORTS_SUCCESS, data); +}; + +export const receiveReportsError = ({ commit }) => { + commit(types.RECEIVE_REPORTS_ERROR); +}; diff --git a/app/assets/javascripts/reports/codequality_report/store/getters.js b/app/assets/javascripts/reports/codequality_report/store/getters.js new file mode 100644 index 00000000000..5df58c7f85f --- /dev/null +++ b/app/assets/javascripts/reports/codequality_report/store/getters.js @@ -0,0 +1,58 @@ +import { LOADING, ERROR, SUCCESS } from '../../constants'; +import { sprintf, __, s__, n__ } from '~/locale'; + +export const hasCodequalityIssues = state => + Boolean(state.newIssues?.length || state.resolvedIssues?.length); + +export const codequalityStatus = state => { + if (state.isLoading) { + return LOADING; + } + if (state.hasError) { + return ERROR; + } + + return SUCCESS; +}; + +export const codequalityText = state => { + const { newIssues, resolvedIssues } = state; + const text = []; + + if (!newIssues.length && !resolvedIssues.length) { + text.push(s__('ciReport|No changes to code quality')); + } else { + text.push(s__('ciReport|Code quality')); + + if (resolvedIssues.length) { + text.push(n__(' improved on %d point', ' improved on %d points', resolvedIssues.length)); + } + + if (newIssues.length && resolvedIssues.length) { + text.push(__(' and')); + } + + if (newIssues.length) { + text.push(n__(' degraded on %d point', ' degraded on %d points', newIssues.length)); + } + } + + return text.join(''); +}; + +export const codequalityPopover = state => { + if (state.headPath && !state.basePath) { + return { + title: s__('ciReport|Base pipeline codequality artifact not found'), + content: sprintf( + s__('ciReport|%{linkStartTag}Learn more about codequality reports %{linkEndTag}'), + { + linkStartTag: `<a href="${state.helpPath}" target="_blank" rel="noopener noreferrer">`, + linkEndTag: '<i class="fa fa-external-link" aria-hidden="true"></i></a>', + }, + false, + ), + }; + } + return {}; +}; diff --git a/app/assets/javascripts/reports/codequality_report/store/index.js b/app/assets/javascripts/reports/codequality_report/store/index.js new file mode 100644 index 00000000000..047964260ad --- /dev/null +++ b/app/assets/javascripts/reports/codequality_report/store/index.js @@ -0,0 +1,16 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import * as actions from './actions'; +import * as getters from './getters'; +import mutations from './mutations'; +import state from './state'; + +Vue.use(Vuex); + +export default initialState => + new Vuex.Store({ + actions, + getters, + mutations, + state: state(initialState), + }); diff --git a/app/assets/javascripts/reports/codequality_report/store/mutation_types.js b/app/assets/javascripts/reports/codequality_report/store/mutation_types.js new file mode 100644 index 00000000000..c362c973ae1 --- /dev/null +++ b/app/assets/javascripts/reports/codequality_report/store/mutation_types.js @@ -0,0 +1,5 @@ +export const SET_PATHS = 'SET_PATHS'; + +export const REQUEST_REPORTS = 'REQUEST_REPORTS'; +export const RECEIVE_REPORTS_SUCCESS = 'RECEIVE_REPORTS_SUCCESS'; +export const RECEIVE_REPORTS_ERROR = 'RECEIVE_REPORTS_ERROR'; diff --git a/app/assets/javascripts/reports/codequality_report/store/mutations.js b/app/assets/javascripts/reports/codequality_report/store/mutations.js new file mode 100644 index 00000000000..7ef4f3ce2db --- /dev/null +++ b/app/assets/javascripts/reports/codequality_report/store/mutations.js @@ -0,0 +1,24 @@ +import * as types from './mutation_types'; + +export default { + [types.SET_PATHS](state, paths) { + state.basePath = paths.basePath; + state.headPath = paths.headPath; + state.baseBlobPath = paths.baseBlobPath; + state.headBlobPath = paths.headBlobPath; + state.helpPath = paths.helpPath; + }, + [types.REQUEST_REPORTS](state) { + state.isLoading = true; + }, + [types.RECEIVE_REPORTS_SUCCESS](state, data) { + state.hasError = false; + state.isLoading = false; + state.newIssues = data.newIssues; + state.resolvedIssues = data.resolvedIssues; + }, + [types.RECEIVE_REPORTS_ERROR](state) { + state.isLoading = false; + state.hasError = true; + }, +}; diff --git a/app/assets/javascripts/reports/codequality_report/store/state.js b/app/assets/javascripts/reports/codequality_report/store/state.js new file mode 100644 index 00000000000..38ab53b432e --- /dev/null +++ b/app/assets/javascripts/reports/codequality_report/store/state.js @@ -0,0 +1,15 @@ +export default () => ({ + basePath: null, + headPath: null, + + baseBlobPath: null, + headBlobPath: null, + + isLoading: false, + hasError: false, + + newIssues: [], + resolvedIssues: [], + + helpPath: null, +}); diff --git a/app/assets/javascripts/reports/codequality_report/store/utils/codequality_comparison.js b/app/assets/javascripts/reports/codequality_report/store/utils/codequality_comparison.js new file mode 100644 index 00000000000..eba9e340c4e --- /dev/null +++ b/app/assets/javascripts/reports/codequality_report/store/utils/codequality_comparison.js @@ -0,0 +1,41 @@ +import CodeQualityComparisonWorker from '../../workers/codequality_comparison_worker'; + +export const parseCodeclimateMetrics = (issues = [], path = '') => { + return issues.map(issue => { + const parsedIssue = { + ...issue, + name: issue.description, + }; + + if (issue?.location?.path) { + let parseCodeQualityUrl = `${path}/${issue.location.path}`; + parsedIssue.path = issue.location.path; + + if (issue?.location?.lines?.begin) { + parsedIssue.line = issue.location.lines.begin; + parseCodeQualityUrl += `#L${issue.location.lines.begin}`; + } else if (issue?.location?.positions?.begin?.line) { + parsedIssue.line = issue.location.positions.begin.line; + parseCodeQualityUrl += `#L${issue.location.positions.begin.line}`; + } + + parsedIssue.urlPath = parseCodeQualityUrl; + } + + return parsedIssue; + }); +}; + +export const doCodeClimateComparison = (headIssues, baseIssues) => { + // Do these comparisons in worker threads to avoid blocking the main thread + return new Promise((resolve, reject) => { + const worker = new CodeQualityComparisonWorker(); + worker.addEventListener('message', ({ data }) => + data.newIssues && data.resolvedIssues ? resolve(data) : reject(data), + ); + worker.postMessage({ + headIssues, + baseIssues, + }); + }); +}; diff --git a/app/assets/javascripts/reports/codequality_report/workers/codequality_comparison_worker.js b/app/assets/javascripts/reports/codequality_report/workers/codequality_comparison_worker.js new file mode 100644 index 00000000000..fc55602f95c --- /dev/null +++ b/app/assets/javascripts/reports/codequality_report/workers/codequality_comparison_worker.js @@ -0,0 +1,28 @@ +import { differenceBy } from 'lodash'; + +const KEY_TO_FILTER_BY = 'fingerprint'; + +// eslint-disable-next-line no-restricted-globals +self.addEventListener('message', e => { + const { data } = e; + + if (data === undefined) { + return null; + } + + const { headIssues, baseIssues } = data; + + if (!headIssues || !baseIssues) { + // eslint-disable-next-line no-restricted-globals + return self.postMessage({}); + } + + // eslint-disable-next-line no-restricted-globals + self.postMessage({ + newIssues: differenceBy(headIssues, baseIssues, KEY_TO_FILTER_BY), + resolvedIssues: differenceBy(baseIssues, headIssues, KEY_TO_FILTER_BY), + }); + + // eslint-disable-next-line no-restricted-globals + return self.close(); +}); diff --git a/app/finders/events_finder.rb b/app/finders/events_finder.rb index 004fbc4cd22..4c619f3d7ea 100644 --- a/app/finders/events_finder.rb +++ b/app/finders/events_finder.rb @@ -54,17 +54,10 @@ class EventsFinder if current_user && scope == 'all' EventCollection.new(current_user.authorized_projects).all_project_events else - # EventCollection is responsible for applying the feature flag - apply_feature_flags(source.events) + source.events end end - def apply_feature_flags(events) - return events if ::Feature.enabled?(:wiki_events) - - events.not_wiki_page - end - # rubocop: disable CodeReuse/ActiveRecord def by_current_user_access(events) events.merge(Project.public_or_visible_to_user(current_user)) diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb index 8dbbec8c21d..90ebab731ea 100644 --- a/app/helpers/environments_helper.rb +++ b/app/helpers/environments_helper.rb @@ -36,7 +36,8 @@ module EnvironmentsHelper "environment-name": environment.name, "environments-path": project_environments_path(project, format: :json), "environment-id": environment.id, - "cluster-applications-documentation-path" => help_page_path('user/clusters/applications.md', anchor: 'elastic-stack') + "cluster-applications-documentation-path" => help_page_path('user/clusters/applications.md', anchor: 'elastic-stack'), + "clusters-path": project_clusters_path(project, format: :json) } end diff --git a/app/mailers/emails/service_desk.rb b/app/mailers/emails/service_desk.rb new file mode 100644 index 00000000000..29fe608472d --- /dev/null +++ b/app/mailers/emails/service_desk.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +module Emails + module ServiceDesk + extend ActiveSupport::Concern + include MarkupHelper + + included do + layout 'service_desk', only: [:service_desk_thank_you_email, :service_desk_new_note_email] + end + + def service_desk_thank_you_email(issue_id) + setup_service_desk_mail(issue_id) + + email_sender = sender( + @support_bot.id, + send_from_user_email: false, + sender_name: @project.service_desk_setting&.outgoing_name + ) + options = service_desk_options(email_sender, 'thank_you') + .merge(subject: "Re: #{subject_base}") + + mail_new_thread(@issue, options) + end + + def service_desk_new_note_email(issue_id, note_id) + @note = Note.find(note_id) + setup_service_desk_mail(issue_id) + + email_sender = sender(@note.author_id) + options = service_desk_options(email_sender, 'new_note') + .merge(subject: subject_base) + + mail_answer_thread(@issue, options) + end + + private + + def setup_service_desk_mail(issue_id) + @issue = Issue.find(issue_id) + @project = @issue.project + @support_bot = User.support_bot + + @sent_notification = SentNotification.record(@issue, @support_bot.id, reply_key) + end + + def service_desk_options(email_sender, email_type) + { + from: email_sender, + to: @issue.service_desk_reply_to + }.tap do |options| + next unless template_body = template_content(email_type) + + options[:body] = template_body + options[:content_type] = 'text/html' + end + end + + def template_content(email_type) + template = Gitlab::Template::ServiceDeskTemplate.find(email_type, @project) + + text = substitute_template_replacements(template.content) + + markdown(text, project: @project) + rescue Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError + nil + end + + def substitute_template_replacements(template_body) + template_body + .gsub(/%\{\s*ISSUE_ID\s*\}/, issue_id) + .gsub(/%\{\s*ISSUE_PATH\s*\}/, issue_path) + .gsub(/%\{\s*NOTE_TEXT\s*\}/, note_text) + end + + def issue_id + "#{Issue.reference_prefix}#{@issue.iid}" + end + + def issue_path + @issue.to_reference(full: true) + end + + def note_text + @note&.note.to_s + end + + def subject_base + "#{@issue.title} (##{@issue.iid})" + end + end +end diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 2cf72d40635..f9aba3fe4f2 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -19,6 +19,7 @@ class Notify < ApplicationMailer include Emails::Releases include Emails::Groups include Emails::Reviews + include Emails::ServiceDesk helper TimeboxesHelper helper MergeRequestsHelper diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb index cb7c6a36c27..f3a4076e69c 100644 --- a/app/mailers/previews/notify_preview.rb +++ b/app/mailers/previews/notify_preview.rb @@ -165,6 +165,18 @@ class NotifyPreview < ActionMailer::Preview Notify.unknown_sign_in_email(user, '127.0.0.1', Time.current).message end + def service_desk_new_note_email + cleanup do + note = create_note(noteable_type: 'Issue', noteable_id: issue.id, note: 'Issue note content') + + Notify.service_desk_new_note_email(issue.id, note.id).message + end + end + + def service_desk_thank_you_email + Notify.service_desk_thank_you_email(issue.id).message + end + private def project diff --git a/app/models/event.rb b/app/models/event.rb index 9c0fcbb354b..6cd091ca217 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -84,7 +84,6 @@ class Event < ApplicationRecord scope :for_design, -> { where(target_type: 'DesignManagement::Design') } # Needed to implement feature flag: can be removed when feature flag is removed - scope :not_wiki_page, -> { where('target_type IS NULL or target_type <> ?', 'WikiPage::Meta') } scope :not_design, -> { where('target_type IS NULL or target_type <> ?', 'DesignManagement::Design') } scope :with_associations, -> do diff --git a/app/models/event_collection.rb b/app/models/event_collection.rb index 6c268bf6c36..ce062abeaaf 100644 --- a/app/models/event_collection.rb +++ b/app/models/event_collection.rb @@ -45,7 +45,6 @@ class EventCollection private def apply_feature_flags(events) - events = events.not_wiki_page unless ::Feature.enabled?(:wiki_events) events = events.not_design unless ::Feature.enabled?(:design_activity_events) events diff --git a/app/presenters/clusters/cluster_presenter.rb b/app/presenters/clusters/cluster_presenter.rb index 85a62fefd8f..3dfa9626a79 100644 --- a/app/presenters/clusters/cluster_presenter.rb +++ b/app/presenters/clusters/cluster_presenter.rb @@ -2,6 +2,7 @@ module Clusters class ClusterPresenter < Gitlab::View::Presenter::Delegated + include ::Gitlab::Utils::StrongMemoize include ActionView::Helpers::SanitizeHelper include ActionView::Helpers::UrlHelper include IconsHelper @@ -60,6 +61,12 @@ module Clusters end end + def gitlab_managed_apps_logs_path + return unless logs_project && can_read_cluster? + + project_logs_path(logs_project, cluster_id: cluster.id) + end + def read_only_kubernetes_platform_fields? !cluster.provided_by_user? end @@ -85,6 +92,16 @@ module Clusters ActionController::Base.helpers.image_path(path) end + # currently log explorer is only available in the scope of the project + # for group and instance level cluster selected project does not affects + # fetching logs from gitlab managed apps namespace, therefore any project + # available to user will be sufficient. + def logs_project + strong_memoize(:logs_project) do + cluster.all_projects.first + end + end + def clusterable if cluster.group_type? cluster.group diff --git a/app/serializers/cluster_entity.rb b/app/serializers/cluster_entity.rb index 8a1d41dbd96..a46f2889a96 100644 --- a/app/serializers/cluster_entity.rb +++ b/app/serializers/cluster_entity.rb @@ -16,4 +16,8 @@ class ClusterEntity < Grape::Entity expose :path do |cluster| Clusters::ClusterPresenter.new(cluster).show_path # rubocop: disable CodeReuse/Presenter end + + expose :gitlab_managed_apps_logs_path do |cluster| + Clusters::ClusterPresenter.new(cluster, current_user: request.current_user).gitlab_managed_apps_logs_path # rubocop: disable CodeReuse/Presenter + end end diff --git a/app/serializers/cluster_serializer.rb b/app/serializers/cluster_serializer.rb index 27156d3178f..92363a4942c 100644 --- a/app/serializers/cluster_serializer.rb +++ b/app/serializers/cluster_serializer.rb @@ -10,6 +10,7 @@ class ClusterSerializer < BaseSerializer :cluster_type, :enabled, :environment_scope, + :gitlab_managed_apps_logs_path, :name, :nodes, :path, diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb index 7f71906bc89..5e184e41885 100644 --- a/app/services/event_create_service.rb +++ b/app/services/event_create_service.rb @@ -120,8 +120,6 @@ class EventCreateService # # @return a tuple of event and either :found or :created def wiki_event(wiki_page_meta, author, action) - return unless Feature.enabled?(:wiki_events) - raise IllegalActionError, action unless Event::WIKI_ACTIONS.include?(action) if duplicate = existing_wiki_event(wiki_page_meta, action) diff --git a/app/services/git/wiki_push_service.rb b/app/services/git/wiki_push_service.rb index 8bdbc28f3e8..b3937a10a70 100644 --- a/app/services/git/wiki_push_service.rb +++ b/app/services/git/wiki_push_service.rb @@ -23,7 +23,7 @@ module Git end def can_process_wiki_events? - Feature.enabled?(:wiki_events) && Feature.enabled?(:wiki_events_on_git_push, project) + Feature.enabled?(:wiki_events_on_git_push, project) end def push_changes diff --git a/app/services/wiki_pages/base_service.rb b/app/services/wiki_pages/base_service.rb index a0256ea5e69..2967684f7bc 100644 --- a/app/services/wiki_pages/base_service.rb +++ b/app/services/wiki_pages/base_service.rb @@ -44,8 +44,6 @@ module WikiPages end def create_wiki_event(page) - return unless ::Feature.enabled?(:wiki_events) - response = WikiPages::EventCreateService.new(current_user).execute(slug_for_page(page), page, event_action) log_error(response.message) if response.error? diff --git a/app/services/wiki_pages/event_create_service.rb b/app/services/wiki_pages/event_create_service.rb index 18a45d057a9..0453c90d693 100644 --- a/app/services/wiki_pages/event_create_service.rb +++ b/app/services/wiki_pages/event_create_service.rb @@ -10,8 +10,6 @@ module WikiPages end def execute(slug, page, action) - return ServiceResponse.success(message: 'No event created as `wiki_events` feature is disabled') unless ::Feature.enabled?(:wiki_events) - event = Event.transaction do wiki_page_meta = WikiPage::Meta.find_or_create(slug, page) diff --git a/app/views/layouts/service_desk.html.haml b/app/views/layouts/service_desk.html.haml new file mode 100644 index 00000000000..26d15a74403 --- /dev/null +++ b/app/views/layouts/service_desk.html.haml @@ -0,0 +1,24 @@ +%html{ lang: "en" } + %head + %meta{ content: "text/html; charset=utf-8", "http-equiv" => "Content-Type" } + -# haml-lint:disable NoPlainNodes + %title + GitLab + -# haml-lint:enable NoPlainNodes + = stylesheet_link_tag 'notify' + = yield :head + %body + .content + = yield + .footer{ style: "margin-top: 10px;" } + %p + — + %br + = link_to "Unsubscribe", @unsubscribe_url + + -# EE-specific start + - if Gitlab::CurrentSettings.email_additional_text.present? + %br + %br + = Gitlab::Utils.nlbr(Gitlab::CurrentSettings.email_additional_text) + -# EE-specific end diff --git a/app/views/notify/service_desk_new_note_email.html.haml b/app/views/notify/service_desk_new_note_email.html.haml new file mode 100644 index 00000000000..7c6be6688d0 --- /dev/null +++ b/app/views/notify/service_desk_new_note_email.html.haml @@ -0,0 +1,5 @@ +- if Gitlab::CurrentSettings.email_author_in_body + %div + #{link_to @note.author_name, user_url(@note.author)} wrote: +%div + = markdown(@note.note, pipeline: :email, author: @note.author) diff --git a/app/views/notify/service_desk_new_note_email.text.erb b/app/views/notify/service_desk_new_note_email.text.erb new file mode 100644 index 00000000000..208953a437d --- /dev/null +++ b/app/views/notify/service_desk_new_note_email.text.erb @@ -0,0 +1,6 @@ +New response for issue #<%= @issue.iid %>: + +Author: <%= sanitize_name(@note.author_name) %> + +<%= @note.note %> +<%# EE-specific start %><%= render_if_exists 'layouts/mailer/additional_text'%><%# EE-specific end %> diff --git a/app/views/notify/service_desk_thank_you_email.html.haml b/app/views/notify/service_desk_thank_you_email.html.haml new file mode 100644 index 00000000000..a3407acd9ba --- /dev/null +++ b/app/views/notify/service_desk_thank_you_email.html.haml @@ -0,0 +1,2 @@ +%p + Thank you for your support request! We are tracking your request as ticket ##{@issue.iid}, and will respond as soon as we can. diff --git a/app/views/notify/service_desk_thank_you_email.text.erb b/app/views/notify/service_desk_thank_you_email.text.erb new file mode 100644 index 00000000000..8281607a4a8 --- /dev/null +++ b/app/views/notify/service_desk_thank_you_email.text.erb @@ -0,0 +1,6 @@ +Thank you for your support request! We are tracking your request as ticket #<%= @issue.iid %>, and will respond as soon as we can. + +To unsubscribe from this issue, please paste the following link into your browser: + +<%= @unsubscribe_url %> +<%# EE-specific start %><%= render_if_exists 'layouts/mailer/additional_text' %><%# EE-specific end %> diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml index afa344e87f7..03534bf78d1 100644 --- a/app/views/shared/_event_filter.html.haml +++ b/app/views/shared/_event_filter.html.haml @@ -15,7 +15,7 @@ = render_if_exists 'events/epics_filter' - if comments_visible? = event_filter_link EventFilter::COMMENTS, _('Comments'), s_('EventFilterBy|Filter by comments') - - if Feature.enabled?(:wiki_events) && (@project.nil? || @project.has_wiki?) + - if @project.nil? || @project.has_wiki? = event_filter_link EventFilter::WIKI, _('Wiki'), s_('EventFilterBy|Filter by wiki') - if event_filter_visible(:designs) = event_filter_link EventFilter::DESIGNS, _('Designs'), s_('EventFilterBy|Filter by designs') diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 62c0bcf0093..4b308f0cb3b 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -1660,6 +1660,14 @@ :weight: 2 :idempotent: :tags: [] +- :name: service_desk_email_receiver + :feature_category: :issue_tracking + :has_external_dependencies: + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: + :tags: [] - :name: system_hook_push :feature_category: :source_code_management :has_external_dependencies: diff --git a/app/workers/service_desk_email_receiver_worker.rb b/app/workers/service_desk_email_receiver_worker.rb new file mode 100644 index 00000000000..8649034445c --- /dev/null +++ b/app/workers/service_desk_email_receiver_worker.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class ServiceDeskEmailReceiverWorker < EmailReceiverWorker # rubocop:disable Scalability/IdempotentWorker + include ApplicationWorker + + def perform(raw) + return unless ::Gitlab::ServiceDeskEmail.enabled? + + begin + Gitlab::Email::ServiceDeskReceiver.new(raw).execute + rescue => e + handle_failure(raw, e) + end + end +end |