summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-07-13 18:09:16 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-13 18:09:16 +0000
commitd9aac35d79489591396456aceefa609de4b8b05c (patch)
tree3ef25ee74f8ba1a72771a519c912d5e239ff2bb8 /app
parentff430539d5299de3a066cb8397b302626761a745 (diff)
downloadgitlab-ce-d9aac35d79489591396456aceefa609de4b8b05c.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/alert_management/components/alert_management_table.vue24
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue24
-rw-r--r--app/assets/javascripts/boards/components/board_list_header.vue2
-rw-r--r--app/assets/javascripts/issuables_list/components/issuable_list_root_app.vue42
-rw-r--r--app/assets/javascripts/issuables_list/constants.js2
-rw-r--r--app/assets/javascripts/jira_import/utils/jira_import_utils.js34
-rw-r--r--app/assets/stylesheets/pages/alert_management/list.scss4
-rw-r--r--app/controllers/concerns/issuable_collections.rb56
-rw-r--r--app/controllers/projects/issues_controller.rb24
-rw-r--r--app/controllers/projects/service_desk_controller.rb45
-rw-r--r--app/controllers/projects/settings/operations_controller.rb4
-rw-r--r--app/controllers/projects_controller.rb2
-rw-r--r--app/helpers/ci/runners_helper.rb45
-rw-r--r--app/helpers/runners_helper.rb43
-rw-r--r--app/services/issues/move_service.rb11
-rw-r--r--app/services/notification_service.rb16
-rw-r--r--app/services/service_desk_settings/update_service.rb19
-rw-r--r--app/views/projects/services/prometheus/_external_alerts.html.haml2
-rw-r--r--app/views/projects/services/prometheus/_top.html.haml2
19 files changed, 279 insertions, 122 deletions
diff --git a/app/assets/javascripts/alert_management/components/alert_management_table.vue b/app/assets/javascripts/alert_management/components/alert_management_table.vue
index d86b57538ea..924d0c9b234 100644
--- a/app/assets/javascripts/alert_management/components/alert_management_table.vue
+++ b/app/assets/javascripts/alert_management/components/alert_management_table.vue
@@ -44,6 +44,8 @@ const initialPaginationState = {
lastPageSize: null,
};
+const TWELVE_HOURS_IN_MS = 12 * 60 * 60 * 1000;
+
export default {
i18n: {
noAlertsMsg: s__(
@@ -149,9 +151,20 @@ export default {
update(data) {
const { alertManagementAlerts: { nodes: list = [], pageInfo = {} } = {} } =
data.project || {};
+ const now = new Date();
+
+ const listWithData = list.map(alert => {
+ const then = new Date(alert.startedAt);
+ const diff = now - then;
+
+ return {
+ ...alert,
+ isNew: diff < TWELVE_HOURS_IN_MS,
+ };
+ });
return {
- list,
+ list: listWithData,
pageInfo,
};
},
@@ -207,9 +220,6 @@ export default {
hasAlerts() {
return this.alerts?.list?.length;
},
- tbodyTrClass() {
- return !this.loading && this.hasAlerts ? bodyTrClass : '';
- },
showPaginationControls() {
return Boolean(this.prevPage || this.nextPage);
},
@@ -290,6 +300,12 @@ export default {
resetPagination() {
this.pagination = initialPaginationState;
},
+ tbodyTrClass(item) {
+ return {
+ [bodyTrClass]: !this.loading && this.hasAlerts,
+ 'new-alert': item?.isNew,
+ };
+ },
handleAlertError(errorMessage) {
this.errored = true;
this.errorMessage = errorMessage;
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
index 63f3b3bc1f0..476866ca9e1 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
@@ -14,7 +14,6 @@ import {
GlFormSelect,
} from '@gitlab/ui';
import { debounce } from 'lodash';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import ToggleButton from '~/vue_shared/components/toggle_button.vue';
import csrf from '~/lib/utils/csrf';
@@ -42,7 +41,6 @@ export default {
directives: {
'gl-modal': GlModalDirective,
},
- mixins: [glFeatureFlagsMixin()],
props: {
prometheus: {
type: Object,
@@ -129,7 +127,7 @@ export default {
return !this.isGeneric ? this.$options.i18n.prometheusInfo : '';
},
prometheusFeatureEnabled() {
- return !this.isGeneric && this.glFeatures.alertIntegrationsDropdown;
+ return !this.isGeneric;
},
jsonIsValid() {
return this.testAlert.error === null;
@@ -147,13 +145,9 @@ export default {
}, JSON_VALIDATE_DELAY),
},
created() {
- if (this.glFeatures.alertIntegrationsDropdown) {
- this.selectedEndpoint = this.prometheus.prometheusIsActivated
- ? this.options[1].value
- : this.options[0].value;
- } else {
- this.selectedEndpoint = this.options[0].value;
- }
+ this.selectedEndpoint = this.prometheus.prometheusIsActivated
+ ? this.options[1].value
+ : this.options[0].value;
},
methods: {
clearJson() {
@@ -187,9 +181,6 @@ export default {
},
toggleService(value) {
this.canSaveForm = true;
- if (!this.glFeatures.alertIntegrationsDropdown) {
- this.toggleActivated(value);
- }
if (this.isGeneric) {
this.activated.generic = value;
@@ -334,7 +325,6 @@ export default {
</div>
<gl-form @submit.prevent="onSubmit" @reset.prevent="onReset">
<gl-form-group
- v-if="glFeatures.alertIntegrationsDropdown"
:label="$options.i18n.integrationsLabel"
label-for="integrations"
label-class="label-bold"
@@ -433,7 +423,6 @@ export default {
</gl-modal>
</gl-form-group>
<gl-form-group
- v-if="glFeatures.alertIntegrationsDropdown"
:label="$options.i18n.alertJson"
label-for="alert-json"
label-class="label-bold"
@@ -452,10 +441,7 @@ export default {
<gl-button :disabled="!canTestAlert" @click="validateTestAlert">{{
$options.i18n.testAlertInfo
}}</gl-button>
- <div
- v-if="glFeatures.alertIntegrationsDropdown"
- class="footer-block row-content-block gl-display-flex gl-justify-content-space-between"
- >
+ <div class="footer-block row-content-block gl-display-flex gl-justify-content-space-between">
<gl-button type="submit" variant="success" category="primary" :disabled="!canSaveConfig">
{{ __('Save changes') }}
</gl-button>
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index 5e617c89aa0..02a04cb4e46 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -281,7 +281,7 @@ export default {
</board-delete>
<div
v-if="showBoardListAndBoardInfo"
- class="issue-count-badge gl-pr-0 no-drag text-secondary"
+ class="issue-count-badge gl-display-inline-flex gl-pr-0 no-drag text-secondary"
:class="{ 'gl-display-none': !list.isExpanded && isSwimlanesHeader }"
>
<span class="gl-display-inline-flex">
diff --git a/app/assets/javascripts/issuables_list/components/issuable_list_root_app.vue b/app/assets/javascripts/issuables_list/components/issuable_list_root_app.vue
index f859384916b..cc90d23eda7 100644
--- a/app/assets/javascripts/issuables_list/components/issuable_list_root_app.vue
+++ b/app/assets/javascripts/issuables_list/components/issuable_list_root_app.vue
@@ -5,8 +5,9 @@ import { n__ } from '~/locale';
import getIssuesListDetailsQuery from '../queries/get_issues_list_details.query.graphql';
import {
calculateJiraImportLabel,
- isFinished,
isInProgress,
+ setFinishedAlertHideMap,
+ shouldShowFinishedAlert,
} from '~/jira_import/utils/jira_import_utils';
export default {
@@ -35,8 +36,6 @@ export default {
},
data() {
return {
- isFinishedAlertShowing: true,
- isInProgressAlertShowing: true,
jiraImport: {},
};
},
@@ -48,15 +47,18 @@ export default {
fullPath: this.projectPath,
};
},
- update: ({ project }) => ({
- importedIssuesCount: last(project.jiraImports.nodes)?.importedIssuesCount,
- isInProgress: isInProgress(project.jiraImportStatus),
- isFinished: isFinished(project.jiraImportStatus),
- label: calculateJiraImportLabel(
+ update: ({ project }) => {
+ const label = calculateJiraImportLabel(
project.jiraImports.nodes,
project.issues.nodes.flatMap(({ labels }) => labels.nodes),
- ),
- }),
+ );
+ return {
+ importedIssuesCount: last(project.jiraImports.nodes)?.importedIssuesCount,
+ label,
+ shouldShowFinishedAlert: shouldShowFinishedAlert(label.title, project.jiraImportStatus),
+ shouldShowInProgressAlert: isInProgress(project.jiraImportStatus),
+ };
+ },
skip() {
return !this.isJiraConfigured || !this.canEdit;
},
@@ -73,19 +75,14 @@ export default {
labelTarget() {
return `${this.issuesPath}?label_name[]=${encodeURIComponent(this.jiraImport.label.title)}`;
},
- shouldShowFinishedAlert() {
- return this.isFinishedAlertShowing && this.jiraImport.isFinished;
- },
- shouldShowInProgressAlert() {
- return this.isInProgressAlertShowing && this.jiraImport.isInProgress;
- },
},
methods: {
hideFinishedAlert() {
- this.isFinishedAlertShowing = false;
+ setFinishedAlertHideMap(this.jiraImport.label.title);
+ this.jiraImport.shouldShowFinishedAlert = false;
},
hideInProgressAlert() {
- this.isInProgressAlertShowing = false;
+ this.jiraImport.shouldShowInProgressAlert = false;
},
},
};
@@ -93,10 +90,15 @@ export default {
<template>
<div class="issuable-list-root">
- <gl-alert v-if="shouldShowInProgressAlert" @dismiss="hideInProgressAlert">
+ <gl-alert v-if="jiraImport.shouldShowInProgressAlert" @dismiss="hideInProgressAlert">
{{ __('Import in progress. Refresh page to see newly added issues.') }}
</gl-alert>
- <gl-alert v-if="shouldShowFinishedAlert" variant="success" @dismiss="hideFinishedAlert">
+
+ <gl-alert
+ v-if="jiraImport.shouldShowFinishedAlert"
+ variant="success"
+ @dismiss="hideFinishedAlert"
+ >
{{ finishedMessage }}
<gl-label
:background-color="jiraImport.label.color"
diff --git a/app/assets/javascripts/issuables_list/constants.js b/app/assets/javascripts/issuables_list/constants.js
index e240efd2804..f008ba1bf4a 100644
--- a/app/assets/javascripts/issuables_list/constants.js
+++ b/app/assets/javascripts/issuables_list/constants.js
@@ -52,3 +52,5 @@ export const availableSortOptionsJira = [
},
},
];
+
+export const JIRA_IMPORT_SUCCESS_ALERT_HIDE_MAP_KEY = 'jira-import-success-alert-hide-map';
diff --git a/app/assets/javascripts/jira_import/utils/jira_import_utils.js b/app/assets/javascripts/jira_import/utils/jira_import_utils.js
index e82a3f44a29..a1186b087e1 100644
--- a/app/assets/javascripts/jira_import/utils/jira_import_utils.js
+++ b/app/assets/javascripts/jira_import/utils/jira_import_utils.js
@@ -1,4 +1,5 @@
import { last } from 'lodash';
+import { JIRA_IMPORT_SUCCESS_ALERT_HIDE_MAP_KEY } from '~/issuables_list/constants';
export const IMPORT_STATE = {
FAILED: 'failed',
@@ -68,3 +69,36 @@ export const calculateJiraImportLabel = (jiraImports, labels) => {
title,
};
};
+
+/**
+ * Calculates whether the Jira import success alert should be shown.
+ *
+ * @param {string} labelTitle - Jira import label, for checking localStorage
+ * @param {string} importStatus - Jira import status
+ * @returns {boolean} - A boolean indicating whether to show the success alert
+ */
+export const shouldShowFinishedAlert = (labelTitle, importStatus) => {
+ const finishedAlertHideMap =
+ JSON.parse(localStorage.getItem(JIRA_IMPORT_SUCCESS_ALERT_HIDE_MAP_KEY)) || {};
+
+ const shouldHide = finishedAlertHideMap[labelTitle];
+
+ return !shouldHide && isFinished(importStatus);
+};
+
+/**
+ * Updates the localStorage map to permanently hide the Jira import success alert
+ *
+ * @param {string} labelTitle - Jira import label, for checking localStorage
+ */
+export const setFinishedAlertHideMap = labelTitle => {
+ const finishedAlertHideMap =
+ JSON.parse(localStorage.getItem(JIRA_IMPORT_SUCCESS_ALERT_HIDE_MAP_KEY)) || {};
+
+ finishedAlertHideMap[labelTitle] = true;
+
+ localStorage.setItem(
+ JIRA_IMPORT_SUCCESS_ALERT_HIDE_MAP_KEY,
+ JSON.stringify(finishedAlertHideMap),
+ );
+};
diff --git a/app/assets/stylesheets/pages/alert_management/list.scss b/app/assets/stylesheets/pages/alert_management/list.scss
index 5d3fd0d7dcf..e420209b1fc 100644
--- a/app/assets/stylesheets/pages/alert_management/list.scss
+++ b/app/assets/stylesheets/pages/alert_management/list.scss
@@ -1,4 +1,8 @@
.alert-management-list {
+ .new-alert {
+ background-color: $issues-today-bg;
+ }
+
// these styles need to be deleted once GlTable component looks in GitLab same as in @gitlab/ui
table {
color: $gray-700;
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index 9ef067e8797..e44a32c2702 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -81,34 +81,36 @@ module IssuableCollections
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def finder_options
- params[:state] = default_state if params[:state].blank?
-
- options = {
- scope: params[:scope],
- state: params[:state],
- confidential: Gitlab::Utils.to_boolean(params[:confidential]),
- sort: set_sort_order
- }
-
- # Used by view to highlight active option
- @sort = options[:sort]
-
- # When a user looks for an exact iid, we do not filter by search but only by iid
- if params[:search] =~ /^#(?<iid>\d+)\z/
- options[:iids] = Regexp.last_match[:iid]
- params[:search] = nil
+ strong_memoize(:finder_options) do
+ params[:state] = default_state if params[:state].blank?
+
+ options = {
+ scope: params[:scope],
+ state: params[:state],
+ confidential: Gitlab::Utils.to_boolean(params[:confidential]),
+ sort: set_sort_order
+ }
+
+ # Used by view to highlight active option
+ @sort = options[:sort]
+
+ # When a user looks for an exact iid, we do not filter by search but only by iid
+ if params[:search] =~ /^#(?<iid>\d+)\z/
+ options[:iids] = Regexp.last_match[:iid]
+ params[:search] = nil
+ end
+
+ if @project
+ options[:project_id] = @project.id
+ options[:attempt_project_search_optimizations] = true
+ elsif @group
+ options[:group_id] = @group.id
+ options[:include_subgroups] = true
+ options[:attempt_group_search_optimizations] = true
+ end
+
+ params.permit(finder_type.valid_params).merge(options)
end
-
- if @project
- options[:project_id] = @project.id
- options[:attempt_project_search_optimizations] = true
- elsif @group
- options[:group_id] = @group.id
- options[:include_subgroups] = true
- options[:attempt_group_search_optimizations] = true
- end
-
- params.permit(finder_type.valid_params).merge(options)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 92f6797ffe4..12b5a538bc9 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -11,11 +11,11 @@ class Projects::IssuesController < Projects::ApplicationController
include RecordUserLastActivity
def issue_except_actions
- %i[index calendar new create bulk_update import_csv export_csv]
+ %i[index calendar new create bulk_update import_csv export_csv service_desk]
end
def set_issuables_index_only_actions
- %i[index calendar]
+ %i[index calendar service_desk]
end
prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
@@ -223,6 +223,11 @@ class Projects::IssuesController < Projects::ApplicationController
redirect_to project_issues_path(project)
end
+ def service_desk
+ @issues = @issuables # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ @users.push(User.support_bot) # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ end
+
protected
def sorting_field
@@ -320,6 +325,17 @@ class Projects::IssuesController < Projects::ApplicationController
private
+ def finder_options
+ options = super
+
+ return options unless service_desk?
+
+ options.reject! { |key| key == 'author_username' || key == 'author_id' }
+ options[:author_id] = User.support_bot
+
+ options
+ end
+
def branch_link(branch)
project_compare_path(project, from: project.default_branch, to: branch[:name])
end
@@ -337,6 +353,10 @@ class Projects::IssuesController < Projects::ApplicationController
def rate_limiter
::Gitlab::ApplicationRateLimiter
end
+
+ def service_desk?
+ action_name == 'service_desk'
+ end
end
Projects::IssuesController.prepend_if_ee('EE::Projects::IssuesController')
diff --git a/app/controllers/projects/service_desk_controller.rb b/app/controllers/projects/service_desk_controller.rb
new file mode 100644
index 00000000000..bcd190bbc2c
--- /dev/null
+++ b/app/controllers/projects/service_desk_controller.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+class Projects::ServiceDeskController < Projects::ApplicationController
+ before_action :authorize_admin_project!
+
+ def show
+ json_response
+ end
+
+ def update
+ Projects::UpdateService.new(project, current_user, { service_desk_enabled: params[:service_desk_enabled] }).execute
+
+ result = ServiceDeskSettings::UpdateService.new(project, current_user, setting_params).execute
+
+ if result[:status] == :success
+ json_response
+ else
+ render json: { message: result[:message] }, status: :unprocessable_entity
+ end
+ end
+
+ private
+
+ def setting_params
+ params.permit(:issue_template_key, :outgoing_name, :project_key)
+ end
+
+ def json_response
+ respond_to do |format|
+ service_desk_settings = project.service_desk_setting
+
+ service_desk_attributes =
+ {
+ service_desk_address: project.service_desk_address,
+ service_desk_enabled: project.service_desk_enabled,
+ issue_template_key: service_desk_settings&.issue_template_key,
+ template_file_missing: service_desk_settings&.issue_template_missing?,
+ outgoing_name: service_desk_settings&.outgoing_name,
+ project_key: service_desk_settings&.project_key
+ }
+
+ format.json { render json: service_desk_attributes }
+ end
+ end
+end
diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb
index c7aeac829af..36215898346 100644
--- a/app/controllers/projects/settings/operations_controller.rb
+++ b/app/controllers/projects/settings/operations_controller.rb
@@ -5,10 +5,6 @@ module Projects
class OperationsController < Projects::ApplicationController
before_action :authorize_admin_operations!
before_action :authorize_read_prometheus_alerts!, only: [:reset_alerting_token]
- before_action do
- push_frontend_feature_flag(:alert_integrations_dropdown, project)
- end
-
respond_to :json, only: [:reset_alerting_token]
helper_method :error_tracking_setting
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 00c01c86ae2..a5666cb70ac 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -38,6 +38,7 @@ class ProjectsController < Projects::ApplicationController
before_action only: [:new, :create] do
frontend_experimentation_tracking_data(:new_create_project_ui, 'click_tab')
push_frontend_feature_flag(:new_create_project_ui) if experiment_enabled?(:new_create_project_ui)
+ push_frontend_feature_flag(:service_desk_custom_address, @project)
end
layout :determine_layout
@@ -391,6 +392,7 @@ class ProjectsController < Projects::ApplicationController
:initialize_with_readme,
:autoclose_referenced_issues,
:suggestion_commit_message,
+ :service_desk_enabled,
project_feature_attributes: %i[
builds_access_level
diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb
new file mode 100644
index 00000000000..8cdb28b2874
--- /dev/null
+++ b/app/helpers/ci/runners_helper.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Ci
+ module RunnersHelper
+ def runner_status_icon(runner)
+ status = runner.status
+ case status
+ when :not_connected
+ content_tag :i, nil,
+ class: "fa fa-warning",
+ title: "New runner. Has not connected yet"
+
+ when :online, :offline, :paused
+ content_tag :i, nil,
+ class: "fa fa-circle runner-status-#{status}",
+ title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
+ end
+ end
+
+ def runner_link(runner)
+ display_name = truncate(runner.display_name, length: 15)
+ id = "\##{runner.id}"
+
+ if current_user && current_user.admin
+ link_to admin_runner_path(runner) do
+ display_name + id
+ end
+ else
+ display_name + id
+ end
+ end
+
+ # Due to inability of performing sorting of runners by cached "contacted_at" values we have to show uncached values if sorting by "contacted_asc" is requested.
+ # Please refer to the following issue for more details: https://gitlab.com/gitlab-org/gitlab-foss/issues/55920
+ def runner_contacted_at(runner)
+ if params[:sort] == 'contacted_asc'
+ runner.uncached_contacted_at
+ else
+ runner.contacted_at
+ end
+ end
+ end
+end
+
+Ci::RunnersHelper.prepend_if_ee('EE::Ci::RunnersHelper')
diff --git a/app/helpers/runners_helper.rb b/app/helpers/runners_helper.rb
deleted file mode 100644
index d871aaa9c86..00000000000
--- a/app/helpers/runners_helper.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: true
-
-module RunnersHelper
- def runner_status_icon(runner)
- status = runner.status
- case status
- when :not_connected
- content_tag :i, nil,
- class: "fa fa-warning",
- title: "New runner. Has not connected yet"
-
- when :online, :offline, :paused
- content_tag :i, nil,
- class: "fa fa-circle runner-status-#{status}",
- title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
- end
- end
-
- def runner_link(runner)
- display_name = truncate(runner.display_name, length: 15)
- id = "\##{runner.id}"
-
- if current_user && current_user.admin
- link_to admin_runner_path(runner) do
- display_name + id
- end
- else
- display_name + id
- end
- end
-
- # Due to inability of performing sorting of runners by cached "contacted_at" values we have to show uncached values if sorting by "contacted_asc" is requested.
- # Please refer to the following issue for more details: https://gitlab.com/gitlab-org/gitlab-foss/issues/55920
- def runner_contacted_at(runner)
- if params[:sort] == 'contacted_asc'
- runner.uncached_contacted_at
- else
- runner.contacted_at
- end
- end
-end
-
-RunnersHelper.prepend_if_ee('EE::RunnersHelper')
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index 2409396c1ac..ce1466307e1 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -19,11 +19,22 @@ module Issues
notify_participants
+ # Updates old issue sent notifications allowing
+ # to receive service desk emails on the new moved issue.
+ update_service_desk_sent_notifications
+
new_entity
end
private
+ def update_service_desk_sent_notifications
+ return unless original_entity.from_service_desk?
+
+ original_entity
+ .sent_notifications.update_all(project_id: new_entity.project_id, noteable_id: new_entity.id)
+ end
+
def update_old_entity
super
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 73e60ac8420..87664af3c10 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -294,6 +294,7 @@ class NotificationService
return true if note.system_note_with_references?
send_new_note_notifications(note)
+ send_service_desk_notification(note)
end
def send_new_note_notifications(note)
@@ -305,6 +306,21 @@ class NotificationService
end
end
+ def send_service_desk_notification(note)
+ return unless Gitlab::ServiceDesk.supported?
+ return unless note.noteable_type == 'Issue'
+
+ issue = note.noteable
+ support_bot = User.support_bot
+
+ return unless issue.service_desk_reply_to.present?
+ return unless issue.project.service_desk_enabled?
+ return if note.author == support_bot
+ return unless issue.subscribed?(support_bot, issue.project)
+
+ mailer.service_desk_new_note_email(issue.id, note.id).deliver_later
+ end
+
# Notify users when a new release is created
def send_new_release_notifications(release)
recipients = NotificationRecipients::BuildService.build_new_release_recipients(release)
diff --git a/app/services/service_desk_settings/update_service.rb b/app/services/service_desk_settings/update_service.rb
new file mode 100644
index 00000000000..08106b04d18
--- /dev/null
+++ b/app/services/service_desk_settings/update_service.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module ServiceDeskSettings
+ class UpdateService < BaseService
+ def execute
+ settings = ServiceDeskSetting.safe_find_or_create_by!(project_id: project.id)
+
+ unless ::Feature.enabled?(:service_desk_custom_address, project)
+ params.delete(:project_key)
+ end
+
+ if settings.update(params)
+ success
+ else
+ error(settings.errors.full_messages.to_sentence)
+ end
+ end
+ end
+end
diff --git a/app/views/projects/services/prometheus/_external_alerts.html.haml b/app/views/projects/services/prometheus/_external_alerts.html.haml
index 9beccd36dfa..7fc67a8cb4f 100644
--- a/app/views/projects/services/prometheus/_external_alerts.html.haml
+++ b/app/views/projects/services/prometheus/_external_alerts.html.haml
@@ -5,4 +5,4 @@
- authorization_key = @project.alerting_setting.try(:token)
- learn_more_url = help_page_path('user/project/integrations/prometheus', anchor: 'external-prometheus-instances')
-#js-settings-prometheus-alerts{ data: { notify_url: notify_url, authorization_key: authorization_key, change_key_url: reset_alerting_token_project_settings_operations_path(@project), learn_more_url: learn_more_url, disabled: Feature.enabled?(:alert_integrations_dropdown, @service.project) && @service.manual_configuration? } }
+#js-settings-prometheus-alerts{ data: { notify_url: notify_url, authorization_key: authorization_key, change_key_url: reset_alerting_token_project_settings_operations_path(@project), learn_more_url: learn_more_url, disabled: true } }
diff --git a/app/views/projects/services/prometheus/_top.html.haml b/app/views/projects/services/prometheus/_top.html.haml
index 62225958c04..338414be5ab 100644
--- a/app/views/projects/services/prometheus/_top.html.haml
+++ b/app/views/projects/services/prometheus/_top.html.haml
@@ -1,4 +1,4 @@
-- return unless Feature.enabled?(:alert_integrations_dropdown, @service.project) && @service.manual_configuration?
+- return unless @service.manual_configuration?
.row
.col-lg-12