Commit 63b344f1 authored by Nick Thomas's avatar Nick Thomas

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

Move 6 integrations to Integrations:: namespace

See merge request gitlab-org/gitlab!62265
parents da04a73c 2b881190
...@@ -1654,11 +1654,8 @@ Gitlab/NamespacedClass: ...@@ -1654,11 +1654,8 @@ Gitlab/NamespacedClass:
- 'app/models/project_services/ci_service.rb' - 'app/models/project_services/ci_service.rb'
- 'app/models/project_services/discord_service.rb' - 'app/models/project_services/discord_service.rb'
- 'app/models/project_services/drone_ci_service.rb' - 'app/models/project_services/drone_ci_service.rb'
- 'app/models/project_services/external_wiki_service.rb'
- 'app/models/project_services/flowdock_service.rb'
- 'app/models/project_services/hangouts_chat_service.rb' - 'app/models/project_services/hangouts_chat_service.rb'
- 'app/models/project_services/hipchat_service.rb' - 'app/models/project_services/hipchat_service.rb'
- 'app/models/project_services/irker_service.rb'
- 'app/models/project_services/issue_tracker_data.rb' - 'app/models/project_services/issue_tracker_data.rb'
- 'app/models/project_services/jenkins_service.rb' - 'app/models/project_services/jenkins_service.rb'
- 'app/models/project_services/jira_tracker_data.rb' - 'app/models/project_services/jira_tracker_data.rb'
...@@ -1669,9 +1666,6 @@ Gitlab/NamespacedClass: ...@@ -1669,9 +1666,6 @@ Gitlab/NamespacedClass:
- 'app/models/project_services/mock_monitoring_service.rb' - 'app/models/project_services/mock_monitoring_service.rb'
- 'app/models/project_services/monitoring_service.rb' - 'app/models/project_services/monitoring_service.rb'
- 'app/models/project_services/open_project_tracker_data.rb' - 'app/models/project_services/open_project_tracker_data.rb'
- 'app/models/project_services/packagist_service.rb'
- 'app/models/project_services/pipelines_email_service.rb'
- 'app/models/project_services/pivotaltracker_service.rb'
- 'app/models/project_services/prometheus_service.rb' - 'app/models/project_services/prometheus_service.rb'
- 'app/models/project_services/pushover_service.rb' - 'app/models/project_services/pushover_service.rb'
- 'app/models/project_services/slack_service.rb' - 'app/models/project_services/slack_service.rb'
......
# frozen_string_literal: true
module Integrations
class ExternalWiki < Integration
include ActionView::Helpers::UrlHelper
prop_accessor :external_wiki_url
validates :external_wiki_url, presence: true, public_url: true, if: :activated?
def title
s_('ExternalWikiService|External wiki')
end
def description
s_('ExternalWikiService|Link to an external wiki from the sidebar.')
end
def self.to_param
'external_wiki'
end
def fields
[
{
type: 'text',
name: 'external_wiki_url',
title: s_('ExternalWikiService|External wiki URL'),
placeholder: s_('ExternalWikiService|https://example.com/xxx/wiki/...'),
help: 'Enter the URL to the external wiki.',
required: true
}
]
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/wiki/index', anchor: 'link-an-external-wiki'), target: '_blank', rel: 'noopener noreferrer'
s_('Link an external wiki from the project\'s sidebar. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def execute(_data)
response = Gitlab::HTTP.get(properties['external_wiki_url'], verify: true)
response.body if response.code == 200
rescue StandardError
nil
end
def self.supported_events
%w()
end
end
end
# frozen_string_literal: true
module Integrations
class Flowdock < Integration
include ActionView::Helpers::UrlHelper
prop_accessor :token
validates :token, presence: true, if: :activated?
def title
'Flowdock'
end
def description
s_('FlowdockService|Send event notifications from GitLab to Flowdock flows.')
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('api/services', anchor: 'flowdock'), target: '_blank', rel: 'noopener noreferrer'
s_('FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'flowdock'
end
def fields
[
{ type: 'text', name: 'token', placeholder: s_('FlowdockService|1b609b52537...'), required: true, help: 'Enter your Flowdock token.' }
]
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
::Flowdock::Git.post(
data[:ref],
data[:before],
data[:after],
token: token,
repo: project.repository,
repo_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}",
commit_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/-/commit/%s",
diff_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/compare/%s...%s"
)
end
end
end
# frozen_string_literal: true
require 'uri'
module Integrations
class Irker < Integration
prop_accessor :server_host, :server_port, :default_irc_uri
prop_accessor :recipients, :channels
boolean_accessor :colorize_messages
validates :recipients, presence: true, if: :validate_recipients?
before_validation :get_channels
def title
'Irker (IRC gateway)'
end
def description
'Send IRC messages.'
end
def self.to_param
'irker'
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
IrkerWorker.perform_async(project_id, channels,
colorize_messages, data, settings)
end
def settings
{
server_host: server_host.presence || 'localhost',
server_port: server_port.presence || 6659
}
end
def fields
[
{ type: 'text', name: 'server_host', placeholder: 'localhost',
help: 'Irker daemon hostname (defaults to localhost)' },
{ type: 'text', name: 'server_port', placeholder: 6659,
help: 'Irker daemon port (defaults to 6659)' },
{ type: 'text', name: 'default_irc_uri', title: 'Default IRC URI',
help: 'A default IRC URI to prepend before each recipient (optional)',
placeholder: 'irc://irc.network.net:6697/' },
{ type: 'textarea', name: 'recipients',
placeholder: 'Recipients/channels separated by whitespaces', required: true,
help: 'Recipients have to be specified with a full URI: '\
'irc[s]://irc.network.net[:port]/#channel. Special cases: if '\
'you want the channel to be a nickname instead, append ",isnick" to ' \
'the channel name; if the channel is protected by a secret password, ' \
' append "?key=secretpassword" to the URI (Note that due to a bug, if you ' \
' want to use a password, you have to omit the "#" on the channel). If you ' \
' specify a default IRC URI to prepend before each recipient, you can just ' \
' give a channel name.' },
{ type: 'checkbox', name: 'colorize_messages' }
]
end
def help
' NOTE: Irker does NOT have built-in authentication, which makes it' \
' vulnerable to spamming IRC channels if it is hosted outside of a ' \
' firewall. Please make sure you run the daemon within a secured network ' \
' to prevent abuse. For more details, read: http://www.catb.org/~esr/irker/security.html.'
end
private
def get_channels
return true unless activated?
return true if recipients.nil? || recipients.empty?
map_recipients
errors.add(:recipients, 'are all invalid') if channels.empty?
true
end
def map_recipients
self.channels = recipients.split(/\s+/).map do |recipient|
format_channel(recipient)
end
channels.reject!(&:nil?)
end
def format_channel(recipient)
uri = nil
# Try to parse the chan as a full URI
begin
uri = consider_uri(URI.parse(recipient))
rescue URI::InvalidURIError
end
unless uri.present? && default_irc_uri.nil?
begin
new_recipient = URI.join(default_irc_uri, '/', recipient).to_s
uri = consider_uri(URI.parse(new_recipient))
rescue StandardError
log_error("Unable to create a valid URL", default_irc_uri: default_irc_uri, recipient: recipient)
end
end
uri
end
def consider_uri(uri)
return if uri.scheme.nil?
# Authorize both irc://domain.com/#chan and irc://domain.com/chan
if uri.is_a?(URI) && uri.scheme[/^ircs?\z/] && !uri.path.nil?
uri.to_s
end
end
end
end
# frozen_string_literal: true
module Integrations
class Packagist < Integration
prop_accessor :username, :token, :server
validates :username, presence: true, if: :activated?
validates :token, presence: true, if: :activated?
default_value_for :push_events, true
default_value_for :tag_push_events, true
after_save :compose_service_hook, if: :activated?
def title
'Packagist'
end
def description
s_('Integrations|Update your Packagist projects.')
end
def self.to_param
'packagist'
end
def fields
[
{ type: 'text', name: 'username', placeholder: '', required: true },
{ type: 'text', name: 'token', placeholder: '', required: true },
{ type: 'text', name: 'server', placeholder: 'https://packagist.org', required: false }
]
end
def self.supported_events
%w(push merge_request tag_push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
service_hook.execute(data)
end
def test(data)
begin
result = execute(data)
return { success: false, result: result[:message] } if result[:http_status] != 202
rescue StandardError => error
return { success: false, result: error }
end
{ success: true, result: result[:message] }
end
def compose_service_hook
hook = service_hook || build_service_hook
hook.url = hook_url
hook.save
end
def hook_url
base_url = server.presence || 'https://packagist.org'
"#{base_url}/api/update-package?username=#{username}&apiToken=#{token}"
end
end
end
# frozen_string_literal: true
module Integrations
class PipelinesEmail < Integration
include NotificationBranchSelection
prop_accessor :recipients, :branches_to_be_notified
boolean_accessor :notify_only_broken_pipelines, :notify_only_default_branch
validates :recipients, presence: true, if: :validate_recipients?
def initialize_properties
if properties.nil?
self.properties = {}
self.notify_only_broken_pipelines = true
self.branches_to_be_notified = "default"
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 title
_('Pipeline status emails')
end
def description
_('Email the pipeline status to a list of recipients.')
end
def self.to_param
'pipelines_email'
end
def self.supported_events
%w[pipeline]
end
def self.default_test_event
'pipeline'
end
def execute(data, force: false)
return unless supported_events.include?(data[:object_kind])
return unless force || should_pipeline_be_notified?(data)
all_recipients = retrieve_recipients(data)
return unless all_recipients.any?
pipeline_id = data[:object_attributes][:id]
PipelineNotificationWorker.new.perform(pipeline_id, recipients: all_recipients)
end
def can_test?
project&.ci_pipelines&.any?
end
def fields
[
{ type: 'textarea',
name: 'recipients',
help: _('Comma-separated list of email addresses.'),
required: true },
{ type: 'checkbox',
name: 'notify_only_broken_pipelines' },
{ type: 'select',
name: 'branches_to_be_notified',
choices: branch_choices }
]
end
def test(data)
result = execute(data, force: true)
{ success: true, result: result }
rescue StandardError => error
{ success: false, result: error }
end
def should_pipeline_be_notified?(data)
notify_for_branch?(data) && notify_for_pipeline?(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
def retrieve_recipients(data)
recipients.to_s.split(/[,\r\n ]+/).reject(&:empty?)
end
end
end
# frozen_string_literal: true
module Integrations
class Pivotaltracker < Integration
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'
prop_accessor :token, :restrict_to_branch
validates :token, presence: true, if: :activated?
def title
'PivotalTracker'
end
def description
s_('PivotalTrackerService|Add commit messages as comments to PivotalTracker stories.')
end
def self.to_param
'pivotaltracker'
end
def fields
[
{
type: 'text',
name: 'token',
placeholder: s_('PivotalTrackerService|Pivotal Tracker API token.'),
required: true
},
{
type: 'text',
name: 'restrict_to_branch',
placeholder: s_('PivotalTrackerService|Comma-separated list of branches which will be ' \
'automatically inspected. Leave blank to include all branches.')
}
]
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
return unless allowed_branch?(data[:ref])
data[:commits].each do |commit|
message = {
'source_commit' => {
'commit_id' => commit[:id],
'author' => commit[:author][:name],
'url' => commit[:url],
'message' => commit[:message]
}
}
Gitlab::HTTP.post(
API_ENDPOINT,
body: message.to_json,
headers: {
'Content-Type' => 'application/json',
'X-TrackerToken' => token
}
)
end
end
private
def allowed_branch?(ref)
return true unless ref.present? && restrict_to_branch.present?
branch = Gitlab::Git.ref_name(ref)
allowed_branches = restrict_to_branch.split(',').map(&:strip)
branch.present? && allowed_branches.include?(branch)
end
end
end
...@@ -193,15 +193,17 @@ class Project < ApplicationRecord ...@@ -193,15 +193,17 @@ class Project < ApplicationRecord
has_one :datadog_service, class_name: 'Integrations::Datadog' has_one :datadog_service, class_name: 'Integrations::Datadog'
has_one :emails_on_push_service, class_name: 'Integrations::EmailsOnPush' has_one :emails_on_push_service, class_name: 'Integrations::EmailsOnPush'
has_one :ewm_service, class_name: 'Integrations::Ewm' 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 :irker_service, class_name: 'Integrations::Irker'
has_one :jira_service, class_name: 'Integrations::Jira' has_one :jira_service, class_name: 'Integrations::Jira'
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 :redmine_service, class_name: 'Integrations::Redmine'
has_one :youtrack_service, class_name: 'Integrations::Youtrack' has_one :youtrack_service, class_name: 'Integrations::Youtrack'
has_one :discord_service has_one :discord_service
has_one :drone_ci_service has_one :drone_ci_service
has_one :pipelines_email_service
has_one :irker_service
has_one :pivotaltracker_service
has_one :flowdock_service
has_one :mattermost_slash_commands_service has_one :mattermost_slash_commands_service
has_one :mattermost_service has_one :mattermost_service
has_one :slack_slash_commands_service has_one :slack_slash_commands_service
...@@ -210,12 +212,10 @@ class Project < ApplicationRecord ...@@ -210,12 +212,10 @@ class Project < ApplicationRecord
has_one :teamcity_service has_one :teamcity_service
has_one :pushover_service has_one :pushover_service
has_one :jenkins_service has_one :jenkins_service
has_one :external_wiki_service
has_one :prometheus_service, inverse_of: :project has_one :prometheus_service, inverse_of: :project
has_one :mock_ci_service has_one :mock_ci_service
has_one :mock_monitoring_service has_one :mock_monitoring_service
has_one :microsoft_teams_service has_one :microsoft_teams_service
has_one :packagist_service
has_one :hangouts_chat_service has_one :hangouts_chat_service
has_one :unify_circuit_service has_one :unify_circuit_service
has_one :webex_teams_service has_one :webex_teams_service
......
# frozen_string_literal: true
class ExternalWikiService < Integration
include ActionView::Helpers::UrlHelper
prop_accessor :external_wiki_url
validates :external_wiki_url, presence: true, public_url: true, if: :activated?
def title
s_('ExternalWikiService|External wiki')
end
def description
s_('ExternalWikiService|Link to an external wiki from the sidebar.')
end
def self.to_param
'external_wiki'
end
def fields
[
{
type: 'text',
name: 'external_wiki_url',
title: s_('ExternalWikiService|External wiki URL'),
placeholder: s_('ExternalWikiService|https://example.com/xxx/wiki/...'),
help: 'Enter the URL to the external wiki.',
required: true
}
]
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/wiki/index', anchor: 'link-an-external-wiki'), target: '_blank', rel: 'noopener noreferrer'
s_('Link an external wiki from the project\'s sidebar. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def execute(_data)
response = Gitlab::HTTP.get(properties['external_wiki_url'], verify: true)
response.body if response.code == 200
rescue StandardError
nil
end
def self.supported_events
%w()
end
end
# frozen_string_literal: true
class FlowdockService < Integration
include ActionView::Helpers::UrlHelper
prop_accessor :token
validates :token, presence: true, if: :activated?
def title
'Flowdock'
end
def description
s_('FlowdockService|Send event notifications from GitLab to Flowdock flows.')
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('api/services', anchor: 'flowdock'), target: '_blank', rel: 'noopener noreferrer'
s_('FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'flowdock'
end
def fields
[
{ type: 'text', name: 'token', placeholder: s_('FlowdockService|1b609b52537...'), required: true, help: 'Enter your Flowdock token.' }
]
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
Flowdock::Git.post(
data[:ref],
data[:before],
data[:after],
token: token,
repo: project.repository,
repo_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}",
commit_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/-/commit/%s",
diff_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/compare/%s...%s"
)
end
end
# frozen_string_literal: true
require 'uri'
class IrkerService < Integration
prop_accessor :server_host, :server_port, :default_irc_uri
prop_accessor :recipients, :channels
boolean_accessor :colorize_messages
validates :recipients, presence: true, if: :validate_recipients?
before_validation :get_channels
def title
'Irker (IRC gateway)'
end
def description
'Send IRC messages.'
end
def self.to_param
'irker'
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
IrkerWorker.perform_async(project_id, channels,
colorize_messages, data, settings)
end
def settings
{
server_host: server_host.presence || 'localhost',
server_port: server_port.presence || 6659
}
end
def fields
[
{ type: 'text', name: 'server_host', placeholder: 'localhost',
help: 'Irker daemon hostname (defaults to localhost)' },
{ type: 'text', name: 'server_port', placeholder: 6659,
help: 'Irker daemon port (defaults to 6659)' },
{ type: 'text', name: 'default_irc_uri', title: 'Default IRC URI',
help: 'A default IRC URI to prepend before each recipient (optional)',
placeholder: 'irc://irc.network.net:6697/' },
{ type: 'textarea', name: 'recipients',
placeholder: 'Recipients/channels separated by whitespaces', required: true,
help: 'Recipients have to be specified with a full URI: '\
'irc[s]://irc.network.net[:port]/#channel. Special cases: if '\
'you want the channel to be a nickname instead, append ",isnick" to ' \
'the channel name; if the channel is protected by a secret password, ' \
' append "?key=secretpassword" to the URI (Note that due to a bug, if you ' \
' want to use a password, you have to omit the "#" on the channel). If you ' \
' specify a default IRC URI to prepend before each recipient, you can just ' \
' give a channel name.' },
{ type: 'checkbox', name: 'colorize_messages' }
]
end
def help
' NOTE: Irker does NOT have built-in authentication, which makes it' \
' vulnerable to spamming IRC channels if it is hosted outside of a ' \
' firewall. Please make sure you run the daemon within a secured network ' \
' to prevent abuse. For more details, read: http://www.catb.org/~esr/irker/security.html.'
end
private
def get_channels
return true unless activated?
return true if recipients.nil? || recipients.empty?
map_recipients
errors.add(:recipients, 'are all invalid') if channels.empty?
true
end
def map_recipients
self.channels = recipients.split(/\s+/).map do |recipient|
format_channel(recipient)
end
channels.reject!(&:nil?)
end
def format_channel(recipient)
uri = nil
# Try to parse the chan as a full URI
begin
uri = consider_uri(URI.parse(recipient))
rescue URI::InvalidURIError
end
unless uri.present? && default_irc_uri.nil?
begin
new_recipient = URI.join(default_irc_uri, '/', recipient).to_s
uri = consider_uri(URI.parse(new_recipient))
rescue StandardError
log_error("Unable to create a valid URL", default_irc_uri: default_irc_uri, recipient: recipient)
end
end
uri
end
def consider_uri(uri)
return if uri.scheme.nil?
# Authorize both irc://domain.com/#chan and irc://domain.com/chan
if uri.is_a?(URI) && uri.scheme[/^ircs?\z/] && !uri.path.nil?
uri.to_s
end
end
end
# frozen_string_literal: true
class PackagistService < Integration
prop_accessor :username, :token, :server
validates :username, presence: true, if: :activated?
validates :token, presence: true, if: :activated?
default_value_for :push_events, true
default_value_for :tag_push_events, true
after_save :compose_service_hook, if: :activated?
def title
'Packagist'
end
def description
s_('Integrations|Update your Packagist projects.')
end
def self.to_param
'packagist'
end
def fields
[
{ type: 'text', name: 'username', placeholder: '', required: true },
{ type: 'text', name: 'token', placeholder: '', required: true },
{ type: 'text', name: 'server', placeholder: 'https://packagist.org', required: false }
]
end
def self.supported_events
%w(push merge_request tag_push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
service_hook.execute(data)
end
def test(data)
begin
result = execute(data)
return { success: false, result: result[:message] } if result[:http_status] != 202
rescue StandardError => error
return { success: false, result: error }
end
{ success: true, result: result[:message] }
end
def compose_service_hook
hook = service_hook || build_service_hook
hook.url = hook_url
hook.save
end
def hook_url
base_url = server.presence || 'https://packagist.org'
"#{base_url}/api/update-package?username=#{username}&apiToken=#{token}"
end
end
# frozen_string_literal: true
class PipelinesEmailService < Integration
include NotificationBranchSelection
prop_accessor :recipients, :branches_to_be_notified
boolean_accessor :notify_only_broken_pipelines, :notify_only_default_branch
validates :recipients, presence: true, if: :validate_recipients?
def initialize_properties
if properties.nil?
self.properties = {}
self.notify_only_broken_pipelines = true
self.branches_to_be_notified = "default"
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 title
_('Pipeline status emails')
end
def description
_('Email the pipeline status to a list of recipients.')
end
def self.to_param
'pipelines_email'
end
def self.supported_events
%w[pipeline]
end
def self.default_test_event
'pipeline'
end
def execute(data, force: false)
return unless supported_events.include?(data[:object_kind])
return unless force || should_pipeline_be_notified?(data)
all_recipients = retrieve_recipients(data)
return unless all_recipients.any?
pipeline_id = data[:object_attributes][:id]
PipelineNotificationWorker.new.perform(pipeline_id, recipients: all_recipients)
end
def can_test?
project&.ci_pipelines&.any?
end
def fields
[
{ type: 'textarea',
name: 'recipients',
help: _('Comma-separated list of email addresses.'),
required: true },
{ type: 'checkbox',
name: 'notify_only_broken_pipelines' },
{ type: 'select',
name: 'branches_to_be_notified',
choices: branch_choices }
]
end
def test(data)
result = execute(data, force: true)
{ success: true, result: result }
rescue StandardError => error
{ success: false, result: error }
end
def should_pipeline_be_notified?(data)
notify_for_branch?(data) && notify_for_pipeline?(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
def retrieve_recipients(data)
recipients.to_s.split(/[,\r\n ]+/).reject(&:empty?)
end
end
# frozen_string_literal: true
class PivotaltrackerService < Integration
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'
prop_accessor :token, :restrict_to_branch
validates :token, presence: true, if: :activated?
def title
'PivotalTracker'
end
def description
s_('PivotalTrackerService|Add commit messages as comments to PivotalTracker stories.')
end
def self.to_param
'pivotaltracker'
end
def fields
[
{
type: 'text',
name: 'token',
placeholder: s_('PivotalTrackerService|Pivotal Tracker API token.'),
required: true
},
{
type: 'text',
name: 'restrict_to_branch',
placeholder: s_('PivotalTrackerService|Comma-separated list of branches which will be ' \
'automatically inspected. Leave blank to include all branches.')
}
]
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
return unless allowed_branch?(data[:ref])
data[:commits].each do |commit|
message = {
'source_commit' => {
'commit_id' => commit[:id],
'author' => commit[:author][:name],
'url' => commit[:url],
'message' => commit[:message]
}
}
Gitlab::HTTP.post(
API_ENDPOINT,
body: message.to_json,
headers: {
'Content-Type' => 'application/json',
'X-TrackerToken' => token
}
)
end
end
private
def allowed_branch?(ref)
return true unless ref.present? && restrict_to_branch.present?
branch = Gitlab::Git.ref_name(ref)
allowed_branches = restrict_to_branch.split(',').map(&:strip)
branch.present? && allowed_branches.include?(branch)
end
end
...@@ -784,22 +784,22 @@ module API ...@@ -784,22 +784,22 @@ module API
::Integrations::Datadog, ::Integrations::Datadog,
::Integrations::EmailsOnPush, ::Integrations::EmailsOnPush,
::Integrations::Ewm, ::Integrations::Ewm,
::Integrations::ExternalWiki,
::Integrations::Flowdock,
::Integrations::Irker,
::Integrations::Jira, ::Integrations::Jira,
::Integrations::Packagist,
::Integrations::PipelinesEmail,
::Integrations::Pivotaltracker,
::Integrations::Redmine, ::Integrations::Redmine,
::Integrations::Youtrack, ::Integrations::Youtrack,
::BuildkiteService, ::BuildkiteService,
::DiscordService, ::DiscordService,
::DroneCiService, ::DroneCiService,
::ExternalWikiService,
::FlowdockService,
::HangoutsChatService, ::HangoutsChatService,
::IrkerService,
::JenkinsService, ::JenkinsService,
::MattermostSlashCommandsService, ::MattermostSlashCommandsService,
::SlackSlashCommandsService, ::SlackSlashCommandsService,
::PackagistService,
::PipelinesEmailService,
::PivotaltrackerService,
::PrometheusService, ::PrometheusService,
::PushoverService, ::PushoverService,
::SlackService, ::SlackService,
......
...@@ -34,7 +34,7 @@ module Flowdock ...@@ -34,7 +34,7 @@ module Flowdock
# Send git push notification to Flowdock # Send git push notification to Flowdock
def post def post
messages.each do |message| messages.each do |message|
Flowdock::Client.new(flow_token: @token).post_to_thread(message) ::Flowdock::Client.new(flow_token: @token).post_to_thread(message)
end end
end end
......
...@@ -5,7 +5,8 @@ module Gitlab ...@@ -5,7 +5,8 @@ module Gitlab
class StiType < ActiveRecord::Type::String class StiType < ActiveRecord::Type::String
NAMESPACED_INTEGRATIONS = Set.new(%w( NAMESPACED_INTEGRATIONS = Set.new(%w(
Asana Assembla Bamboo Bugzilla Campfire Confluence CustomIssueTracker Datadog Asana Assembla Bamboo Bugzilla Campfire Confluence CustomIssueTracker Datadog
EmailsOnPush Ewm IssueTracker Jira Redmine Youtrack EmailsOnPush Ewm ExternalWiki Flowdock IssueTracker Irker Jira Packagist PipelinesEmail
Pivotaltracker Redmine Youtrack
)).freeze )).freeze
def cast(value) def cast(value)
......
...@@ -127,9 +127,9 @@ FactoryBot.define do ...@@ -127,9 +127,9 @@ FactoryBot.define do
end end
end end
factory :external_wiki_service do factory :external_wiki_service, class: 'Integrations::ExternalWiki' do
project project
type { ExternalWikiService } type { 'ExternalWikiService' }
active { true } active { true }
external_wiki_url { 'http://external-wiki-url.com' } external_wiki_url { 'http://external-wiki-url.com' }
end end
...@@ -167,7 +167,7 @@ FactoryBot.define do ...@@ -167,7 +167,7 @@ FactoryBot.define do
type { 'SlackService' } type { 'SlackService' }
end end
factory :pipelines_email_service do factory :pipelines_email_service, class: 'Integrations::PipelinesEmail' do
project project
active { true } active { true }
type { 'PipelinesEmailService' } type { 'PipelinesEmailService' }
......
...@@ -905,7 +905,7 @@ RSpec.describe Integration do ...@@ -905,7 +905,7 @@ RSpec.describe Integration do
with_them do with_them do
it 'returns the right result' do it 'returns the right result' do
expect(build(:service, type: type, active: active).external_wiki?).to eq(result) expect(create(:service, type: type, active: active).external_wiki?).to eq(result)
end end
end end
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe ExternalWikiService do RSpec.describe Integrations::ExternalWiki do
describe "Associations" do describe "Associations" do
it { is_expected.to belong_to :project } it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook } it { is_expected.to have_one :service_hook }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe FlowdockService do RSpec.describe Integrations::Flowdock do
describe "Associations" do describe "Associations" do
it { is_expected.to belong_to :project } it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook } it { is_expected.to have_one :service_hook }
......
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
require 'socket' require 'socket'
require 'json' require 'json'
RSpec.describe IrkerService do RSpec.describe Integrations::Irker do
describe 'Associations' do describe 'Associations' do
it { is_expected.to belong_to :project } it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook } it { is_expected.to have_one :service_hook }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe PackagistService do RSpec.describe Integrations::Packagist do
let(:packagist_params) do let(:packagist_params) do
{ {
active: true, active: true,
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe PipelinesEmailService, :mailer do RSpec.describe Integrations::PipelinesEmail, :mailer do
let(:pipeline) do let(:pipeline) do
create(:ci_pipeline, :failed, create(:ci_pipeline, :failed,
project: project, project: project,
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe PivotaltrackerService do RSpec.describe Integrations::Pivotaltracker do
include StubRequests include StubRequests
describe 'Associations' do describe 'Associations' do
...@@ -35,7 +35,7 @@ RSpec.describe PivotaltrackerService do ...@@ -35,7 +35,7 @@ RSpec.describe PivotaltrackerService do
end end
end end
let(:url) { PivotaltrackerService::API_ENDPOINT } let(:url) { described_class::API_ENDPOINT }
def push_data(branch: 'master') def push_data(branch: 'master')
{ {
......
...@@ -1158,7 +1158,7 @@ RSpec.describe Project, factory_default: :keep do ...@@ -1158,7 +1158,7 @@ RSpec.describe Project, factory_default: :keep do
it 'returns an active external wiki' do it 'returns an active external wiki' do
create(:service, project: project, type: 'ExternalWikiService', active: true) create(:service, project: project, type: 'ExternalWikiService', active: true)
is_expected.to be_kind_of(ExternalWikiService) is_expected.to be_kind_of(Integrations::ExternalWiki)
end end
it 'does not return an inactive external wiki' do it 'does not return an inactive external wiki' do
......
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