Commit 2612a344 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch '201855-rename-project_services_to_integrations-5' into 'master'

Move 5 integrations to Integrations:: namespace [RUN ALL RSPEC]

See merge request gitlab-org/gitlab!61854
parents 030a76b3 c1867b98
......@@ -1674,16 +1674,11 @@ Gitlab/NamespacedClass:
- 'app/models/project_services/alerts_service_data.rb'
- 'app/models/project_services/bugzilla_service.rb'
- 'app/models/project_services/buildkite_service.rb'
- 'app/models/project_services/builds_email_service.rb'
- 'app/models/project_services/campfire_service.rb'
- 'app/models/project_services/chat_notification_service.rb'
- 'app/models/project_services/ci_service.rb'
- 'app/models/project_services/confluence_service.rb'
- 'app/models/project_services/custom_issue_tracker_service.rb'
- 'app/models/project_services/datadog_service.rb'
- 'app/models/project_services/discord_service.rb'
- 'app/models/project_services/drone_ci_service.rb'
- 'app/models/project_services/emails_on_push_service.rb'
- 'app/models/project_services/ewm_service.rb'
- 'app/models/project_services/external_wiki_service.rb'
- 'app/models/project_services/flowdock_service.rb'
......
......@@ -2,12 +2,15 @@
# This class is to be removed with 9.1
# We should also by then remove BuildsEmailService from database
class BuildsEmailService < Integration
def self.to_param
'builds_email'
end
# https://gitlab.com/gitlab-org/gitlab/-/issues/331064
module Integrations
class BuildsEmail < Integration
def self.to_param
'builds_email'
end
def self.supported_events
%w[]
def self.supported_events
%w[]
end
end
end
# frozen_string_literal: true
module Integrations
class Campfire < Integration
prop_accessor :token, :subdomain, :room
validates :token, presence: true, if: :activated?
def title
'Campfire'
end
def description
'Send notifications about push events to Campfire chat rooms.'
end
def self.to_param
'campfire'
end
def fields
[
{ type: 'text', name: 'token', placeholder: '', required: true },
{ type: 'text', name: 'subdomain', placeholder: '' },
{ type: 'text', name: 'room', placeholder: '' }
]
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
message = build_message(data)
speak(self.room, message, auth)
end
private
def base_uri
@base_uri ||= "https://#{subdomain}.campfirenow.com"
end
def auth
# use a dummy password, as explained in the Campfire API doc:
# https://github.com/basecamp/campfire-api#authentication
@auth ||= {
basic_auth: {
username: token,
password: 'X'
}
}
end
# Post a message into a room, returns the message Hash in case of success.
# Returns nil otherwise.
# https://github.com/basecamp/campfire-api/blob/master/sections/messages.md#create-message
def speak(room_name, message, auth)
room = rooms(auth).find { |r| r["name"] == room_name }
return unless room
path = "/room/#{room["id"]}/speak.json"
body = {
body: {
message: {
type: 'TextMessage',
body: message
}
}
}
res = Gitlab::HTTP.post(path, base_uri: base_uri, **auth.merge(body))
res.code == 201 ? res : nil
end
# Returns a list of rooms, or [].
# https://github.com/basecamp/campfire-api/blob/master/sections/rooms.md#get-rooms
def rooms(auth)
res = Gitlab::HTTP.get("/rooms.json", base_uri: base_uri, **auth)
res.code == 200 ? res["rooms"] : []
end
def build_message(push)
ref = Gitlab::Git.ref_name(push[:ref])
before = push[:before]
after = push[:after]
message = []
message << "[#{project.full_name}] "
message << "#{push[:user_name]} "
if Gitlab::Git.blank_ref?(before)
message << "pushed new branch #{ref} \n"
elsif Gitlab::Git.blank_ref?(after)
message << "removed branch #{ref} \n"
else
message << "pushed #{push[:total_commits_count]} commits to #{ref}. "
message << "#{project.web_url}/compare/#{before}...#{after}"
end
message.join
end
end
end
# frozen_string_literal: true
module Integrations
class Confluence < Integration
include ActionView::Helpers::UrlHelper
VALID_SCHEME_MATCH = %r{\Ahttps?\Z}.freeze
VALID_HOST_MATCH = %r{\A.+\.atlassian\.net\Z}.freeze
VALID_PATH_MATCH = %r{\A/wiki(/|\Z)}.freeze
prop_accessor :confluence_url
validates :confluence_url, presence: true, if: :activated?
validate :validate_confluence_url_is_cloud, if: :activated?
after_commit :cache_project_has_confluence
def self.to_param
'confluence'
end
def self.supported_events
%w()
end
def title
s_('ConfluenceService|Confluence Workspace')
end
def description
s_('ConfluenceService|Link to a Confluence Workspace from the sidebar.')
end
def help
return unless project&.wiki_enabled?
if activated?
wiki_url = project.wiki.web_url
s_(
'ConfluenceService|Your GitLab wiki is still available at %{wiki_link}. To re-enable the link to the GitLab wiki, disable this integration.' %
{ wiki_link: link_to(wiki_url, wiki_url) }
).html_safe
else
s_('ConfluenceService|Link to a Confluence Workspace from the sidebar. Enabling this integration replaces the "Wiki" sidebar link with a link to the Confluence Workspace. The GitLab wiki is still available at the original URL.').html_safe
end
end
def fields
[
{
type: 'text',
name: 'confluence_url',
title: s_('Confluence Cloud Workspace URL'),
placeholder: 'https://example.atlassian.net/wiki',
required: true
}
]
end
def can_test?
false
end
private
def validate_confluence_url_is_cloud
unless confluence_uri_valid?
errors.add(:confluence_url, 'URL must be to a Confluence Cloud Workspace hosted on atlassian.net')
end
end
def confluence_uri_valid?
return false unless confluence_url
uri = URI.parse(confluence_url)
(uri.scheme&.match(VALID_SCHEME_MATCH) &&
uri.host&.match(VALID_HOST_MATCH) &&
uri.path&.match(VALID_PATH_MATCH)).present?
rescue URI::InvalidURIError
false
end
def cache_project_has_confluence
return unless project && !project.destroyed?
project.project_setting.save! unless project.project_setting.persisted?
project.project_setting.update_column(:has_confluence, active?)
end
end
end
# frozen_string_literal: true
module Integrations
class Datadog < Integration
DEFAULT_SITE = 'datadoghq.com'
URL_TEMPLATE = 'https://webhooks-http-intake.logs.%{datadog_site}/v1/input/'
URL_TEMPLATE_API_KEYS = 'https://app.%{datadog_site}/account/settings#api'
URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_SITE}/account_management/api-app-keys/"
SUPPORTED_EVENTS = %w[
pipeline job
].freeze
prop_accessor :datadog_site, :api_url, :api_key, :datadog_service, :datadog_env
with_options if: :activated? do
validates :api_key, presence: true, format: { with: /\A\w+\z/ }
validates :datadog_site, format: { with: /\A[\w\.]+\z/, allow_blank: true }
validates :api_url, public_url: { allow_blank: true }
validates :datadog_site, presence: true, unless: -> (obj) { obj.api_url.present? }
validates :api_url, presence: true, unless: -> (obj) { obj.datadog_site.present? }
end
after_save :compose_service_hook, if: :activated?
def initialize_properties
super
self.datadog_site ||= DEFAULT_SITE
end
def self.supported_events
SUPPORTED_EVENTS
end
def self.default_test_event
'pipeline'
end
def configurable_events
[] # do not allow to opt out of required hooks
end
def title
'Datadog'
end
def description
'Trace your GitLab pipelines with Datadog'
end
def help
nil
end
def self.to_param
'datadog'
end
def fields
[
{
type: 'text',
name: 'datadog_site',
placeholder: DEFAULT_SITE,
help: 'Choose the Datadog site to send data to. Set to "datadoghq.eu" to send data to the EU site',
required: false
},
{
type: 'text',
name: 'api_url',
title: 'API URL',
help: '(Advanced) Define the full URL for your Datadog site directly',
required: false
},
{
type: 'password',
name: 'api_key',
title: _('API key'),
non_empty_password_title: s_('ProjectService|Enter new API key'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current API key'),
help: "<a href=\"#{api_keys_url}\" target=\"_blank\">API key</a> used for authentication with Datadog",
required: true
},
{
type: 'text',
name: 'datadog_service',
title: 'Service',
placeholder: 'gitlab-ci',
help: 'Name of this GitLab instance that all data will be tagged with'
},
{
type: 'text',
name: 'datadog_env',
title: 'Env',
help: 'The environment tag that traces will be tagged with'
}
]
end
def compose_service_hook
hook = service_hook || build_service_hook
hook.url = hook_url
hook.save
end
def hook_url
url = api_url.presence || sprintf(URL_TEMPLATE, datadog_site: datadog_site)
url = URI.parse(url)
url.path = File.join(url.path || '/', api_key)
query = { service: datadog_service.presence, env: datadog_env.presence }.compact
url.query = query.to_query unless query.empty?
url.to_s
end
def api_keys_url
return URL_API_KEYS_DOCS unless datadog_site.presence
sprintf(URL_TEMPLATE_API_KEYS, datadog_site: datadog_site)
end
def execute(data)
return if project.disabled_services.include?(to_param)
object_kind = data[:object_kind]
object_kind = 'job' if object_kind == 'build'
return unless supported_events.include?(object_kind)
service_hook.execute(data, "#{object_kind} hook")
end
def test(data)
begin
result = execute(data)
return { success: false, result: result[:message] } if result[:http_status] != 200
rescue StandardError => error
return { success: false, result: error }
end
{ success: true, result: result[:message] }
end
end
end
# frozen_string_literal: true
module Integrations
class EmailsOnPush < Integration
include NotificationBranchSelection
RECIPIENTS_LIMIT = 750
boolean_accessor :send_from_committer_email
boolean_accessor :disable_diffs
prop_accessor :recipients, :branches_to_be_notified
validates :recipients, presence: true, if: :validate_recipients?
validate :number_of_recipients_within_limit, if: :validate_recipients?
def self.valid_recipients(recipients)
recipients.split.select do |recipient|
recipient.include?('@')
end.uniq(&:downcase)
end
def title
s_('EmailsOnPushService|Emails on push')
end
def description
s_('EmailsOnPushService|Email the commits and diff of each push to a list of recipients.')
end
def self.to_param
'emails_on_push'
end
def self.supported_events
%w(push tag_push)
end
def initialize_properties
super
self.branches_to_be_notified = 'all' if branches_to_be_notified.nil?
end
def execute(push_data)
return unless supported_events.include?(push_data[:object_kind])
return if project.emails_disabled?
return unless notify_for_ref?(push_data)
EmailsOnPushWorker.perform_async(
project_id,
recipients,
push_data,
send_from_committer_email: send_from_committer_email?,
disable_diffs: disable_diffs?
)
end
def notify_for_ref?(push_data)
return true if push_data[:object_kind] == 'tag_push'
return true if push_data.dig(:object_attributes, :tag)
notify_for_branch?(push_data)
end
def send_from_committer_email?
Gitlab::Utils.to_boolean(self.send_from_committer_email)
end
def disable_diffs?
Gitlab::Utils.to_boolean(self.disable_diffs)
end
def fields
domains = Notify.allowed_email_domains.map { |domain| "user@#{domain}" }.join(", ")
[
{ type: 'checkbox', name: 'send_from_committer_email', title: s_("EmailsOnPushService|Send from committer"),
help: s_("EmailsOnPushService|Send notifications from the committer's email address if the domain matches the domain used by your GitLab instance (such as %{domains}).") % { domains: domains } },
{ type: 'checkbox', name: 'disable_diffs', title: s_("EmailsOnPushService|Disable code diffs"),
help: s_("EmailsOnPushService|Don't include possibly sensitive code diffs in notification body.") },
{ type: 'select', name: 'branches_to_be_notified', choices: branch_choices },
{
type: 'textarea',
name: 'recipients',
placeholder: s_('EmailsOnPushService|tanuki@example.com gitlab@example.com'),
help: s_('EmailsOnPushService|Emails separated by whitespace.')
}
]
end
private
def number_of_recipients_within_limit
return if recipients.blank?
if self.class.valid_recipients(recipients).size > RECIPIENTS_LIMIT
errors.add(:recipients, s_("EmailsOnPushService|can't exceed %{recipients_limit}") % { recipients_limit: RECIPIENTS_LIMIT })
end
end
end
end
......@@ -153,11 +153,12 @@ class Project < ApplicationRecord
has_one :asana_service, class_name: 'Integrations::Asana'
has_one :assembla_service, class_name: 'Integrations::Assembla'
has_one :bamboo_service, class_name: 'Integrations::Bamboo'
has_one :campfire_service
has_one :datadog_service
has_one :campfire_service, class_name: 'Integrations::Campfire'
has_one :confluence_service, class_name: 'Integrations::Confluence'
has_one :datadog_service, class_name: 'Integrations::Datadog'
has_one :emails_on_push_service, class_name: 'Integrations::EmailsOnPush'
has_one :discord_service
has_one :drone_ci_service
has_one :emails_on_push_service
has_one :ewm_service
has_one :pipelines_email_service
has_one :irker_service
......@@ -176,7 +177,6 @@ class Project < ApplicationRecord
has_one :youtrack_service
has_one :custom_issue_tracker_service
has_one :bugzilla_service
has_one :confluence_service
has_one :external_wiki_service
has_one :prometheus_service, inverse_of: :project
has_one :mock_ci_service
......
# frozen_string_literal: true
class CampfireService < Integration
prop_accessor :token, :subdomain, :room
validates :token, presence: true, if: :activated?
def title
'Campfire'
end
def description
'Send notifications about push events to Campfire chat rooms.'
end
def self.to_param
'campfire'
end
def fields
[
{ type: 'text', name: 'token', placeholder: '', required: true },
{ type: 'text', name: 'subdomain', placeholder: '' },
{ type: 'text', name: 'room', placeholder: '' }
]
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
message = build_message(data)
speak(self.room, message, auth)
end
private
def base_uri
@base_uri ||= "https://#{subdomain}.campfirenow.com"
end
def auth
# use a dummy password, as explained in the Campfire API doc:
# https://github.com/basecamp/campfire-api#authentication
@auth ||= {
basic_auth: {
username: token,
password: 'X'
}
}
end
# Post a message into a room, returns the message Hash in case of success.
# Returns nil otherwise.
# https://github.com/basecamp/campfire-api/blob/master/sections/messages.md#create-message
def speak(room_name, message, auth)
room = rooms(auth).find { |r| r["name"] == room_name }
return unless room
path = "/room/#{room["id"]}/speak.json"
body = {
body: {
message: {
type: 'TextMessage',
body: message
}
}
}
res = Gitlab::HTTP.post(path, base_uri: base_uri, **auth.merge(body))
res.code == 201 ? res : nil
end
# Returns a list of rooms, or [].
# https://github.com/basecamp/campfire-api/blob/master/sections/rooms.md#get-rooms
def rooms(auth)
res = Gitlab::HTTP.get("/rooms.json", base_uri: base_uri, **auth)
res.code == 200 ? res["rooms"] : []
end
def build_message(push)
ref = Gitlab::Git.ref_name(push[:ref])
before = push[:before]
after = push[:after]
message = []
message << "[#{project.full_name}] "
message << "#{push[:user_name]} "
if Gitlab::Git.blank_ref?(before)
message << "pushed new branch #{ref} \n"
elsif Gitlab::Git.blank_ref?(after)
message << "removed branch #{ref} \n"
else
message << "pushed #{push[:total_commits_count]} commits to #{ref}. "
message << "#{project.web_url}/compare/#{before}...#{after}"
end
message.join
end
end
# frozen_string_literal: true
class ConfluenceService < Integration
include ActionView::Helpers::UrlHelper
VALID_SCHEME_MATCH = %r{\Ahttps?\Z}.freeze
VALID_HOST_MATCH = %r{\A.+\.atlassian\.net\Z}.freeze
VALID_PATH_MATCH = %r{\A/wiki(/|\Z)}.freeze
prop_accessor :confluence_url
validates :confluence_url, presence: true, if: :activated?
validate :validate_confluence_url_is_cloud, if: :activated?
after_commit :cache_project_has_confluence
def self.to_param
'confluence'
end
def self.supported_events
%w()
end
def title
s_('ConfluenceService|Confluence Workspace')
end
def description
s_('ConfluenceService|Link to a Confluence Workspace from the sidebar.')
end
def help
return unless project&.wiki_enabled?
if activated?
wiki_url = project.wiki.web_url
s_(
'ConfluenceService|Your GitLab wiki is still available at %{wiki_link}. To re-enable the link to the GitLab wiki, disable this integration.' %
{ wiki_link: link_to(wiki_url, wiki_url) }
).html_safe
else
s_('ConfluenceService|Link to a Confluence Workspace from the sidebar. Enabling this integration replaces the "Wiki" sidebar link with a link to the Confluence Workspace. The GitLab wiki is still available at the original URL.').html_safe
end
end
def fields
[
{
type: 'text',
name: 'confluence_url',
title: s_('Confluence Cloud Workspace URL'),
placeholder: 'https://example.atlassian.net/wiki',
required: true
}
]
end
def can_test?
false
end
private
def validate_confluence_url_is_cloud
unless confluence_uri_valid?
errors.add(:confluence_url, 'URL must be to a Confluence Cloud Workspace hosted on atlassian.net')
end
end
def confluence_uri_valid?
return false unless confluence_url
uri = URI.parse(confluence_url)
(uri.scheme&.match(VALID_SCHEME_MATCH) &&
uri.host&.match(VALID_HOST_MATCH) &&
uri.path&.match(VALID_PATH_MATCH)).present?
rescue URI::InvalidURIError
false
end
def cache_project_has_confluence
return unless project && !project.destroyed?
project.project_setting.save! unless project.project_setting.persisted?
project.project_setting.update_column(:has_confluence, active?)
end
end
# frozen_string_literal: true
class DatadogService < Integration
DEFAULT_SITE = 'datadoghq.com'
URL_TEMPLATE = 'https://webhooks-http-intake.logs.%{datadog_site}/v1/input/'
URL_TEMPLATE_API_KEYS = 'https://app.%{datadog_site}/account/settings#api'
URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_SITE}/account_management/api-app-keys/"
SUPPORTED_EVENTS = %w[
pipeline job
].freeze
prop_accessor :datadog_site, :api_url, :api_key, :datadog_service, :datadog_env
with_options if: :activated? do
validates :api_key, presence: true, format: { with: /\A\w+\z/ }
validates :datadog_site, format: { with: /\A[\w\.]+\z/, allow_blank: true }
validates :api_url, public_url: { allow_blank: true }
validates :datadog_site, presence: true, unless: -> (obj) { obj.api_url.present? }
validates :api_url, presence: true, unless: -> (obj) { obj.datadog_site.present? }
end
after_save :compose_service_hook, if: :activated?
def initialize_properties
super
self.datadog_site ||= DEFAULT_SITE
end
def self.supported_events
SUPPORTED_EVENTS
end
def self.default_test_event
'pipeline'
end
def configurable_events
[] # do not allow to opt out of required hooks
end
def title
'Datadog'
end
def description
'Trace your GitLab pipelines with Datadog'
end
def help
nil
# Maybe adding something in the future
# We could link to static help pages as well
# [More information](#{Gitlab::Routing.url_helpers.help_page_url('integration/datadog')})"
end
def self.to_param
'datadog'
end
def fields
[
{
type: 'text',
name: 'datadog_site',
placeholder: DEFAULT_SITE,
help: 'Choose the Datadog site to send data to. Set to "datadoghq.eu" to send data to the EU site',
required: false
},
{
type: 'text',
name: 'api_url',
title: 'API URL',
help: '(Advanced) Define the full URL for your Datadog site directly',
required: false
},
{
type: 'password',
name: 'api_key',
title: _('API key'),
non_empty_password_title: s_('ProjectService|Enter new API key'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current API key'),
help: "<a href=\"#{api_keys_url}\" target=\"_blank\">API key</a> used for authentication with Datadog",
required: true
},
{
type: 'text',
name: 'datadog_service',
title: 'Service',
placeholder: 'gitlab-ci',
help: 'Name of this GitLab instance that all data will be tagged with'
},
{
type: 'text',
name: 'datadog_env',
title: 'Env',
help: 'The environment tag that traces will be tagged with'
}
]
end
def compose_service_hook
hook = service_hook || build_service_hook
hook.url = hook_url
hook.save
end
def hook_url
url = api_url.presence || sprintf(URL_TEMPLATE, datadog_site: datadog_site)
url = URI.parse(url)
url.path = File.join(url.path || '/', api_key)
query = { service: datadog_service.presence, env: datadog_env.presence }.compact
url.query = query.to_query unless query.empty?
url.to_s
end
def api_keys_url
return URL_API_KEYS_DOCS unless datadog_site.presence
sprintf(URL_TEMPLATE_API_KEYS, datadog_site: datadog_site)
end
def execute(data)
return if project.disabled_services.include?(to_param)
object_kind = data[:object_kind]
object_kind = 'job' if object_kind == 'build'
return unless supported_events.include?(object_kind)
service_hook.execute(data, "#{object_kind} hook")
end
def test(data)
begin
result = execute(data)
return { success: false, result: result[:message] } if result[:http_status] != 200
rescue StandardError => error
return { success: false, result: error }
end
{ success: true, result: result[:message] }
end
end
# frozen_string_literal: true
class EmailsOnPushService < Integration
include NotificationBranchSelection
RECIPIENTS_LIMIT = 750
boolean_accessor :send_from_committer_email
boolean_accessor :disable_diffs
prop_accessor :recipients, :branches_to_be_notified
validates :recipients, presence: true, if: :validate_recipients?
validate :number_of_recipients_within_limit, if: :validate_recipients?
def self.valid_recipients(recipients)
recipients.split.select do |recipient|
recipient.include?('@')
end.uniq(&:downcase)
end
def title
s_('EmailsOnPushService|Emails on push')
end
def description
s_('EmailsOnPushService|Email the commits and diff of each push to a list of recipients.')
end
def self.to_param
'emails_on_push'
end
def self.supported_events
%w(push tag_push)
end
def initialize_properties
super
self.branches_to_be_notified = 'all' if branches_to_be_notified.nil?
end
def execute(push_data)
return unless supported_events.include?(push_data[:object_kind])
return if project.emails_disabled?
return unless notify_for_ref?(push_data)
EmailsOnPushWorker.perform_async(
project_id,
recipients,
push_data,
send_from_committer_email: send_from_committer_email?,
disable_diffs: disable_diffs?
)
end
def notify_for_ref?(push_data)
return true if push_data[:object_kind] == 'tag_push'
return true if push_data.dig(:object_attributes, :tag)
notify_for_branch?(push_data)
end
def send_from_committer_email?
Gitlab::Utils.to_boolean(self.send_from_committer_email)
end
def disable_diffs?
Gitlab::Utils.to_boolean(self.disable_diffs)
end
def fields
domains = Notify.allowed_email_domains.map { |domain| "user@#{domain}" }.join(", ")
[
{ type: 'checkbox', name: 'send_from_committer_email', title: s_("EmailsOnPushService|Send from committer"),
help: s_("EmailsOnPushService|Send notifications from the committer's email address if the domain matches the domain used by your GitLab instance (such as %{domains}).") % { domains: domains } },
{ type: 'checkbox', name: 'disable_diffs', title: s_("EmailsOnPushService|Disable code diffs"),
help: s_("EmailsOnPushService|Don't include possibly sensitive code diffs in notification body.") },
{ type: 'select', name: 'branches_to_be_notified', choices: branch_choices },
{
type: 'textarea',
name: 'recipients',
placeholder: s_('EmailsOnPushService|tanuki@example.com gitlab@example.com'),
help: s_('EmailsOnPushService|Emails separated by whitespace.')
}
]
end
private
def number_of_recipients_within_limit
return if recipients.blank?
if self.class.valid_recipients(recipients).size > RECIPIENTS_LIMIT
errors.add(:recipients, s_("EmailsOnPushService|can't exceed %{recipients_limit}") % { recipients_limit: RECIPIENTS_LIMIT })
end
end
end
......@@ -58,7 +58,7 @@ class EmailsOnPushWorker # rubocop:disable Scalability/IdempotentWorker
end
end
EmailsOnPushService.valid_recipients(recipients).each do |recipient|
Integrations::EmailsOnPush.valid_recipients(recipients).each do |recipient|
send_email(
recipient,
project_id,
......
......@@ -777,15 +777,15 @@ module API
::Integrations::Asana,
::Integrations::Assembla,
::Integrations::Bamboo,
::Integrations::Campfire,
::Integrations::Confluence,
::Integrations::Datadog,
::Integrations::EmailsOnPush,
::BugzillaService,
::BuildkiteService,
::ConfluenceService,
::CampfireService,
::CustomIssueTrackerService,
::DatadogService,
::DiscordService,
::DroneCiService,
::EmailsOnPushService,
::EwmService,
::ExternalWikiService,
::FlowdockService,
......
......@@ -4,7 +4,7 @@ module Gitlab
module Integrations
class StiType < ActiveRecord::Type::String
NAMESPACED_INTEGRATIONS = Set.new(%w(
Asana Assembla Bamboo
Asana Assembla Bamboo Campfire Confluence Datadog EmailsOnPush
)).freeze
def cast(value)
......
......@@ -12,7 +12,7 @@ FactoryBot.define do
issue_tracker
end
factory :emails_on_push_service do
factory :emails_on_push_service, class: 'Integrations::EmailsOnPush' do
project
type { 'EmailsOnPushService' }
active { true }
......@@ -79,7 +79,7 @@ FactoryBot.define do
end
end
factory :confluence_service do
factory :confluence_service, class: 'Integrations::Confluence' do
project
active { true }
confluence_url { 'https://example.atlassian.net/wiki' }
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe CampfireService do
RSpec.describe Integrations::Campfire do
include StubRequests
describe 'Associations' do
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe ConfluenceService do
RSpec.describe Integrations::Confluence do
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
......
......@@ -3,7 +3,7 @@ require 'securerandom'
require 'spec_helper'
RSpec.describe DatadogService, :model do
RSpec.describe Integrations::Datadog do
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
let_it_be(:build) { create(:ci_build, project: project) }
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe EmailsOnPushService do
RSpec.describe Integrations::EmailsOnPush do
let_it_be(:project) { create_default(:project).freeze }
describe 'Validations' do
......
......@@ -46,6 +46,7 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_one(:asana_service) }
it { is_expected.to have_many(:boards) }
it { is_expected.to have_one(:campfire_service) }
it { is_expected.to have_one(:datadog_service) }
it { is_expected.to have_one(:discord_service) }
it { is_expected.to have_one(:drone_ci_service) }
it { is_expected.to have_one(:emails_on_push_service) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment