summaryrefslogtreecommitdiff
path: root/app/models/project_services
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/project_services')
-rw-r--r--app/models/project_services/bugzilla_service.rb24
-rw-r--r--app/models/project_services/buildkite_service.rb143
-rw-r--r--app/models/project_services/chat_notification_service.rb252
-rw-r--r--app/models/project_services/ci_service.rb42
-rw-r--r--app/models/project_services/custom_issue_tracker_service.rb23
-rw-r--r--app/models/project_services/data_fields.rb59
-rw-r--r--app/models/project_services/discord_service.rb66
-rw-r--r--app/models/project_services/drone_ci_service.rb104
-rw-r--r--app/models/project_services/ewm_service.rb36
-rw-r--r--app/models/project_services/external_wiki_service.rb50
-rw-r--r--app/models/project_services/flowdock_service.rb50
-rw-r--r--app/models/project_services/hangouts_chat_service.rb71
-rw-r--r--app/models/project_services/hipchat_service.rb32
-rw-r--r--app/models/project_services/irker_service.rb121
-rw-r--r--app/models/project_services/issue_tracker_data.rb9
-rw-r--r--app/models/project_services/issue_tracker_service.rb152
-rw-r--r--app/models/project_services/jenkins_service.rb111
-rw-r--r--app/models/project_services/jira_service.rb541
-rw-r--r--app/models/project_services/jira_tracker_data.rb24
-rw-r--r--app/models/project_services/mattermost_service.rb31
-rw-r--r--app/models/project_services/mattermost_slash_commands_service.rb57
-rw-r--r--app/models/project_services/microsoft_teams_service.rb57
-rw-r--r--app/models/project_services/mock_ci_service.rb90
-rw-r--r--app/models/project_services/open_project_service.rb18
-rw-r--r--app/models/project_services/open_project_tracker_data.rb16
-rw-r--r--app/models/project_services/packagist_service.rb65
-rw-r--r--app/models/project_services/pipelines_email_service.rb103
-rw-r--r--app/models/project_services/pivotaltracker_service.rb76
-rw-r--r--app/models/project_services/prometheus_service.rb4
-rw-r--r--app/models/project_services/pushover_service.rb105
-rw-r--r--app/models/project_services/redmine_service.rb23
-rw-r--r--app/models/project_services/slack_mattermost/notifier.rb24
-rw-r--r--app/models/project_services/slack_service.rb57
-rw-r--r--app/models/project_services/slack_slash_commands_service.rb34
-rw-r--r--app/models/project_services/slash_commands_service.rb65
-rw-r--r--app/models/project_services/teamcity_service.rb189
-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/models/project_services/youtrack_service.rb40
39 files changed, 2 insertions, 3076 deletions
diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb
deleted file mode 100644
index d1c56d2a4d5..00000000000
--- a/app/models/project_services/bugzilla_service.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-class BugzillaService < IssueTrackerService
- include ActionView::Helpers::UrlHelper
-
- validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
-
- def title
- 'Bugzilla'
- end
-
- def description
- s_("IssueTracker|Use Bugzilla as this project's issue tracker.")
- end
-
- def help
- docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/bugzilla'), target: '_blank', rel: 'noopener noreferrer'
- s_("IssueTracker|Use Bugzilla as this project's issue tracker. %{docs_link}").html_safe % { docs_link: docs_link.html_safe }
- end
-
- def self.to_param
- 'bugzilla'
- end
-end
diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb
deleted file mode 100644
index f2ea5066e37..00000000000
--- a/app/models/project_services/buildkite_service.rb
+++ /dev/null
@@ -1,143 +0,0 @@
-# frozen_string_literal: true
-
-require "addressable/uri"
-
-class BuildkiteService < CiService
- include ReactiveService
-
- ENDPOINT = "https://buildkite.com"
-
- prop_accessor :project_url, :token
-
- validates :project_url, presence: true, public_url: true, if: :activated?
- validates :token, presence: true, if: :activated?
-
- after_save :compose_service_hook, if: :activated?
-
- def self.supported_events
- %w(push merge_request tag_push)
- end
-
- # This is a stub method to work with deprecated API response
- # TODO: remove enable_ssl_verification after 14.0
- # https://gitlab.com/gitlab-org/gitlab/-/issues/222808
- def enable_ssl_verification
- true
- end
-
- # Since SSL verification will always be enabled for Buildkite,
- # we no longer needs to store the boolean.
- # This is a stub method to work with deprecated API param.
- # TODO: remove enable_ssl_verification after 14.0
- # https://gitlab.com/gitlab-org/gitlab/-/issues/222808
- def enable_ssl_verification=(_value)
- self.properties.delete('enable_ssl_verification') # Remove unused key
- end
-
- def webhook_url
- "#{buildkite_endpoint('webhook')}/deliver/#{webhook_token}"
- end
-
- def compose_service_hook
- hook = service_hook || build_service_hook
- hook.url = webhook_url
- hook.enable_ssl_verification = true
- hook.save
- end
-
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- service_hook.execute(data)
- end
-
- def commit_status(sha, ref)
- with_reactive_cache(sha, ref) {|cached| cached[:commit_status] }
- end
-
- def commit_status_path(sha)
- "#{buildkite_endpoint('gitlab')}/status/#{status_token}.json?commit=#{sha}"
- end
-
- def build_page(sha, ref)
- "#{project_url}/builds?commit=#{sha}"
- end
-
- def title
- 'Buildkite'
- end
-
- def description
- 'Run CI/CD pipelines with Buildkite.'
- end
-
- def self.to_param
- 'buildkite'
- end
-
- def fields
- [
- { type: 'text',
- name: 'token',
- title: 'Integration Token',
- help: 'This token will be provided when you create a Buildkite pipeline with a GitLab repository',
- required: true },
-
- { type: 'text',
- name: 'project_url',
- title: 'Pipeline URL',
- placeholder: "#{ENDPOINT}/acme-inc/test-pipeline",
- required: true }
- ]
- end
-
- def calculate_reactive_cache(sha, ref)
- response = Gitlab::HTTP.try_get(commit_status_path(sha), request_options)
-
- status =
- if response&.code == 200 && response['status']
- response['status']
- else
- :error
- end
-
- { commit_status: status }
- end
-
- private
-
- def webhook_token
- token_parts.first
- end
-
- def status_token
- token_parts.second
- end
-
- def token_parts
- if token.present?
- token.split(':')
- else
- []
- end
- end
-
- def buildkite_endpoint(subdomain = nil)
- if subdomain.present?
- uri = Addressable::URI.parse(ENDPOINT)
- new_endpoint = "#{uri.scheme || 'http'}://#{subdomain}.#{uri.host}"
-
- if uri.port.present?
- "#{new_endpoint}:#{uri.port}"
- else
- new_endpoint
- end
- else
- ENDPOINT
- end
- end
-
- def request_options
- { verify: false, extra_log_info: { project_id: project_id } }
- end
-end
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 2f841bf903e..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 notify_label?(data)
-
- return unless webhook.present?
-
- object_kind = data[:object_kind]
-
- data = custom_data(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.dig(:issue, :labels) || data.dig(:merge_request, :labels)
-
- return false if labels.nil?
-
- 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)
- 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/ci_service.rb b/app/models/project_services/ci_service.rb
deleted file mode 100644
index 0733da761d5..00000000000
--- a/app/models/project_services/ci_service.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-# frozen_string_literal: true
-
-# Base class for CI services
-# List methods you need to implement to get your CI service
-# working with GitLab merge requests
-class CiService < Integration
- default_value_for :category, 'ci'
-
- def valid_token?(token)
- self.respond_to?(:token) && self.token.present? && ActiveSupport::SecurityUtils.secure_compare(token, self.token)
- end
-
- def self.supported_events
- %w(push)
- end
-
- # Return complete url to build page
- #
- # Ex.
- # http://jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c
- #
- def build_page(sha, ref)
- # implement inside child
- end
-
- # Return string with build status or :error symbol
- #
- # Allowed states: 'success', 'failed', 'running', 'pending', 'skipped'
- #
- #
- # Ex.
- # @service.commit_status('13be4ac', 'master')
- # # => 'success'
- #
- # @service.commit_status('2abe4ac', 'dev')
- # # => 'running'
- #
- #
- def commit_status(sha, ref)
- # implement inside child
- end
-end
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
deleted file mode 100644
index 6f99d104904..00000000000
--- a/app/models/project_services/custom_issue_tracker_service.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-class CustomIssueTrackerService < IssueTrackerService
- include ActionView::Helpers::UrlHelper
- validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
-
- def title
- s_('IssueTracker|Custom issue tracker')
- end
-
- def description
- s_("IssueTracker|Use a custom issue tracker as this project's issue tracker.")
- end
-
- def help
- docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/custom_issue_tracker'), target: '_blank', rel: 'noopener noreferrer'
- s_('IssueTracker|Use a custom issue tracker that is not in the integration list. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
- end
-
- def self.to_param
- 'custom_issue_tracker'
- end
-end
diff --git a/app/models/project_services/data_fields.rb b/app/models/project_services/data_fields.rb
deleted file mode 100644
index ca4dc0375fb..00000000000
--- a/app/models/project_services/data_fields.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: true
-
-module DataFields
- extend ActiveSupport::Concern
-
- class_methods do
- # Provide convenient accessor methods for data fields.
- # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
- def data_field(*args)
- args.each do |arg|
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- unless method_defined?(arg)
- def #{arg}
- data_fields.send('#{arg}') || (properties && properties['#{arg}'])
- end
- end
-
- def #{arg}=(value)
- @old_data_fields ||= {}
- @old_data_fields['#{arg}'] ||= #{arg} # set only on the first assignment, IOW we remember the original value only
- data_fields.send('#{arg}=', value)
- end
-
- def #{arg}_touched?
- @old_data_fields ||= {}
- @old_data_fields.has_key?('#{arg}')
- end
-
- def #{arg}_changed?
- #{arg}_touched? && @old_data_fields['#{arg}'] != #{arg}
- end
-
- def #{arg}_was
- return unless #{arg}_touched?
- return if data_fields.persisted? # arg_was does not work for attr_encrypted
-
- legacy_properties_data['#{arg}']
- end
- RUBY
- end
- end
- end
-
- included do
- has_one :issue_tracker_data, autosave: true, inverse_of: :integration, foreign_key: :service_id
- has_one :jira_tracker_data, autosave: true, inverse_of: :integration, foreign_key: :service_id
- has_one :open_project_tracker_data, autosave: true, inverse_of: :integration, foreign_key: :service_id
-
- def data_fields
- raise NotImplementedError
- end
-
- def data_fields_present?
- data_fields.present?
- rescue NotImplementedError
- 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/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
deleted file mode 100644
index ab1ba768a8f..00000000000
--- a/app/models/project_services/drone_ci_service.rb
+++ /dev/null
@@ -1,104 +0,0 @@
-# frozen_string_literal: true
-
-class DroneCiService < CiService
- include ReactiveService
- include ServicePushDataValidations
-
- prop_accessor :drone_url, :token
- boolean_accessor :enable_ssl_verification
-
- validates :drone_url, presence: true, public_url: true, if: :activated?
- validates :token, presence: true, if: :activated?
-
- after_save :compose_service_hook, if: :activated?
-
- def compose_service_hook
- hook = service_hook || build_service_hook
- # If using a service template, project may not be available
- hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.full_path}", "&name=#{project.path}", "&access_token=#{token}"].join if project
- hook.enable_ssl_verification = !!enable_ssl_verification
- hook.save
- end
-
- def execute(data)
- case data[:object_kind]
- when 'push'
- service_hook.execute(data) if push_valid?(data)
- when 'merge_request'
- service_hook.execute(data) if merge_request_valid?(data)
- when 'tag_push'
- service_hook.execute(data) if tag_push_valid?(data)
- end
- end
-
- def allow_target_ci?
- true
- end
-
- def self.supported_events
- %w(push merge_request tag_push)
- end
-
- def commit_status_path(sha, ref)
- Gitlab::Utils.append_path(
- drone_url,
- "gitlab/#{project.full_path}/commits/#{sha}?branch=#{Addressable::URI.encode_component(ref.to_s)}&access_token=#{token}")
- end
-
- def commit_status(sha, ref)
- with_reactive_cache(sha, ref) { |cached| cached[:commit_status] }
- end
-
- def calculate_reactive_cache(sha, ref)
- response = Gitlab::HTTP.try_get(commit_status_path(sha, ref),
- verify: enable_ssl_verification,
- extra_log_info: { project_id: project_id })
-
- status =
- if response && response.code == 200 && response['status']
- case response['status']
- when 'killed'
- :canceled
- when 'failure', 'error'
- # Because drone return error if some test env failed
- :failed
- else
- response["status"]
- end
- else
- :error
- end
-
- { commit_status: status }
- end
-
- def build_page(sha, ref)
- Gitlab::Utils.append_path(
- drone_url,
- "gitlab/#{project.full_path}/redirect/commits/#{sha}?branch=#{Addressable::URI.encode_component(ref.to_s)}")
- end
-
- def title
- 'Drone'
- end
-
- def description
- s_('ProjectService|Run CI/CD pipelines with Drone.')
- end
-
- def self.to_param
- 'drone_ci'
- end
-
- def help
- s_('ProjectService|Run CI/CD pipelines with Drone.')
- end
-
- def fields
- [
- { type: 'text', name: 'token', help: s_('ProjectService|Token for the Drone project.'), required: true },
- { type: 'text', name: 'drone_url', title: s_('ProjectService|Drone server URL'), placeholder: 'http://drone.example.com', required: true },
- { type: 'checkbox', name: 'enable_ssl_verification', title: "Enable SSL verification" }
- ]
- end
-end
diff --git a/app/models/project_services/ewm_service.rb b/app/models/project_services/ewm_service.rb
deleted file mode 100644
index 90fcbb10d2b..00000000000
--- a/app/models/project_services/ewm_service.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-class EwmService < IssueTrackerService
- include ActionView::Helpers::UrlHelper
-
- validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
-
- def self.reference_pattern(only_long: true)
- @reference_pattern ||= %r{(?<issue>\b(bug|task|work item|workitem|rtcwi|defect)\b\s+\d+)}i
- end
-
- def title
- 'EWM'
- end
-
- def description
- s_("IssueTracker|Use IBM Engineering Workflow Management as this project's issue tracker.")
- end
-
- def help
- docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/ewm'), target: '_blank', rel: 'noopener noreferrer'
- s_("IssueTracker|Use IBM Engineering Workflow Management as this project's issue tracker. %{docs_link}").html_safe % { docs_link: docs_link.html_safe }
- end
-
- def self.to_param
- 'ewm'
- end
-
- def can_test?
- false
- end
-
- def issue_url(iid)
- issues_url.gsub(':id', iid.to_s.split(' ')[-1])
- end
-end
diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb
deleted file mode 100644
index f49b008533d..00000000000
--- a/app/models/project_services/external_wiki_service.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-# 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
diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb
deleted file mode 100644
index 7aae5af7454..00000000000
--- a/app/models/project_services/flowdock_service.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-# 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
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/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
deleted file mode 100644
index 71d8e7bfac4..00000000000
--- a/app/models/project_services/hipchat_service.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-# This service is scheduled for removal. All records must
-# be deleted before the class can be removed.
-# https://gitlab.com/gitlab-org/gitlab/-/issues/27954
-class HipchatService < Integration
- before_save :prevent_save
-
- def self.to_param
- 'hipchat'
- end
-
- def self.supported_events
- []
- end
-
- def execute(data)
- # We removed the hipchat gem due to https://gitlab.com/gitlab-org/gitlab/-/issues/325851#note_537143149
- # HipChat is unusable anyway, so do nothing in this method
- end
-
- private
-
- def prevent_save
- errors.add(:base, _('HipChat endpoint is deprecated and should not be created or modified.'))
-
- # Stops execution of callbacks and database operation while
- # preserving expectations of #save (will not raise) & #save! (raises)
- # https://guides.rubyonrails.org/active_record_callbacks.html#halting-execution
- throw :abort # rubocop:disable Cop/BanCatchThrow
- end
-end
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
deleted file mode 100644
index 5cca620c659..00000000000
--- a/app/models/project_services/irker_service.rb
+++ /dev/null
@@ -1,121 +0,0 @@
-# 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
diff --git a/app/models/project_services/issue_tracker_data.rb b/app/models/project_services/issue_tracker_data.rb
deleted file mode 100644
index 414f2c1da4d..00000000000
--- a/app/models/project_services/issue_tracker_data.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-class IssueTrackerData < ApplicationRecord
- include Services::DataFields
-
- attr_encrypted :project_url, encryption_options
- attr_encrypted :issues_url, encryption_options
- attr_encrypted :new_issue_url, encryption_options
-end
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
deleted file mode 100644
index 099e3c336dd..00000000000
--- a/app/models/project_services/issue_tracker_service.rb
+++ /dev/null
@@ -1,152 +0,0 @@
-# frozen_string_literal: true
-
-class IssueTrackerService < Integration
- validate :one_issue_tracker, if: :activated?, on: :manual_change
-
- # TODO: we can probably just delegate as part of
- # https://gitlab.com/gitlab-org/gitlab/issues/29404
- data_field :project_url, :issues_url, :new_issue_url
-
- default_value_for :category, 'issue_tracker'
-
- before_validation :handle_properties
- before_validation :set_default_data, on: :create
-
- # Pattern used to extract links from comments
- # Override this method on services that uses different patterns
- # This pattern does not support cross-project references
- # The other code assumes that this pattern is a superset of all
- # overridden patterns. See ReferenceRegexes.external_pattern
- def self.reference_pattern(only_long: false)
- if only_long
- /(\b[A-Z][A-Z0-9_]*-)#{Gitlab::Regex.issue}/
- else
- /(\b[A-Z][A-Z0-9_]*-|#{Issue.reference_prefix})#{Gitlab::Regex.issue}/
- end
- end
-
- def handle_properties
- # this has been moved from initialize_properties and should be improved
- # as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
- return unless properties
-
- @legacy_properties_data = properties.dup
- data_values = properties.slice!('title', 'description')
- data_values.reject! { |key| data_fields.changed.include?(key) }
- data_values.slice!(*data_fields.attributes.keys)
- data_fields.assign_attributes(data_values) if data_values.present?
-
- self.properties = {}
- end
-
- def legacy_properties_data
- @legacy_properties_data ||= {}
- end
-
- def supports_data_fields?
- true
- end
-
- def data_fields
- issue_tracker_data || self.build_issue_tracker_data
- end
-
- def default?
- default
- end
-
- def issue_url(iid)
- issues_url.gsub(':id', iid.to_s)
- end
-
- def issue_tracker_path
- project_url
- end
-
- def new_issue_path
- new_issue_url
- end
-
- def issue_path(iid)
- issue_url(iid)
- end
-
- def fields
- [
- { type: 'text', name: 'project_url', title: _('Project URL'), help: s_('IssueTracker|The URL to the project in the external issue tracker.'), required: true },
- { type: 'text', name: 'issues_url', title: s_('IssueTracker|Issue URL'), help: s_('IssueTracker|The URL to view an issue in the external issue tracker. Must contain %{colon_id}.') % { colon_id: '<code>:id</code>'.html_safe }, required: true },
- { type: 'text', name: 'new_issue_url', title: s_('IssueTracker|New issue URL'), help: s_('IssueTracker|The URL to create an issue in the external issue tracker.'), required: true }
- ]
- end
-
- def initialize_properties
- {}
- end
-
- # Initialize with default properties values
- def set_default_data
- return unless issues_tracker.present?
-
- # we don't want to override if we have set something
- return if project_url || issues_url || new_issue_url
-
- data_fields.project_url = issues_tracker['project_url']
- data_fields.issues_url = issues_tracker['issues_url']
- data_fields.new_issue_url = issues_tracker['new_issue_url']
- end
-
- def self.supported_events
- %w(push)
- end
-
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- message = "#{self.type} was unable to reach #{self.project_url}. Check the url and try again."
- result = false
-
- begin
- response = Gitlab::HTTP.head(self.project_url, verify: true)
-
- if response
- message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
- result = true
- end
- rescue Gitlab::HTTP::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => error
- message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}"
- end
- log_info(message)
- result
- end
-
- def support_close_issue?
- false
- end
-
- def support_cross_reference?
- false
- end
-
- private
-
- def enabled_in_gitlab_config
- Gitlab.config.issues_tracker &&
- Gitlab.config.issues_tracker.values.any? &&
- issues_tracker
- end
-
- def issues_tracker
- Gitlab.config.issues_tracker[to_param]
- end
-
- def one_issue_tracker
- return if template? || instance?
- return if project.blank?
-
- if project.integrations.external_issue_trackers.where.not(id: id).any?
- errors.add(:base, _('Another issue tracker is already in use. Only one issue tracker service can be active at a time'))
- end
- end
-end
-
-IssueTrackerService.prepend_mod_with('IssueTrackerService')
diff --git a/app/models/project_services/jenkins_service.rb b/app/models/project_services/jenkins_service.rb
deleted file mode 100644
index 990a35cd617..00000000000
--- a/app/models/project_services/jenkins_service.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-# frozen_string_literal: true
-
-class JenkinsService < CiService
- include ActionView::Helpers::UrlHelper
-
- prop_accessor :jenkins_url, :project_name, :username, :password
-
- before_update :reset_password
-
- validates :jenkins_url, presence: true, addressable_url: true, if: :activated?
- validates :project_name, presence: true, if: :activated?
- validates :username, presence: true, if: ->(service) { service.activated? && service.password_touched? && service.password.present? }
-
- default_value_for :push_events, true
- default_value_for :merge_requests_events, false
- default_value_for :tag_push_events, false
-
- after_save :compose_service_hook, if: :activated?
-
- def reset_password
- # don't reset the password if a new one is provided
- if (jenkins_url_changed? || username.blank?) && !password_touched?
- self.password = nil
- end
- end
-
- def compose_service_hook
- hook = service_hook || build_service_hook
- hook.url = hook_url
- hook.save
- end
-
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- service_hook.execute(data, "#{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
-
- def hook_url
- url = URI.parse(jenkins_url)
- url.path = File.join(url.path || '/', "project/#{project_name}")
- url.user = ERB::Util.url_encode(username) unless username.blank?
- url.password = ERB::Util.url_encode(password) unless password.blank?
- url.to_s
- end
-
- def self.supported_events
- %w(push merge_request tag_push)
- end
-
- def title
- 'Jenkins'
- end
-
- def description
- s_('Run CI/CD pipelines with Jenkins.')
- end
-
- def help
- docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('integration/jenkins'), target: '_blank', rel: 'noopener noreferrer'
- s_('Run CI/CD pipelines with Jenkins when you push to a repository, or when a merge request is created, updated, or merged. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
- end
-
- def self.to_param
- 'jenkins'
- end
-
- def fields
- [
- {
- type: 'text',
- name: 'jenkins_url',
- title: s_('ProjectService|Jenkins server URL'),
- required: true,
- placeholder: 'http://jenkins.example.com',
- help: s_('The URL of the Jenkins server.')
- },
- {
- type: 'text',
- name: 'project_name',
- required: true,
- placeholder: 'my_project_name',
- help: s_('The name of the Jenkins project. Copy the name from the end of the URL to the project.')
- },
- {
- type: 'text',
- name: 'username',
- required: true,
- help: s_('The username for the Jenkins server.')
- },
- {
- type: 'password',
- name: 'password',
- help: s_('The password for the Jenkins server.'),
- non_empty_password_title: s_('ProjectService|Enter new password.'),
- non_empty_password_help: s_('ProjectService|Leave blank to use your current password.')
- }
- ]
- end
-end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
deleted file mode 100644
index 5cd6e79eb1d..00000000000
--- a/app/models/project_services/jira_service.rb
+++ /dev/null
@@ -1,541 +0,0 @@
-# frozen_string_literal: true
-
-# Accessible as Project#external_issue_tracker
-class JiraService < IssueTrackerService
- extend ::Gitlab::Utils::Override
- include Gitlab::Routing
- include ApplicationHelper
- include ActionView::Helpers::AssetUrlHelper
- include Gitlab::Utils::StrongMemoize
-
- PROJECTS_PER_PAGE = 50
-
- # TODO: use jira_service.deployment_type enum when https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37003 is merged
- DEPLOYMENT_TYPES = {
- server: 'SERVER',
- cloud: 'CLOUD'
- }.freeze
-
- validates :url, public_url: true, presence: true, if: :activated?
- validates :api_url, public_url: true, allow_blank: true
- validates :username, presence: true, if: :activated?
- validates :password, presence: true, if: :activated?
-
- validates :jira_issue_transition_id,
- format: { with: Gitlab::Regex.jira_transition_id_regex, message: s_("JiraService|transition ids can have only numbers which can be split with , or ;") },
- allow_blank: true
-
- # Jira Cloud version is deprecating authentication via username and password.
- # We should use username/password for Jira Server and email/api_token for Jira Cloud,
- # for more information check: https://gitlab.com/gitlab-org/gitlab-foss/issues/49936.
-
- # TODO: we can probably just delegate as part of
- # https://gitlab.com/gitlab-org/gitlab/issues/29404
- data_field :username, :password, :url, :api_url, :jira_issue_transition_automatic, :jira_issue_transition_id, :project_key, :issues_enabled,
- :vulnerabilities_enabled, :vulnerabilities_issuetype
-
- before_update :reset_password
- after_commit :update_deployment_type, on: [:create, :update], if: :update_deployment_type?
-
- enum comment_detail: {
- standard: 1,
- all_details: 2
- }
-
- alias_method :project_url, :url
-
- # When these are false GitLab does not create cross reference
- # comments on Jira except when an issue gets transitioned.
- def self.supported_events
- %w(commit merge_request)
- end
-
- def self.supported_event_actions
- %w(comment)
- end
-
- # {PROJECT-KEY}-{NUMBER} Examples: JIRA-1, PROJECT-1
- def self.reference_pattern(only_long: true)
- @reference_pattern ||= /(?<issue>\b#{Gitlab::Regex.jira_issue_key_regex})/
- end
-
- def initialize_properties
- {}
- end
-
- def data_fields
- jira_tracker_data || self.build_jira_tracker_data
- end
-
- def reset_password
- data_fields.password = nil if reset_password?
- end
-
- def set_default_data
- return unless issues_tracker.present?
-
- return if url
-
- data_fields.url ||= issues_tracker['url']
- data_fields.api_url ||= issues_tracker['api_url']
- end
-
- def options
- url = URI.parse(client_url)
-
- {
- username: username&.strip,
- password: password,
- site: URI.join(url, '/').to_s, # Intended to find the root
- context_path: url.path,
- auth_type: :basic,
- read_timeout: 120,
- use_cookies: true,
- additional_cookies: ['OBBasicAuth=fromDialog'],
- use_ssl: url.scheme == 'https'
- }
- end
-
- def client
- @client ||= begin
- JIRA::Client.new(options).tap do |client|
- # Replaces JIRA default http client with our implementation
- client.request_client = Gitlab::Jira::HttpClient.new(client.options)
- end
- end
- end
-
- def help
- jira_doc_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_url('integration/jira/index.html') }
- s_("JiraService|You need to configure Jira before enabling this integration. For more details, read the %{jira_doc_link_start}Jira integration documentation%{link_end}.") % { jira_doc_link_start: jira_doc_link_start, link_end: '</a>'.html_safe }
- end
-
- def title
- 'Jira'
- end
-
- def description
- s_("JiraService|Use Jira as this project's issue tracker.")
- end
-
- def self.to_param
- 'jira'
- end
-
- def fields
- [
- {
- type: 'text',
- name: 'url',
- title: s_('JiraService|Web URL'),
- placeholder: 'https://jira.example.com',
- help: s_('JiraService|Base URL of the Jira instance.'),
- required: true
- },
- {
- type: 'text',
- name: 'api_url',
- title: s_('JiraService|Jira API URL'),
- help: s_('JiraService|If different from Web URL.')
- },
- {
- type: 'text',
- name: 'username',
- title: s_('JiraService|Username or Email'),
- help: s_('JiraService|Use a username for server version and an email for cloud version.'),
- required: true
- },
- {
- type: 'password',
- name: 'password',
- title: s_('JiraService|Password or API token'),
- non_empty_password_title: s_('JiraService|Enter new password or API token'),
- non_empty_password_help: s_('JiraService|Leave blank to use your current password or API token.'),
- help: s_('JiraService|Use a password for server version and an API token for cloud version.'),
- required: true
- }
- ]
- end
-
- def issues_url
- "#{url}/browse/:id"
- end
-
- def new_issue_url
- "#{url}/secure/CreateIssue!default.jspa"
- end
-
- alias_method :original_url, :url
- def url
- original_url&.delete_suffix('/')
- end
-
- alias_method :original_api_url, :api_url
- def api_url
- original_api_url&.delete_suffix('/')
- end
-
- def execute(push)
- # This method is a no-op, because currently JiraService does not
- # support any events.
- end
-
- def find_issue(issue_key, rendered_fields: false, transitions: false)
- expands = []
- expands << 'renderedFields' if rendered_fields
- expands << 'transitions' if transitions
- options = { expand: expands.join(',') } if expands.any?
-
- jira_request { client.Issue.find(issue_key, options || {}) }
- end
-
- def close_issue(entity, external_issue, current_user)
- issue = find_issue(external_issue.iid, transitions: jira_issue_transition_automatic)
-
- return if issue.nil? || has_resolution?(issue) || !issue_transition_enabled?
-
- commit_id = case entity
- when Commit then entity.id
- when MergeRequest then entity.diff_head_sha
- end
-
- commit_url = build_entity_url(:commit, commit_id)
-
- # Depending on the Jira project's workflow, a comment during transition
- # may or may not be allowed. Refresh the issue after transition and check
- # if it is closed, so we don't have one comment for every commit.
- issue = find_issue(issue.key) if transition_issue(issue)
- add_issue_solved_comment(issue, commit_id, commit_url) if has_resolution?(issue)
- log_usage(:close_issue, current_user)
- end
-
- def create_cross_reference_note(mentioned, noteable, author)
- unless can_cross_reference?(noteable)
- return s_("JiraService|Events for %{noteable_model_name} are disabled.") % { noteable_model_name: noteable.model_name.plural.humanize(capitalize: false) }
- end
-
- jira_issue = find_issue(mentioned.id)
-
- return unless jira_issue.present?
-
- noteable_id = noteable.respond_to?(:iid) ? noteable.iid : noteable.id
- noteable_type = noteable_name(noteable)
- entity_url = build_entity_url(noteable_type, noteable_id)
- entity_meta = build_entity_meta(noteable)
-
- data = {
- user: {
- name: author.name,
- url: resource_url(user_path(author))
- },
- project: {
- name: project.full_path,
- url: resource_url(project_path(project))
- },
- entity: {
- id: entity_meta[:id],
- name: noteable_type.humanize.downcase,
- url: entity_url,
- title: noteable.title,
- description: entity_meta[:description],
- branch: entity_meta[:branch]
- }
- }
-
- add_comment(data, jira_issue).tap { log_usage(:cross_reference, author) }
- end
-
- def valid_connection?
- test(nil)[:success]
- end
-
- def test(_)
- result = server_info
- success = result.present?
- result = @error&.message unless success
-
- { success: success, result: result }
- end
-
- override :support_close_issue?
- def support_close_issue?
- true
- end
-
- override :support_cross_reference?
- def support_cross_reference?
- true
- end
-
- def issue_transition_enabled?
- jira_issue_transition_automatic || jira_issue_transition_id.present?
- end
-
- private
-
- def server_info
- strong_memoize(:server_info) do
- client_url.present? ? jira_request { client.ServerInfo.all.attrs } : nil
- end
- end
-
- def can_cross_reference?(noteable)
- case noteable
- when Commit then commit_events
- when MergeRequest then merge_requests_events
- else true
- end
- end
-
- # jira_issue_transition_id can have multiple values split by , or ;
- # the issue is transitioned at the order given by the user
- # if any transition fails it will log the error message and stop the transition sequence
- def transition_issue(issue)
- return transition_issue_to_done(issue) if jira_issue_transition_automatic
-
- jira_issue_transition_id.scan(Gitlab::Regex.jira_transition_id_regex).all? do |transition_id|
- transition_issue_to_id(issue, transition_id)
- end
- end
-
- def transition_issue_to_id(issue, transition_id)
- issue.transitions.build.save!(
- transition: { id: transition_id }
- )
-
- true
- rescue StandardError => error
- log_error(
- "Issue transition failed",
- error: {
- exception_class: error.class.name,
- exception_message: error.message,
- exception_backtrace: Gitlab::BacktraceCleaner.clean_backtrace(error.backtrace)
- },
- client_url: client_url
- )
-
- false
- end
-
- def transition_issue_to_done(issue)
- transitions = issue.transitions rescue []
-
- transition = transitions.find do |transition|
- status = transition&.to&.statusCategory
- status && status['key'] == 'done'
- end
-
- return false unless transition
-
- transition_issue_to_id(issue, transition.id)
- end
-
- def log_usage(action, user)
- key = "i_ecosystem_jira_service_#{action}"
-
- Gitlab::UsageDataCounters::HLLRedisCounter.track_event(key, values: user.id)
- end
-
- def add_issue_solved_comment(issue, commit_id, commit_url)
- link_title = "Solved by commit #{commit_id}."
- comment = "Issue solved with [#{commit_id}|#{commit_url}]."
- link_props = build_remote_link_props(url: commit_url, title: link_title, resolved: true)
- send_message(issue, comment, link_props)
- end
-
- def add_comment(data, issue)
- entity_name = data[:entity][:name]
- entity_url = data[:entity][:url]
- entity_title = data[:entity][:title]
-
- message = comment_message(data)
- link_title = "#{entity_name.capitalize} - #{entity_title}"
- link_props = build_remote_link_props(url: entity_url, title: link_title)
-
- unless comment_exists?(issue, message)
- send_message(issue, message, link_props)
- end
- end
-
- def comment_message(data)
- user_link = build_jira_link(data[:user][:name], data[:user][:url])
-
- entity = data[:entity]
- entity_ref = all_details? ? "#{entity[:name]} #{entity[:id]}" : "a #{entity[:name]}"
- entity_link = build_jira_link(entity_ref, entity[:url])
-
- project_link = build_jira_link(project.full_name, Gitlab::Routing.url_helpers.project_url(project))
- branch =
- if entity[:branch].present?
- s_('JiraService| on branch %{branch_link}') % {
- branch_link: build_jira_link(entity[:branch], project_tree_url(project, entity[:branch]))
- }
- end
-
- entity_message = entity[:description].presence if all_details?
- entity_message ||= entity[:title].chomp
-
- s_('JiraService|%{user_link} mentioned this issue in %{entity_link} of %{project_link}%{branch}:{quote}%{entity_message}{quote}') % {
- user_link: user_link,
- entity_link: entity_link,
- project_link: project_link,
- branch: branch,
- entity_message: entity_message
- }
- end
-
- def build_jira_link(title, url)
- "[#{title}|#{url}]"
- end
-
- def has_resolution?(issue)
- issue.respond_to?(:resolution) && issue.resolution.present?
- end
-
- def comment_exists?(issue, message)
- comments = jira_request { issue.comments }
-
- comments.present? && comments.any? { |comment| comment.body.include?(message) }
- end
-
- def send_message(issue, message, remote_link_props)
- return unless client_url.present?
-
- jira_request do
- remote_link = find_remote_link(issue, remote_link_props[:object][:url])
-
- create_issue_comment(issue, message) unless remote_link
- remote_link ||= issue.remotelink.build
- remote_link.save!(remote_link_props)
-
- log_info("Successfully posted", client_url: client_url)
- "SUCCESS: Successfully posted to #{client_url}."
- end
- end
-
- def create_issue_comment(issue, message)
- return unless comment_on_event_enabled
-
- issue.comments.build.save!(body: message)
- end
-
- def find_remote_link(issue, url)
- links = jira_request { issue.remotelink.all }
- return unless links
-
- links.find { |link| link.object["url"] == url }
- end
-
- def build_remote_link_props(url:, title:, resolved: false)
- status = {
- resolved: resolved
- }
-
- {
- GlobalID: 'GitLab',
- relationship: 'mentioned on',
- object: {
- url: url,
- title: title,
- status: status,
- icon: {
- title: 'GitLab', url16x16: asset_url(Gitlab::Favicon.main, host: gitlab_config.base_url)
- }
- }
- }
- end
-
- def resource_url(resource)
- "#{Settings.gitlab.base_url.chomp("/")}#{resource}"
- end
-
- def build_entity_url(noteable_type, entity_id)
- polymorphic_url(
- [
- self.project,
- noteable_type.to_sym
- ],
- id: entity_id,
- host: Settings.gitlab.base_url
- )
- end
-
- def build_entity_meta(noteable)
- if noteable.is_a?(Commit)
- {
- id: noteable.short_id,
- description: noteable.safe_message,
- branch: noteable.ref_names(project.repository).first
- }
- elsif noteable.is_a?(MergeRequest)
- {
- id: noteable.to_reference,
- branch: noteable.source_branch
- }
- else
- {}
- end
- end
-
- def noteable_name(noteable)
- name = noteable.model_name.singular
-
- # ProjectSnippet inherits from Snippet class so it causes
- # routing error building the URL.
- name == "project_snippet" ? "snippet" : name
- end
-
- # Handle errors when doing Jira API calls
- def jira_request
- yield
- rescue StandardError => error
- @error = error
- log_error("Error sending message", client_url: client_url, error: @error.message)
- nil
- end
-
- def client_url
- api_url.presence || url
- end
-
- def reset_password?
- # don't reset the password if a new one is provided
- return false if password_touched?
- return true if api_url_changed?
- return false if api_url.present?
-
- url_changed?
- end
-
- def update_deployment_type?
- (api_url_changed? || url_changed? || username_changed? || password_changed?) &&
- can_test?
- end
-
- def update_deployment_type
- clear_memoization(:server_info) # ensure we run the request when we try to update deployment type
- results = server_info
- return data_fields.deployment_unknown! unless results.present?
-
- case results['deploymentType']
- when 'Server'
- data_fields.deployment_server!
- when 'Cloud'
- data_fields.deployment_cloud!
- else
- data_fields.deployment_unknown!
- end
- end
-
- def self.event_description(event)
- case event
- when "merge_request", "merge_request_events"
- s_("JiraService|Jira comments will be created when an issue gets referenced in a merge request.")
- when "commit", "commit_events"
- s_("JiraService|Jira comments will be created when an issue gets referenced in a commit.")
- end
- end
-end
-
-JiraService.prepend_mod_with('JiraService')
diff --git a/app/models/project_services/jira_tracker_data.rb b/app/models/project_services/jira_tracker_data.rb
deleted file mode 100644
index 2c145abf5c9..00000000000
--- a/app/models/project_services/jira_tracker_data.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-class JiraTrackerData < ApplicationRecord
- include Services::DataFields
- include IgnorableColumns
-
- ignore_columns %i[
- encrypted_proxy_address
- encrypted_proxy_address_iv
- encrypted_proxy_port
- encrypted_proxy_port_iv
- encrypted_proxy_username
- encrypted_proxy_username_iv
- encrypted_proxy_password
- encrypted_proxy_password_iv
- ], remove_with: '14.0', remove_after: '2021-05-22'
-
- attr_encrypted :url, encryption_options
- attr_encrypted :api_url, encryption_options
- attr_encrypted :username, encryption_options
- attr_encrypted :password, encryption_options
-
- enum deployment_type: { unknown: 0, server: 1, cloud: 2 }, _prefix: :deployment
-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
deleted file mode 100644
index 60235a09dcd..00000000000
--- a/app/models/project_services/mattermost_slash_commands_service.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-class MattermostSlashCommandsService < SlashCommandsService
- include Ci::TriggersHelper
-
- prop_accessor :token
-
- def can_test?
- false
- end
-
- def title
- 'Mattermost slash commands'
- end
-
- def description
- "Perform common tasks with slash commands."
- end
-
- def self.to_param
- 'mattermost_slash_commands'
- end
-
- def configure(user, params)
- token = Mattermost::Command.new(user)
- .create(command(params))
-
- update(active: true, token: token) if token
- rescue Mattermost::Error => e
- [false, e.message]
- end
-
- def list_teams(current_user)
- [Mattermost::Team.new(current_user).all, nil]
- rescue Mattermost::Error => e
- [[], e.message]
- end
-
- def chat_responder
- ::Gitlab::Chat::Responder::Mattermost
- end
-
- private
-
- def command(params)
- pretty_project_name = project.full_name
-
- params.merge(
- auto_complete: true,
- auto_complete_desc: "Perform common operations on: #{pretty_project_name}",
- auto_complete_hint: '[help]',
- description: "Perform common operations on: #{pretty_project_name}",
- display_name: "GitLab / #{pretty_project_name}",
- method: 'P',
- username: 'GitLab')
- end
-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/mock_ci_service.rb b/app/models/project_services/mock_ci_service.rb
deleted file mode 100644
index bd6344c6e1a..00000000000
--- a/app/models/project_services/mock_ci_service.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-# frozen_string_literal: true
-
-# For an example companion mocking service, see https://gitlab.com/gitlab-org/gitlab-mock-ci-service
-class MockCiService < CiService
- ALLOWED_STATES = %w[failed canceled running pending success success-with-warnings skipped not_found].freeze
-
- prop_accessor :mock_service_url
- validates :mock_service_url, presence: true, public_url: true, if: :activated?
-
- def title
- 'MockCI'
- end
-
- def description
- 'Mock an external CI'
- end
-
- def self.to_param
- 'mock_ci'
- end
-
- def fields
- [
- {
- type: 'text',
- name: 'mock_service_url',
- title: s_('ProjectService|Mock service URL'),
- placeholder: 'http://localhost:4004',
- required: true
- }
- ]
- end
-
- # Return complete url to build page
- #
- # Ex.
- # http://jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c
- #
- def build_page(sha, ref)
- Gitlab::Utils.append_path(
- mock_service_url,
- "#{project.namespace.path}/#{project.path}/status/#{sha}")
- end
-
- # Return string with build status or :error symbol
- #
- # Allowed states: 'success', 'failed', 'running', 'pending', 'skipped'
- #
- #
- # Ex.
- # @service.commit_status('13be4ac', 'master')
- # # => 'success'
- #
- # @service.commit_status('2abe4ac', 'dev')
- # # => 'running'
- #
- #
- def commit_status(sha, ref)
- response = Gitlab::HTTP.get(commit_status_path(sha), verify: false)
- read_commit_status(response)
- rescue Errno::ECONNREFUSED
- :error
- end
-
- def commit_status_path(sha)
- Gitlab::Utils.append_path(
- mock_service_url,
- "#{project.namespace.path}/#{project.path}/status/#{sha}.json")
- end
-
- def read_commit_status(response)
- return :error unless response.code == 200 || response.code == 404
-
- status = if response.code == 404
- 'pending'
- else
- response['status']
- end
-
- if status.present? && ALLOWED_STATES.include?(status)
- status
- else
- :error
- end
- end
-
- def can_test?
- false
- end
-end
diff --git a/app/models/project_services/open_project_service.rb b/app/models/project_services/open_project_service.rb
deleted file mode 100644
index a24fbc1611d..00000000000
--- a/app/models/project_services/open_project_service.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-class OpenProjectService < IssueTrackerService
- validates :url, public_url: true, presence: true, if: :activated?
- validates :api_url, public_url: true, allow_blank: true, if: :activated?
- validates :token, presence: true, if: :activated?
- validates :project_identifier_code, presence: true, if: :activated?
-
- data_field :url, :api_url, :token, :closed_status_id, :project_identifier_code
-
- def data_fields
- open_project_tracker_data || self.build_open_project_tracker_data
- end
-
- def self.to_param
- 'open_project'
- end
-end
diff --git a/app/models/project_services/open_project_tracker_data.rb b/app/models/project_services/open_project_tracker_data.rb
deleted file mode 100644
index 20de60e40c1..00000000000
--- a/app/models/project_services/open_project_tracker_data.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-class OpenProjectTrackerData < ApplicationRecord
- include Services::DataFields
-
- # When the Open Project is fresh installed, the default closed status id is "13" based on current version: v8.
- DEFAULT_CLOSED_STATUS_ID = "13"
-
- attr_encrypted :url, encryption_options
- attr_encrypted :api_url, encryption_options
- attr_encrypted :token, encryption_options
-
- def closed_status_id
- super || DEFAULT_CLOSED_STATUS_ID
- end
-end
diff --git a/app/models/project_services/packagist_service.rb b/app/models/project_services/packagist_service.rb
deleted file mode 100644
index f3ea8c64302..00000000000
--- a/app/models/project_services/packagist_service.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# 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
diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb
deleted file mode 100644
index 4603193ac8e..00000000000
--- a/app/models/project_services/pipelines_email_service.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-# 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
diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb
deleted file mode 100644
index 6e67984591d..00000000000
--- a/app/models/project_services/pivotaltracker_service.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-# 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
diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb
index b8869547a37..a289c1c2afb 100644
--- a/app/models/project_services/prometheus_service.rb
+++ b/app/models/project_services/prometheus_service.rb
@@ -117,8 +117,8 @@ class PrometheusService < MonitoringService
return false if template?
return false unless project
- project.all_clusters.enabled.eager_load(:application_prometheus).any? do |cluster|
- cluster.application_prometheus&.available?
+ project.all_clusters.enabled.eager_load(:integration_prometheus).any? do |cluster|
+ cluster.integration_prometheus_available?
end
end
diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb
deleted file mode 100644
index 89765fbdf41..00000000000
--- a/app/models/project_services/pushover_service.rb
+++ /dev/null
@@ -1,105 +0,0 @@
-# frozen_string_literal: true
-
-class PushoverService < Integration
- BASE_URI = 'https://api.pushover.net/1'
-
- prop_accessor :api_key, :user_key, :device, :priority, :sound
- validates :api_key, :user_key, :priority, presence: true, if: :activated?
-
- def title
- 'Pushover'
- end
-
- def description
- s_('PushoverService|Get real-time notifications on your device.')
- end
-
- def self.to_param
- 'pushover'
- end
-
- def fields
- [
- { type: 'text', name: 'api_key', title: _('API key'), placeholder: s_('PushoverService|Your application key'), required: true },
- { type: 'text', name: 'user_key', placeholder: s_('PushoverService|Your user key'), required: true },
- { type: 'text', name: 'device', placeholder: s_('PushoverService|Leave blank for all active devices') },
- { type: 'select', name: 'priority', required: true, choices:
- [
- [s_('PushoverService|Lowest Priority'), -2],
- [s_('PushoverService|Low Priority'), -1],
- [s_('PushoverService|Normal Priority'), 0],
- [s_('PushoverService|High Priority'), 1]
- ],
- default_choice: 0 },
- { type: 'select', name: 'sound', choices:
- [
- ['Device default sound', nil],
- ['Pushover (default)', 'pushover'],
- %w(Bike bike),
- %w(Bugle bugle),
- ['Cash Register', 'cashregister'],
- %w(Classical classical),
- %w(Cosmic cosmic),
- %w(Falling falling),
- %w(Gamelan gamelan),
- %w(Incoming incoming),
- %w(Intermission intermission),
- %w(Magic magic),
- %w(Mechanical mechanical),
- ['Piano Bar', 'pianobar'],
- %w(Siren siren),
- ['Space Alarm', 'spacealarm'],
- ['Tug Boat', 'tugboat'],
- ['Alien Alarm (long)', 'alien'],
- ['Climb (long)', 'climb'],
- ['Persistent (long)', 'persistent'],
- ['Pushover Echo (long)', 'echo'],
- ['Up Down (long)', 'updown'],
- ['None (silent)', 'none']
- ] }
- ]
- end
-
- def self.supported_events
- %w(push)
- end
-
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- ref = Gitlab::Git.ref_name(data[:ref])
- before = data[:before]
- after = data[:after]
-
- message =
- if Gitlab::Git.blank_ref?(before)
- s_("PushoverService|%{user_name} pushed new branch \"%{ref}\".") % { user_name: data[:user_name], ref: ref }
- elsif Gitlab::Git.blank_ref?(after)
- s_("PushoverService|%{user_name} deleted branch \"%{ref}\".") % { user_name: data[:user_name], ref: ref }
- else
- s_("PushoverService|%{user_name} push to branch \"%{ref}\".") % { user_name: data[:user_name], ref: ref }
- end
-
- if data[:total_commits_count] > 0
- message = [message, s_("PushoverService|Total commits count: %{total_commits_count}") % { total_commits_count: data[:total_commits_count] }].join("\n")
- end
-
- pushover_data = {
- token: api_key,
- user: user_key,
- device: device,
- priority: priority,
- title: "#{project.full_name}",
- message: message,
- url: data[:project][:web_url],
- url_title: s_("PushoverService|See project %{project_full_name}") % { project_full_name: project.full_name }
- }
-
- # Sound parameter MUST NOT be sent to API if not selected
- if sound
- pushover_data[:sound] = sound
- end
-
- Gitlab::HTTP.post('/messages.json', base_uri: BASE_URI, body: pushover_data)
- end
-end
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
deleted file mode 100644
index 7a0f500209c..00000000000
--- a/app/models/project_services/redmine_service.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-class RedmineService < IssueTrackerService
- include ActionView::Helpers::UrlHelper
- validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
-
- def title
- 'Redmine'
- end
-
- def description
- s_("IssueTracker|Use Redmine as this project's issue tracker.")
- end
-
- def help
- docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/redmine'), target: '_blank', rel: 'noopener noreferrer'
- s_('IssueTracker|Use Redmine as the issue tracker. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
- end
-
- def self.to_param
- 'redmine'
- end
-end
diff --git a/app/models/project_services/slack_mattermost/notifier.rb b/app/models/project_services/slack_mattermost/notifier.rb
deleted file mode 100644
index 1a78cea5933..00000000000
--- a/app/models/project_services/slack_mattermost/notifier.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-module SlackMattermost
- module Notifier
- private
-
- 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.ping(
- message.pretext,
- attachments: message.attachments,
- fallback: message.fallback
- )
- end
-
- class HTTPClient
- def self.post(uri, params = {})
- params.delete(:http_options) # these are internal to the client and we do not want them
- Gitlab::HTTP.post(uri, body: params)
- end
- end
- end
-end
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
deleted file mode 100644
index 548f3623504..00000000000
--- a/app/models/project_services/slack_slash_commands_service.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-class SlackSlashCommandsService < SlashCommandsService
- include Ci::TriggersHelper
-
- def title
- 'Slack slash commands'
- end
-
- def description
- "Perform common operations in Slack"
- end
-
- def self.to_param
- 'slack_slash_commands'
- end
-
- def trigger(params)
- # Format messages to be Slack-compatible
- super.tap do |result|
- result[:text] = format(result[:text]) if result.is_a?(Hash)
- end
- end
-
- def chat_responder
- ::Gitlab::Chat::Responder::Slack
- end
-
- private
-
- def format(text)
- Slack::Messenger::Util::LinkFormatter.format(text) if text
- end
-end
diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb
deleted file mode 100644
index 37d16737052..00000000000
--- a/app/models/project_services/slash_commands_service.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# frozen_string_literal: true
-
-# Base class for Chat services
-# This class is not meant to be used directly, but only to inherrit from.
-class SlashCommandsService < Integration
- default_value_for :category, 'chat'
-
- prop_accessor :token
-
- has_many :chat_names, foreign_key: :service_id, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
-
- def valid_token?(token)
- self.respond_to?(:token) &&
- self.token.present? &&
- ActiveSupport::SecurityUtils.secure_compare(token, self.token)
- end
-
- def self.supported_events
- %w()
- end
-
- def can_test?
- false
- end
-
- def fields
- [
- { type: 'text', name: 'token', placeholder: 'XXxxXXxxXXxxXXxxXXxxXXxx' }
- ]
- end
-
- def trigger(params)
- return unless valid_token?(params[:token])
-
- chat_user = find_chat_user(params)
- user = chat_user&.user
-
- if user
- unless user.can?(:use_slash_commands)
- return Gitlab::SlashCommands::Presenters::Access.new.deactivated if user.deactivated?
-
- return Gitlab::SlashCommands::Presenters::Access.new.access_denied(project)
- end
-
- Gitlab::SlashCommands::Command.new(project, chat_user, params).execute
- else
- url = authorize_chat_name_url(params)
- Gitlab::SlashCommands::Presenters::Access.new(url).authorize
- end
- end
-
- private
-
- # rubocop: disable CodeReuse/ServiceClass
- def find_chat_user(params)
- ChatNames::FindUserService.new(self, params).execute
- end
- # rubocop: enable CodeReuse/ServiceClass
-
- # rubocop: disable CodeReuse/ServiceClass
- def authorize_chat_name_url(params)
- ChatNames::AuthorizeUserService.new(self, params).execute
- end
- # rubocop: enable CodeReuse/ServiceClass
-end
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
deleted file mode 100644
index 6fc24a4778c..00000000000
--- a/app/models/project_services/teamcity_service.rb
+++ /dev/null
@@ -1,189 +0,0 @@
-# frozen_string_literal: true
-
-class TeamcityService < CiService
- include ReactiveService
- include ServicePushDataValidations
-
- prop_accessor :teamcity_url, :build_type, :username, :password
-
- validates :teamcity_url, presence: true, public_url: true, if: :activated?
- validates :build_type, presence: true, if: :activated?
- validates :username,
- presence: true,
- if: ->(service) { service.activated? && service.password }
- validates :password,
- presence: true,
- if: ->(service) { service.activated? && service.username }
-
- attr_accessor :response
-
- after_save :compose_service_hook, if: :activated?
- before_update :reset_password
-
- class << self
- def to_param
- 'teamcity'
- end
-
- def supported_events
- %w(push merge_request)
- end
-
- def event_description(event)
- case event
- when 'push', 'push_events'
- 'TeamCity CI will be triggered after every push to the repository except branch delete'
- when 'merge_request', 'merge_request_events'
- 'TeamCity CI will be triggered after a merge request has been created or updated'
- end
- end
- end
-
- def compose_service_hook
- hook = service_hook || build_service_hook
- hook.save
- end
-
- def reset_password
- if teamcity_url_changed? && !password_touched?
- self.password = nil
- end
- end
-
- def title
- 'JetBrains TeamCity'
- end
-
- def description
- s_('ProjectService|Run CI/CD pipelines with JetBrains TeamCity.')
- end
-
- def help
- s_('To run CI/CD pipelines with JetBrains TeamCity, input the GitLab project details in the TeamCity project Version Control Settings.')
- end
-
- def fields
- [
- {
- type: 'text',
- name: 'teamcity_url',
- title: s_('ProjectService|TeamCity server URL'),
- placeholder: 'https://teamcity.example.com',
- required: true
- },
- {
- type: 'text',
- name: 'build_type',
- help: s_('ProjectService|The build configuration ID of the TeamCity project.'),
- required: true
- },
- {
- type: 'text',
- name: 'username',
- help: s_('ProjectService|Must have permission to trigger a manual build in TeamCity.')
- },
- {
- type: 'password',
- name: 'password',
- non_empty_password_title: s_('ProjectService|Enter new password'),
- non_empty_password_help: s_('ProjectService|Leave blank to use your current password')
- }
- ]
- end
-
- def build_page(sha, ref)
- with_reactive_cache(sha, ref) {|cached| cached[:build_page] }
- end
-
- def commit_status(sha, ref)
- with_reactive_cache(sha, ref) {|cached| cached[:commit_status] }
- end
-
- def calculate_reactive_cache(sha, ref)
- response = get_path("httpAuth/app/rest/builds/branch:unspecified:any,revision:#{sha}")
-
- if response
- { build_page: read_build_page(response), commit_status: read_commit_status(response) }
- else
- { build_page: teamcity_url, commit_status: :error }
- end
- end
-
- def execute(data)
- case data[:object_kind]
- when 'push'
- execute_push(data)
- when 'merge_request'
- execute_merge_request(data)
- end
- end
-
- private
-
- def execute_push(data)
- branch = Gitlab::Git.ref_name(data[:ref])
- post_to_build_queue(data, branch) if push_valid?(data)
- end
-
- def execute_merge_request(data)
- branch = data[:object_attributes][:source_branch]
- post_to_build_queue(data, branch) if merge_request_valid?(data)
- end
-
- def read_build_page(response)
- if response.code != 200
- # If actual build link can't be determined,
- # send user to build summary page.
- build_url("viewLog.html?buildTypeId=#{build_type}")
- else
- # If actual build link is available, go to build result page.
- built_id = response['build']['id']
- build_url("viewLog.html?buildId=#{built_id}&buildTypeId=#{build_type}")
- end
- end
-
- def read_commit_status(response)
- return :error unless response.code == 200 || response.code == 404
-
- status = if response.code == 404
- 'Pending'
- else
- response['build']['status']
- end
-
- return :error unless status.present?
-
- if status.include?('SUCCESS')
- 'success'
- elsif status.include?('FAILURE')
- 'failed'
- elsif status.include?('Pending')
- 'pending'
- else
- :error
- end
- end
-
- def build_url(path)
- Gitlab::Utils.append_path(teamcity_url, path)
- end
-
- def get_path(path)
- Gitlab::HTTP.try_get(build_url(path), verify: false, basic_auth: basic_auth, extra_log_info: { project_id: project_id })
- end
-
- def post_to_build_queue(data, branch)
- Gitlab::HTTP.post(
- build_url('httpAuth/app/rest/buildQueue'),
- body: "<build branchName=#{branch.encode(xml: :attr)}>"\
- "<buildType id=#{build_type.encode(xml: :attr)}/>"\
- '</build>',
- headers: { 'Content-type' => 'application/xml' },
- basic_auth: basic_auth
- )
- end
-
- def basic_auth
- { username: username, password: password }
- 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/models/project_services/youtrack_service.rb b/app/models/project_services/youtrack_service.rb
deleted file mode 100644
index 9760a22a872..00000000000
--- a/app/models/project_services/youtrack_service.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-class YoutrackService < IssueTrackerService
- include ActionView::Helpers::UrlHelper
-
- validates :project_url, :issues_url, presence: true, public_url: true, if: :activated?
-
- # {PROJECT-KEY}-{NUMBER} Examples: YT-1, PRJ-1, gl-030
- def self.reference_pattern(only_long: false)
- if only_long
- /(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+\b)/
- else
- /(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+\b)|(#{Issue.reference_prefix}#{Gitlab::Regex.issue})/
- end
- end
-
- def title
- 'YouTrack'
- end
-
- def description
- s_("IssueTracker|Use YouTrack as this project's issue tracker.")
- end
-
- def help
- docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/youtrack'), target: '_blank', rel: 'noopener noreferrer'
- s_("IssueTracker|Use YouTrack as this project's issue tracker. %{docs_link}").html_safe % { docs_link: docs_link.html_safe }
- end
-
- def self.to_param
- 'youtrack'
- end
-
- def fields
- [
- { type: 'text', name: 'project_url', title: _('Project URL'), help: s_('IssueTracker|The URL to the project in YouTrack.'), required: true },
- { type: 'text', name: 'issues_url', title: s_('ProjectService|Issue URL'), help: s_('IssueTracker|The URL to view an issue in the YouTrack project. Must contain %{colon_id}.') % { colon_id: '<code>:id</code>'.html_safe }, required: true }
- ]
- end
-end