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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/project_services')
-rw-r--r--app/models/project_services/asana_service.rb107
-rw-r--r--app/models/project_services/assembla_service.rb36
-rw-r--r--app/models/project_services/bamboo_service.rb181
-rw-r--r--app/models/project_services/bugzilla_service.rb9
-rw-r--r--app/models/project_services/buildkite_service.rb2
-rw-r--r--app/models/project_services/builds_email_service.rb13
-rw-r--r--app/models/project_services/campfire_service.rb102
-rw-r--r--app/models/project_services/chat_message/alert_message.rb74
-rw-r--r--app/models/project_services/chat_message/base_message.rb86
-rw-r--r--app/models/project_services/chat_message/deployment_message.rb85
-rw-r--r--app/models/project_services/chat_message/issue_message.rb72
-rw-r--r--app/models/project_services/chat_message/merge_message.rb81
-rw-r--r--app/models/project_services/chat_message/note_message.rb84
-rw-r--r--app/models/project_services/chat_message/pipeline_message.rb265
-rw-r--r--app/models/project_services/chat_message/push_message.rb118
-rw-r--r--app/models/project_services/chat_message/wiki_page_message.rb61
-rw-r--r--app/models/project_services/chat_notification_service.rb54
-rw-r--r--app/models/project_services/ci_service.rb2
-rw-r--r--app/models/project_services/confluence_service.rb91
-rw-r--r--app/models/project_services/custom_issue_tracker_service.rb18
-rw-r--r--app/models/project_services/data_fields.rb6
-rw-r--r--app/models/project_services/datadog_service.rb144
-rw-r--r--app/models/project_services/emails_on_push_service.rb97
-rw-r--r--app/models/project_services/ewm_service.rb9
-rw-r--r--app/models/project_services/external_wiki_service.rb5
-rw-r--r--app/models/project_services/flowdock_service.rb13
-rw-r--r--app/models/project_services/hangouts_chat_service.rb17
-rw-r--r--app/models/project_services/hipchat_service.rb143
-rw-r--r--app/models/project_services/irker_service.rb7
-rw-r--r--app/models/project_services/issue_tracker_service.rb12
-rw-r--r--app/models/project_services/jenkins_service.rb4
-rw-r--r--app/models/project_services/jira_service.rb13
-rw-r--r--app/models/project_services/microsoft_teams_service.rb2
-rw-r--r--app/models/project_services/monitoring_service.rb2
-rw-r--r--app/models/project_services/packagist_service.rb4
-rw-r--r--app/models/project_services/pipelines_email_service.rb2
-rw-r--r--app/models/project_services/pivotaltracker_service.rb4
-rw-r--r--app/models/project_services/pushover_service.rb4
-rw-r--r--app/models/project_services/redmine_service.rb2
-rw-r--r--app/models/project_services/slack_service.rb2
-rw-r--r--app/models/project_services/slash_commands_service.rb2
-rw-r--r--app/models/project_services/unify_circuit_service.rb2
-rw-r--r--app/models/project_services/webex_teams_service.rb17
-rw-r--r--app/models/project_services/youtrack_service.rb13
44 files changed, 148 insertions, 1919 deletions
diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb
deleted file mode 100644
index f31bf931a41..00000000000
--- a/app/models/project_services/asana_service.rb
+++ /dev/null
@@ -1,107 +0,0 @@
-# frozen_string_literal: true
-
-require 'asana'
-
-class AsanaService < Service
- include ActionView::Helpers::UrlHelper
-
- prop_accessor :api_key, :restrict_to_branch
- validates :api_key, presence: true, if: :activated?
-
- def title
- 'Asana'
- end
-
- def description
- s_('AsanaService|Add commit messages as comments to Asana tasks')
- end
-
- def help
- docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/asana'), target: '_blank', rel: 'noopener noreferrer'
- s_('Add commit messages as comments to Asana tasks. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
- end
-
- def self.to_param
- 'asana'
- end
-
- def fields
- [
- {
- type: 'text',
- name: 'api_key',
- title: 'API key',
- help: s_('AsanaService|User Personal Access Token. User must have access to the task. All comments are attributed to this user.'),
- # Example Personal Access Token from Asana docs
- placeholder: '0/68a9e79b868c6789e79a124c30b0',
- required: true
- },
- {
- type: 'text',
- name: 'restrict_to_branch',
- title: 'Restrict to branch (optional)',
- help: s_('AsanaService|Comma-separated list of branches to be automatically inspected. Leave blank to include all branches.')
- }
- ]
- end
-
- def self.supported_events
- %w(push)
- end
-
- def client
- @_client ||= begin
- Asana::Client.new do |c|
- c.authentication :access_token, api_key
- end
- end
- end
-
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- # check the branch restriction is poplulated and branch is not included
- branch = Gitlab::Git.ref_name(data[:ref])
- branch_restriction = restrict_to_branch.to_s
- if branch_restriction.present? && branch_restriction.index(branch).nil?
- return
- end
-
- user = data[:user_name]
- project_name = project.full_name
-
- data[:commits].each do |commit|
- push_msg = s_("AsanaService|%{user} pushed to branch %{branch} of %{project_name} ( %{commit_url} ):") % { user: user, branch: branch, project_name: project_name, commit_url: commit[:url] }
- check_commit(commit[:message], push_msg)
- end
- end
-
- def check_commit(message, push_msg)
- # matches either:
- # - #1234
- # - https://app.asana.com/0/{project_gid}/{task_gid}
- # optionally preceded with:
- # - fix/ed/es/ing
- # - close/s/d
- # - closing
- issue_finder = %r{(fix\w*|clos[ei]\w*+)?\W*(?:https://app\.asana\.com/\d+/\w+/(\w+)|#(\w+))}i
-
- message.scan(issue_finder).each do |tuple|
- # tuple will be
- # [ 'fix', 'id_from_url', 'id_from_pound' ]
- taskid = tuple[2] || tuple[1]
-
- begin
- task = Asana::Resources::Task.find_by_id(client, taskid)
- task.add_comment(text: "#{push_msg} #{message}")
-
- if tuple[0]
- task.update(completed: true)
- end
- rescue => e
- log_error(e.message)
- next
- end
- end
- end
-end
diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb
deleted file mode 100644
index 8845fb99605..00000000000
--- a/app/models/project_services/assembla_service.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-class AssemblaService < Service
- prop_accessor :token, :subdomain
- validates :token, presence: true, if: :activated?
-
- def title
- 'Assembla'
- end
-
- def description
- _('Manage projects.')
- end
-
- def self.to_param
- 'assembla'
- end
-
- def fields
- [
- { type: 'text', name: 'token', placeholder: '', required: true },
- { type: 'text', name: 'subdomain', placeholder: '' }
- ]
- end
-
- def self.supported_events
- %w(push)
- end
-
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- url = "https://atlas.assembla.com/spaces/#{subdomain}/github_tool?secret_key=#{token}"
- Gitlab::HTTP.post(url, body: { payload: data }.to_json, headers: { 'Content-Type' => 'application/json' })
- end
-end
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
deleted file mode 100644
index a892d1a4314..00000000000
--- a/app/models/project_services/bamboo_service.rb
+++ /dev/null
@@ -1,181 +0,0 @@
-# frozen_string_literal: true
-
-class BambooService < CiService
- include ActionView::Helpers::UrlHelper
- include ReactiveService
-
- prop_accessor :bamboo_url, :build_key, :username, :password
-
- validates :bamboo_url, presence: true, public_url: true, if: :activated?
- validates :build_key, 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
-
- def compose_service_hook
- hook = service_hook || build_service_hook
- hook.save
- end
-
- def reset_password
- if bamboo_url_changed? && !password_touched?
- self.password = nil
- end
- end
-
- def title
- s_('BambooService|Atlassian Bamboo')
- end
-
- def description
- s_('BambooService|Use the Atlassian Bamboo CI/CD server with GitLab.')
- end
-
- def help
- docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/bamboo'), target: '_blank', rel: 'noopener noreferrer'
- s_('BambooService|Use Atlassian Bamboo to run CI/CD pipelines. You must set up automatic revision labeling and a repository trigger in Bamboo. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
- end
-
- def self.to_param
- 'bamboo'
- end
-
- def fields
- [
- {
- type: 'text',
- name: 'bamboo_url',
- title: s_('BambooService|Bamboo URL'),
- placeholder: s_('https://bamboo.example.com'),
- help: s_('BambooService|Bamboo service root URL.'),
- required: true
- },
- {
- type: 'text',
- name: 'build_key',
- placeholder: s_('KEY'),
- help: s_('BambooService|Bamboo build plan key.'),
- required: true
- },
- {
- type: 'text',
- name: 'username',
- help: s_('BambooService|The user with API access to the Bamboo server.')
- },
- {
- 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 execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- get_path("updateAndBuild.action", { buildKey: build_key })
- end
-
- def calculate_reactive_cache(sha, ref)
- response = try_get_path("rest/api/latest/result/byChangeset/#{sha}")
-
- { build_page: read_build_page(response), commit_status: read_commit_status(response) }
- end
-
- private
-
- def get_build_result(response)
- return if response&.code != 200
-
- # May be nil if no result, a single result hash, or an array if multiple results for a given changeset.
- result = response.dig('results', 'results', 'result')
-
- # In case of multiple results, arbitrarily assume the last one is the most relevant.
- return result.last if result.is_a?(Array)
-
- result
- end
-
- def read_build_page(response)
- result = get_build_result(response)
- key =
- if result.blank?
- # If actual build link can't be determined, send user to build summary page.
- build_key
- else
- # If actual build link is available, go to build result page.
- result.dig('planResultKey', 'key')
- end
-
- build_url("browse/#{key}")
- end
-
- def read_commit_status(response)
- return :error unless response && (response.code == 200 || response.code == 404)
-
- result = get_build_result(response)
- status =
- if result.blank?
- 'Pending'
- else
- result.dig('buildState')
- end
-
- return :error unless status.present?
-
- if status.include?('Success')
- 'success'
- elsif status.include?('Failed')
- 'failed'
- elsif status.include?('Pending')
- 'pending'
- else
- :error
- end
- end
-
- def try_get_path(path, query_params = {})
- params = build_get_params(query_params)
- params[:extra_log_info] = { project_id: project_id }
-
- Gitlab::HTTP.try_get(build_url(path), params)
- end
-
- def get_path(path, query_params = {})
- Gitlab::HTTP.get(build_url(path), build_get_params(query_params))
- end
-
- def build_url(path)
- Gitlab::Utils.append_path(bamboo_url, path)
- end
-
- def build_get_params(query_params)
- params = { verify: false, query: query_params }
- return params if username.blank? && password.blank?
-
- query_params[:os_authType] = 'basic'
- params[:basic_auth] = basic_auth
- params
- end
-
- def basic_auth
- { username: username, password: password }
- end
-end
diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb
index 4332db3e961..d1c56d2a4d5 100644
--- a/app/models/project_services/bugzilla_service.rb
+++ b/app/models/project_services/bugzilla_service.rb
@@ -1,6 +1,8 @@
# 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
@@ -8,7 +10,12 @@ class BugzillaService < IssueTrackerService
end
def description
- s_('IssueTracker|Bugzilla issue tracker')
+ 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
diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb
index 53bb7b47b41..f2ea5066e37 100644
--- a/app/models/project_services/buildkite_service.rb
+++ b/app/models/project_services/buildkite_service.rb
@@ -68,7 +68,7 @@ class BuildkiteService < CiService
end
def description
- 'Buildkite is a platform for running fast, secure, and scalable continuous integration pipelines on your own infrastructure'
+ 'Run CI/CD pipelines with Buildkite.'
end
def self.to_param
diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb
deleted file mode 100644
index f2295a95b60..00000000000
--- a/app/models/project_services/builds_email_service.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-# This class is to be removed with 9.1
-# We should also by then remove BuildsEmailService from database
-class BuildsEmailService < Service
- def self.to_param
- 'builds_email'
- end
-
- def self.supported_events
- %w[]
- end
-end
diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb
deleted file mode 100644
index ad26e42a21b..00000000000
--- a/app/models/project_services/campfire_service.rb
+++ /dev/null
@@ -1,102 +0,0 @@
-# frozen_string_literal: true
-
-class CampfireService < Service
- prop_accessor :token, :subdomain, :room
- validates :token, presence: true, if: :activated?
-
- def title
- 'Campfire'
- end
-
- def description
- 'Simple web-based real-time group chat'
- end
-
- def self.to_param
- 'campfire'
- end
-
- def fields
- [
- { type: 'text', name: 'token', placeholder: '', required: true },
- { type: 'text', name: 'subdomain', placeholder: '' },
- { type: 'text', name: 'room', placeholder: '' }
- ]
- end
-
- def self.supported_events
- %w(push)
- end
-
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- message = build_message(data)
- speak(self.room, message, auth)
- end
-
- private
-
- def base_uri
- @base_uri ||= "https://#{subdomain}.campfirenow.com"
- end
-
- def auth
- # use a dummy password, as explained in the Campfire API doc:
- # https://github.com/basecamp/campfire-api#authentication
- @auth ||= {
- basic_auth: {
- username: token,
- password: 'X'
- }
- }
- end
-
- # Post a message into a room, returns the message Hash in case of success.
- # Returns nil otherwise.
- # https://github.com/basecamp/campfire-api/blob/master/sections/messages.md#create-message
- def speak(room_name, message, auth)
- room = rooms(auth).find { |r| r["name"] == room_name }
- return unless room
-
- path = "/room/#{room["id"]}/speak.json"
- body = {
- body: {
- message: {
- type: 'TextMessage',
- body: message
- }
- }
- }
- res = Gitlab::HTTP.post(path, base_uri: base_uri, **auth.merge(body))
- res.code == 201 ? res : nil
- end
-
- # Returns a list of rooms, or [].
- # https://github.com/basecamp/campfire-api/blob/master/sections/rooms.md#get-rooms
- def rooms(auth)
- res = Gitlab::HTTP.get("/rooms.json", base_uri: base_uri, **auth)
- res.code == 200 ? res["rooms"] : []
- end
-
- def build_message(push)
- ref = Gitlab::Git.ref_name(push[:ref])
- before = push[:before]
- after = push[:after]
-
- message = []
- message << "[#{project.full_name}] "
- message << "#{push[:user_name]} "
-
- if Gitlab::Git.blank_ref?(before)
- message << "pushed new branch #{ref} \n"
- elsif Gitlab::Git.blank_ref?(after)
- message << "removed branch #{ref} \n"
- else
- message << "pushed #{push[:total_commits_count]} commits to #{ref}. "
- message << "#{project.web_url}/compare/#{before}...#{after}"
- end
-
- message.join
- end
-end
diff --git a/app/models/project_services/chat_message/alert_message.rb b/app/models/project_services/chat_message/alert_message.rb
deleted file mode 100644
index c8913775843..00000000000
--- a/app/models/project_services/chat_message/alert_message.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-# frozen_string_literal: true
-
-module ChatMessage
- class AlertMessage < BaseMessage
- attr_reader :title
- attr_reader :alert_url
- attr_reader :severity
- attr_reader :events
- attr_reader :status
- attr_reader :started_at
-
- def initialize(params)
- @project_name = params[:project_name] || params.dig(:project, :path_with_namespace)
- @project_url = params.dig(:project, :web_url) || params[:project_url]
- @title = params.dig(:object_attributes, :title)
- @alert_url = params.dig(:object_attributes, :url)
- @severity = params.dig(:object_attributes, :severity)
- @events = params.dig(:object_attributes, :events)
- @status = params.dig(:object_attributes, :status)
- @started_at = params.dig(:object_attributes, :started_at)
- end
-
- def attachments
- [{
- title: title,
- title_link: alert_url,
- color: attachment_color,
- fields: attachment_fields
- }]
- end
-
- def message
- "Alert firing in #{project_name}"
- end
-
- private
-
- def attachment_color
- "#C95823"
- end
-
- def attachment_fields
- [
- {
- title: "Severity",
- value: severity.to_s.humanize,
- short: true
- },
- {
- title: "Events",
- value: events,
- short: true
- },
- {
- title: "Status",
- value: status.to_s.humanize,
- short: true
- },
- {
- title: "Start time",
- value: format_time(started_at),
- short: true
- }
- ]
- end
-
- # This formats time into the following format
- # April 23rd, 2020 1:06AM UTC
- def format_time(time)
- time = Time.zone.parse(time.to_s)
- time.strftime("%B #{time.day.ordinalize}, %Y %l:%M%p %Z")
- end
- end
-end
diff --git a/app/models/project_services/chat_message/base_message.rb b/app/models/project_services/chat_message/base_message.rb
deleted file mode 100644
index bdd77a919e3..00000000000
--- a/app/models/project_services/chat_message/base_message.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-# frozen_string_literal: true
-
-module ChatMessage
- class BaseMessage
- RELATIVE_LINK_REGEX = /!\[[^\]]*\]\((\/uploads\/[^\)]*)\)/.freeze
-
- attr_reader :markdown
- attr_reader :user_full_name
- attr_reader :user_name
- attr_reader :user_avatar
- attr_reader :project_name
- attr_reader :project_url
-
- def initialize(params)
- @markdown = params[:markdown] || false
- @project_name = params[:project_name] || params.dig(:project, :path_with_namespace)
- @project_url = params.dig(:project, :web_url) || params[:project_url]
- @user_full_name = params.dig(:user, :name) || params[:user_full_name]
- @user_name = params.dig(:user, :username) || params[:user_name]
- @user_avatar = params.dig(:user, :avatar_url) || params[:user_avatar]
- end
-
- def user_combined_name
- if user_full_name.present?
- "#{user_full_name} (#{user_name})"
- else
- user_name
- end
- end
-
- def summary
- return message if markdown
-
- format(message)
- end
-
- def pretext
- summary
- end
-
- def fallback
- format(message)
- end
-
- def attachments
- raise NotImplementedError
- end
-
- def activity
- raise NotImplementedError
- end
-
- private
-
- def message
- raise NotImplementedError
- end
-
- def format(string)
- Slack::Messenger::Util::LinkFormatter.format(format_relative_links(string))
- end
-
- def format_relative_links(string)
- string.gsub(RELATIVE_LINK_REGEX, "#{project_url}\\1")
- end
-
- def attachment_color
- '#345'
- end
-
- def link(text, url)
- "[#{text}](#{url})"
- end
-
- def pretty_duration(seconds)
- parse_string =
- if duration < 1.hour
- '%M:%S'
- else
- '%H:%M:%S'
- end
-
- Time.at(seconds).utc.strftime(parse_string)
- end
- end
-end
diff --git a/app/models/project_services/chat_message/deployment_message.rb b/app/models/project_services/chat_message/deployment_message.rb
deleted file mode 100644
index 5deb757e60f..00000000000
--- a/app/models/project_services/chat_message/deployment_message.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-# frozen_string_literal: true
-
-module ChatMessage
- class DeploymentMessage < BaseMessage
- attr_reader :commit_title
- attr_reader :commit_url
- attr_reader :deployable_id
- attr_reader :deployable_url
- attr_reader :environment
- attr_reader :short_sha
- attr_reader :status
- attr_reader :user_url
-
- def initialize(data)
- super
-
- @commit_title = data[:commit_title]
- @commit_url = data[:commit_url]
- @deployable_id = data[:deployable_id]
- @deployable_url = data[:deployable_url]
- @environment = data[:environment]
- @short_sha = data[:short_sha]
- @status = data[:status]
- @user_url = data[:user_url]
- end
-
- def attachments
- [{
- text: "#{project_link} with job #{deployment_link} by #{user_link}\n#{commit_link}: #{commit_title}",
- color: color
- }]
- end
-
- def activity
- {}
- end
-
- private
-
- def message
- if running?
- "Starting deploy to #{environment}"
- else
- "Deploy to #{environment} #{humanized_status}"
- end
- end
-
- def color
- case status
- when 'success'
- 'good'
- when 'canceled'
- 'warning'
- when 'failed'
- 'danger'
- else
- '#334455'
- end
- end
-
- def project_link
- link(project_name, project_url)
- end
-
- def deployment_link
- link("##{deployable_id}", deployable_url)
- end
-
- def user_link
- link(user_combined_name, user_url)
- end
-
- def commit_link
- link(short_sha, commit_url)
- end
-
- def humanized_status
- status == 'success' ? 'succeeded' : status
- end
-
- def running?
- status == 'running'
- end
- end
-end
diff --git a/app/models/project_services/chat_message/issue_message.rb b/app/models/project_services/chat_message/issue_message.rb
deleted file mode 100644
index c8e90b66bae..00000000000
--- a/app/models/project_services/chat_message/issue_message.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-# frozen_string_literal: true
-
-module ChatMessage
- class IssueMessage < BaseMessage
- attr_reader :title
- attr_reader :issue_iid
- attr_reader :issue_url
- attr_reader :action
- attr_reader :state
- attr_reader :description
-
- def initialize(params)
- super
-
- obj_attr = params[:object_attributes]
- obj_attr = HashWithIndifferentAccess.new(obj_attr)
- @title = obj_attr[:title]
- @issue_iid = obj_attr[:iid]
- @issue_url = obj_attr[:url]
- @action = obj_attr[:action]
- @state = obj_attr[:state]
- @description = obj_attr[:description] || ''
- end
-
- def attachments
- return [] unless opened_issue?
- return description if markdown
-
- description_message
- end
-
- def activity
- {
- title: "Issue #{state} by #{user_combined_name}",
- subtitle: "in #{project_link}",
- text: issue_link,
- image: user_avatar
- }
- end
-
- private
-
- def message
- "[#{project_link}] Issue #{issue_link} #{state} by #{user_combined_name}"
- end
-
- def opened_issue?
- action == 'open'
- end
-
- def description_message
- [{
- title: issue_title,
- title_link: issue_url,
- text: format(description),
- color: '#C95823'
- }]
- end
-
- def project_link
- link(project_name, project_url)
- end
-
- def issue_link
- link(issue_title, issue_url)
- end
-
- def issue_title
- "#{Issue.reference_prefix}#{issue_iid} #{title}"
- end
- end
-end
diff --git a/app/models/project_services/chat_message/merge_message.rb b/app/models/project_services/chat_message/merge_message.rb
deleted file mode 100644
index e45bb9b8ce1..00000000000
--- a/app/models/project_services/chat_message/merge_message.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-# frozen_string_literal: true
-
-module ChatMessage
- class MergeMessage < BaseMessage
- attr_reader :merge_request_iid
- attr_reader :source_branch
- attr_reader :target_branch
- attr_reader :action
- attr_reader :state
- attr_reader :title
-
- def initialize(params)
- super
-
- obj_attr = params[:object_attributes]
- obj_attr = HashWithIndifferentAccess.new(obj_attr)
- @merge_request_iid = obj_attr[:iid]
- @source_branch = obj_attr[:source_branch]
- @target_branch = obj_attr[:target_branch]
- @action = obj_attr[:action]
- @state = obj_attr[:state]
- @title = format_title(obj_attr[:title])
- end
-
- def attachments
- []
- end
-
- def activity
- {
- title: "Merge request #{state_or_action_text} by #{user_combined_name}",
- subtitle: "in #{project_link}",
- text: merge_request_link,
- image: user_avatar
- }
- end
-
- private
-
- def format_title(title)
- '*' + title.lines.first.chomp + '*'
- end
-
- def message
- merge_request_message
- end
-
- def project_link
- link(project_name, project_url)
- end
-
- def merge_request_message
- "#{user_combined_name} #{state_or_action_text} merge request #{merge_request_link} in #{project_link}"
- end
-
- def merge_request_link
- link(merge_request_title, merge_request_url)
- end
-
- def merge_request_title
- "#{MergeRequest.reference_prefix}#{merge_request_iid} #{title}"
- end
-
- def merge_request_url
- "#{project_url}/-/merge_requests/#{merge_request_iid}"
- end
-
- def state_or_action_text
- case action
- when 'approved', 'unapproved'
- action
- when 'approval'
- 'added their approval to'
- when 'unapproval'
- 'removed their approval from'
- else
- state
- end
- end
- end
-end
diff --git a/app/models/project_services/chat_message/note_message.rb b/app/models/project_services/chat_message/note_message.rb
deleted file mode 100644
index 741474fb27b..00000000000
--- a/app/models/project_services/chat_message/note_message.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-# frozen_string_literal: true
-
-module ChatMessage
- class NoteMessage < BaseMessage
- attr_reader :note
- attr_reader :note_url
- attr_reader :title
- attr_reader :target
-
- def initialize(params)
- super
-
- params = HashWithIndifferentAccess.new(params)
- obj_attr = params[:object_attributes]
- @note = obj_attr[:note]
- @note_url = obj_attr[:url]
- @target, @title = case obj_attr[:noteable_type]
- when "Commit"
- create_commit_note(params[:commit])
- when "Issue"
- create_issue_note(params[:issue])
- when "MergeRequest"
- create_merge_note(params[:merge_request])
- when "Snippet"
- create_snippet_note(params[:snippet])
- end
- end
-
- def attachments
- return note if markdown
-
- description_message
- end
-
- def activity
- {
- title: "#{user_combined_name} #{link('commented on ' + target, note_url)}",
- subtitle: "in #{project_link}",
- text: formatted_title,
- image: user_avatar
- }
- end
-
- private
-
- def message
- "#{user_combined_name} #{link('commented on ' + target, note_url)} in #{project_link}: *#{formatted_title}*"
- end
-
- def format_title(title)
- title.lines.first.chomp
- end
-
- def formatted_title
- format_title(title)
- end
-
- def create_issue_note(issue)
- ["issue #{Issue.reference_prefix}#{issue[:iid]}", issue[:title]]
- end
-
- def create_commit_note(commit)
- commit_sha = Commit.truncate_sha(commit[:id])
-
- ["commit #{commit_sha}", commit[:message]]
- end
-
- def create_merge_note(merge_request)
- ["merge request #{MergeRequest.reference_prefix}#{merge_request[:iid]}", merge_request[:title]]
- end
-
- def create_snippet_note(snippet)
- ["snippet #{Snippet.reference_prefix}#{snippet[:id]}", snippet[:title]]
- end
-
- def description_message
- [{ text: format(note), color: attachment_color }]
- end
-
- def project_link
- link(project_name, project_url)
- end
- end
-end
diff --git a/app/models/project_services/chat_message/pipeline_message.rb b/app/models/project_services/chat_message/pipeline_message.rb
deleted file mode 100644
index f4c6938fa78..00000000000
--- a/app/models/project_services/chat_message/pipeline_message.rb
+++ /dev/null
@@ -1,265 +0,0 @@
-# frozen_string_literal: true
-
-module ChatMessage
- class PipelineMessage < BaseMessage
- MAX_VISIBLE_JOBS = 10
-
- attr_reader :user
- attr_reader :ref_type
- attr_reader :ref
- attr_reader :status
- attr_reader :detailed_status
- attr_reader :duration
- attr_reader :finished_at
- attr_reader :pipeline_id
- attr_reader :failed_stages
- attr_reader :failed_jobs
-
- attr_reader :project
- attr_reader :commit
- attr_reader :committer
- attr_reader :pipeline
-
- def initialize(data)
- super
-
- @user = data[:user]
- @user_name = data.dig(:user, :username) || 'API'
-
- pipeline_attributes = data[:object_attributes]
- @ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
- @ref = pipeline_attributes[:ref]
- @status = pipeline_attributes[:status]
- @detailed_status = pipeline_attributes[:detailed_status]
- @duration = pipeline_attributes[:duration].to_i
- @finished_at = pipeline_attributes[:finished_at] ? Time.parse(pipeline_attributes[:finished_at]).to_i : nil
- @pipeline_id = pipeline_attributes[:id]
-
- # Get list of jobs that have actually failed (after exhausting all retries)
- @failed_jobs = actually_failed_jobs(Array(data[:builds]))
- @failed_stages = @failed_jobs.map { |j| j[:stage] }.uniq
-
- @project = Project.find(data[:project][:id])
- @commit = project.commit_by(oid: data[:commit][:id])
- @committer = commit.committer
- @pipeline = Ci::Pipeline.find(pipeline_id)
- end
-
- def pretext
- ''
- end
-
- def attachments
- return message if markdown
-
- [{
- fallback: format(message),
- color: attachment_color,
- author_name: user_combined_name,
- author_icon: user_avatar,
- author_link: author_url,
- title: s_("ChatMessage|Pipeline #%{pipeline_id} %{humanized_status} in %{duration}") %
- {
- pipeline_id: pipeline_id,
- humanized_status: humanized_status,
- duration: pretty_duration(duration)
- },
- title_link: pipeline_url,
- fields: attachments_fields,
- footer: project.name,
- footer_icon: project.avatar_url(only_path: false),
- ts: finished_at
- }]
- end
-
- def activity
- {
- title: s_("ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}") %
- {
- pipeline_link: pipeline_link,
- ref_type: ref_type,
- ref_link: ref_link,
- user_combined_name: user_combined_name,
- humanized_status: humanized_status
- },
- subtitle: s_("ChatMessage|in %{project_link}") % { project_link: project_link },
- text: s_("ChatMessage|in %{duration}") % { duration: pretty_duration(duration) },
- image: user_avatar || ''
- }
- end
-
- private
-
- def actually_failed_jobs(builds)
- succeeded_job_names = builds.map { |b| b[:name] if b[:status] == 'success' }.compact.uniq
-
- failed_jobs = builds.select do |build|
- # Select jobs which doesn't have a successful retry
- build[:status] == 'failed' && !succeeded_job_names.include?(build[:name])
- end
-
- failed_jobs.uniq { |job| job[:name] }.reverse
- end
-
- def failed_stages_field
- {
- title: s_("ChatMessage|Failed stage").pluralize(failed_stages.length),
- value: Slack::Messenger::Util::LinkFormatter.format(failed_stages_links),
- short: true
- }
- end
-
- def failed_jobs_field
- {
- title: s_("ChatMessage|Failed job").pluralize(failed_jobs.length),
- value: Slack::Messenger::Util::LinkFormatter.format(failed_jobs_links),
- short: true
- }
- end
-
- def yaml_error_field
- {
- title: s_("ChatMessage|Invalid CI config YAML file"),
- value: pipeline.yaml_errors,
- short: false
- }
- end
-
- def attachments_fields
- fields = [
- {
- title: ref_type == "tag" ? s_("ChatMessage|Tag") : s_("ChatMessage|Branch"),
- value: Slack::Messenger::Util::LinkFormatter.format(ref_link),
- short: true
- },
- {
- title: s_("ChatMessage|Commit"),
- value: Slack::Messenger::Util::LinkFormatter.format(commit_link),
- short: true
- }
- ]
-
- fields << failed_stages_field if failed_stages.any?
- fields << failed_jobs_field if failed_jobs.any?
- fields << yaml_error_field if pipeline.has_yaml_errors?
-
- fields
- end
-
- def message
- s_("ChatMessage|%{project_link}: Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status} in %{duration}") %
- {
- project_link: project_link,
- pipeline_link: pipeline_link,
- ref_type: ref_type,
- ref_link: ref_link,
- user_combined_name: user_combined_name,
- humanized_status: humanized_status,
- duration: pretty_duration(duration)
- }
- end
-
- def humanized_status
- case status
- when 'success'
- detailed_status == "passed with warnings" ? s_("ChatMessage|has passed with warnings") : s_("ChatMessage|has passed")
- when 'failed'
- s_("ChatMessage|has failed")
- else
- status
- end
- end
-
- def attachment_color
- case status
- when 'success'
- detailed_status == 'passed with warnings' ? 'warning' : 'good'
- else
- 'danger'
- end
- end
-
- def ref_url
- if ref_type == 'tag'
- "#{project_url}/-/tags/#{ref}"
- else
- "#{project_url}/-/commits/#{ref}"
- end
- end
-
- def ref_link
- "[#{ref}](#{ref_url})"
- end
-
- def project_url
- project.web_url
- end
-
- def project_link
- "[#{project.name}](#{project_url})"
- end
-
- def pipeline_failed_jobs_url
- "#{project_url}/-/pipelines/#{pipeline_id}/failures"
- end
-
- def pipeline_url
- if failed_jobs.any?
- pipeline_failed_jobs_url
- else
- "#{project_url}/-/pipelines/#{pipeline_id}"
- end
- end
-
- def pipeline_link
- "[##{pipeline_id}](#{pipeline_url})"
- end
-
- def job_url(job)
- "#{project_url}/-/jobs/#{job[:id]}"
- end
-
- def job_link(job)
- "[#{job[:name]}](#{job_url(job)})"
- end
-
- def failed_jobs_links
- failed = failed_jobs.slice(0, MAX_VISIBLE_JOBS)
- truncated = failed_jobs.slice(MAX_VISIBLE_JOBS, failed_jobs.size)
-
- failed_links = failed.map { |job| job_link(job) }
-
- unless truncated.blank?
- failed_links << s_("ChatMessage|and [%{count} more](%{pipeline_failed_jobs_url})") % {
- count: truncated.size,
- pipeline_failed_jobs_url: pipeline_failed_jobs_url
- }
- end
-
- failed_links.join(I18n.translate(:'support.array.words_connector'))
- end
-
- def stage_link(stage)
- # All stages link to the pipeline page
- "[#{stage}](#{pipeline_url})"
- end
-
- def failed_stages_links
- failed_stages.map { |s| stage_link(s) }.join(I18n.translate(:'support.array.words_connector'))
- end
-
- def commit_url
- Gitlab::UrlBuilder.build(commit)
- end
-
- def commit_link
- "[#{commit.title}](#{commit_url})"
- end
-
- def author_url
- return unless user && committer
-
- Gitlab::UrlBuilder.build(committer)
- end
- end
-end
diff --git a/app/models/project_services/chat_message/push_message.rb b/app/models/project_services/chat_message/push_message.rb
deleted file mode 100644
index c8e70a69c88..00000000000
--- a/app/models/project_services/chat_message/push_message.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-# frozen_string_literal: true
-
-module ChatMessage
- class PushMessage < BaseMessage
- attr_reader :after
- attr_reader :before
- attr_reader :commits
- attr_reader :ref
- attr_reader :ref_type
-
- def initialize(params)
- super
-
- @after = params[:after]
- @before = params[:before]
- @commits = params.fetch(:commits, [])
- @ref_type = Gitlab::Git.tag_ref?(params[:ref]) ? 'tag' : 'branch'
- @ref = Gitlab::Git.ref_name(params[:ref])
- end
-
- def attachments
- return [] if new_branch? || removed_branch?
- return commit_messages if markdown
-
- commit_message_attachments
- end
-
- def activity
- {
- title: humanized_action(short: true),
- subtitle: "in #{project_link}",
- text: compare_link,
- image: user_avatar
- }
- end
-
- private
-
- def humanized_action(short: false)
- action, ref_link, target_link = compose_action_details
- text = [user_combined_name, action, ref_type, ref_link]
- text << target_link unless short
- text.join(' ')
- end
-
- def message
- humanized_action
- end
-
- def format(string)
- Slack::Messenger::Util::LinkFormatter.format(string)
- end
-
- def commit_messages
- commits.map { |commit| compose_commit_message(commit) }.join("\n\n")
- end
-
- def commit_message_attachments
- [{ text: format(commit_messages), color: attachment_color }]
- end
-
- def compose_commit_message(commit)
- author = commit[:author][:name]
- id = Commit.truncate_sha(commit[:id])
- title = commit[:title]
-
- url = commit[:url]
-
- "[#{id}](#{url}): #{title} - #{author}"
- end
-
- def new_branch?
- Gitlab::Git.blank_ref?(before)
- end
-
- def removed_branch?
- Gitlab::Git.blank_ref?(after)
- end
-
- def ref_url
- if ref_type == 'tag'
- "#{project_url}/-/tags/#{ref}"
- else
- "#{project_url}/commits/#{ref}"
- end
- end
-
- def compare_url
- "#{project_url}/compare/#{before}...#{after}"
- end
-
- def ref_link
- "[#{ref}](#{ref_url})"
- end
-
- def project_link
- "[#{project_name}](#{project_url})"
- end
-
- def compare_link
- "[Compare changes](#{compare_url})"
- end
-
- def compose_action_details
- if new_branch?
- ['pushed new', ref_link, "to #{project_link}"]
- elsif removed_branch?
- ['removed', ref, "from #{project_link}"]
- else
- ['pushed to', ref_link, "of #{project_link} (#{compare_link})"]
- end
- end
-
- def attachment_color
- '#345'
- end
- end
-end
diff --git a/app/models/project_services/chat_message/wiki_page_message.rb b/app/models/project_services/chat_message/wiki_page_message.rb
deleted file mode 100644
index ebe7abb379f..00000000000
--- a/app/models/project_services/chat_message/wiki_page_message.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-
-module ChatMessage
- class WikiPageMessage < BaseMessage
- attr_reader :title
- attr_reader :wiki_page_url
- attr_reader :action
- attr_reader :description
-
- def initialize(params)
- super
-
- obj_attr = params[:object_attributes]
- obj_attr = HashWithIndifferentAccess.new(obj_attr)
- @title = obj_attr[:title]
- @wiki_page_url = obj_attr[:url]
- @description = obj_attr[:message]
-
- @action =
- case obj_attr[:action]
- when "create"
- "created"
- when "update"
- "edited"
- end
- end
-
- def attachments
- return description if markdown
-
- description_message
- end
-
- def activity
- {
- title: "#{user_combined_name} #{action} #{wiki_page_link}",
- subtitle: "in #{project_link}",
- text: title,
- image: user_avatar
- }
- end
-
- private
-
- def message
- "#{user_combined_name} #{action} #{wiki_page_link} in #{project_link}: *#{title}*"
- end
-
- def description_message
- [{ text: format(@description), color: attachment_color }]
- end
-
- def project_link
- "[#{project_name}](#{project_url})"
- end
-
- def wiki_page_link
- "[wiki page](#{wiki_page_url})"
- end
- end
-end
diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb
index 4a99842b4d5..2f841bf903e 100644
--- a/app/models/project_services/chat_notification_service.rb
+++ b/app/models/project_services/chat_notification_service.rb
@@ -2,7 +2,7 @@
# Base class for Chat notifications services
# This class is not meant to be used directly, but only to inherit from.
-class ChatNotificationService < Service
+class ChatNotificationService < Integration
include ChatMessage
include NotificationBranchSelection
@@ -15,9 +15,14 @@ class ChatNotificationService < Service
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
+ 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] })
@@ -25,12 +30,14 @@ class ChatNotificationService < Service
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
@@ -65,7 +72,20 @@ class ChatNotificationService < Service
{ 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: '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
@@ -136,11 +156,17 @@ class ChatNotificationService < Service
def notify_label?(data)
return true unless SUPPORTED_EVENTS_FOR_LABEL_FILTER.include?(data[:object_kind]) && labels_to_be_notified.present?
- issue_labels = data.dig(:issue, :labels) || []
- merge_request_labels = data.dig(:merge_request, :labels) || []
- label_titles = (issue_labels + merge_request_labels).pluck(:title)
+ labels = data.dig(:issue, :labels) || data.dig(:merge_request, :labels)
+
+ return false if labels.nil?
- (labels_to_be_notified_list & label_titles).any?
+ 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)
@@ -159,19 +185,19 @@ class ChatNotificationService < Service
def get_message(object_kind, data)
case object_kind
when "push", "tag_push"
- ChatMessage::PushMessage.new(data) if notify_for_ref?(data)
+ Integrations::ChatMessage::PushMessage.new(data) if notify_for_ref?(data)
when "issue"
- ChatMessage::IssueMessage.new(data) unless update?(data)
+ Integrations::ChatMessage::IssueMessage.new(data) unless update?(data)
when "merge_request"
- ChatMessage::MergeMessage.new(data) unless update?(data)
+ Integrations::ChatMessage::MergeMessage.new(data) unless update?(data)
when "note"
- ChatMessage::NoteMessage.new(data)
+ Integrations::ChatMessage::NoteMessage.new(data)
when "pipeline"
- ChatMessage::PipelineMessage.new(data) if should_pipeline_be_notified?(data)
+ Integrations::ChatMessage::PipelineMessage.new(data) if should_pipeline_be_notified?(data)
when "wiki_page"
- ChatMessage::WikiPageMessage.new(data)
+ Integrations::ChatMessage::WikiPageMessage.new(data)
when "deployment"
- ChatMessage::DeploymentMessage.new(data)
+ Integrations::ChatMessage::DeploymentMessage.new(data)
end
end
diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb
index 29edb9ec16f..0733da761d5 100644
--- a/app/models/project_services/ci_service.rb
+++ b/app/models/project_services/ci_service.rb
@@ -3,7 +3,7 @@
# Base class for CI services
# List methods you need to implement to get your CI service
# working with GitLab merge requests
-class CiService < Service
+class CiService < Integration
default_value_for :category, 'ci'
def valid_token?(token)
diff --git a/app/models/project_services/confluence_service.rb b/app/models/project_services/confluence_service.rb
deleted file mode 100644
index 8a6f4de540c..00000000000
--- a/app/models/project_services/confluence_service.rb
+++ /dev/null
@@ -1,91 +0,0 @@
-# frozen_string_literal: true
-
-class ConfluenceService < Service
- include ActionView::Helpers::UrlHelper
-
- VALID_SCHEME_MATCH = %r{\Ahttps?\Z}.freeze
- VALID_HOST_MATCH = %r{\A.+\.atlassian\.net\Z}.freeze
- VALID_PATH_MATCH = %r{\A/wiki(/|\Z)}.freeze
-
- prop_accessor :confluence_url
-
- validates :confluence_url, presence: true, if: :activated?
- validate :validate_confluence_url_is_cloud, if: :activated?
-
- after_commit :cache_project_has_confluence
-
- def self.to_param
- 'confluence'
- end
-
- def self.supported_events
- %w()
- end
-
- def title
- s_('ConfluenceService|Confluence Workspace')
- end
-
- def description
- s_('ConfluenceService|Connect a Confluence Cloud Workspace to GitLab')
- end
-
- def help
- return unless project&.wiki_enabled?
-
- if activated?
- wiki_url = project.wiki.web_url
-
- s_(
- 'ConfluenceService|Your GitLab Wiki can be accessed here: %{wiki_link}. To re-enable your GitLab Wiki, disable this integration' %
- { wiki_link: link_to(wiki_url, wiki_url) }
- ).html_safe
- else
- s_('ConfluenceService|Enabling the Confluence Workspace will disable the default GitLab Wiki. Your GitLab Wiki data will be saved and you can always re-enable it later by turning off this integration').html_safe
- end
- end
-
- def fields
- [
- {
- type: 'text',
- name: 'confluence_url',
- title: 'Confluence Cloud Workspace URL',
- placeholder: s_('ConfluenceService|The URL of the Confluence Workspace'),
- required: true
- }
- ]
- end
-
- def can_test?
- false
- end
-
- private
-
- def validate_confluence_url_is_cloud
- unless confluence_uri_valid?
- errors.add(:confluence_url, 'URL must be to a Confluence Cloud Workspace hosted on atlassian.net')
- end
- end
-
- def confluence_uri_valid?
- return false unless confluence_url
-
- uri = URI.parse(confluence_url)
-
- (uri.scheme&.match(VALID_SCHEME_MATCH) &&
- uri.host&.match(VALID_HOST_MATCH) &&
- uri.path&.match(VALID_PATH_MATCH)).present?
-
- rescue URI::InvalidURIError
- false
- end
-
- def cache_project_has_confluence
- return unless project && !project.destroyed?
-
- project.project_setting.save! unless project.project_setting.persisted?
- project.project_setting.update_column(:has_confluence, active?)
- end
-end
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
index aab8661ec55..6f99d104904 100644
--- a/app/models/project_services/custom_issue_tracker_service.rb
+++ b/app/models/project_services/custom_issue_tracker_service.rb
@@ -1,25 +1,23 @@
# 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
- 'Custom Issue Tracker'
+ s_('IssueTracker|Custom issue tracker')
end
def description
- s_('IssueTracker|Custom issue tracker')
+ s_("IssueTracker|Use a custom issue tracker as this project's issue tracker.")
end
- def self.to_param
- 'custom_issue_tracker'
+ 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 fields
- [
- { type: 'text', name: 'project_url', title: _('Project URL'), required: true },
- { type: 'text', name: 'issues_url', title: s_('ProjectService|Issue URL'), required: true },
- { type: 'text', name: 'new_issue_url', title: s_('ProjectService|New issue URL'), required: true }
- ]
+ 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
index 12ebf260e08..ca4dc0375fb 100644
--- a/app/models/project_services/data_fields.rb
+++ b/app/models/project_services/data_fields.rb
@@ -42,9 +42,9 @@ module DataFields
end
included do
- has_one :issue_tracker_data, autosave: true
- has_one :jira_tracker_data, autosave: true
- has_one :open_project_tracker_data, autosave: true
+ 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
diff --git a/app/models/project_services/datadog_service.rb b/app/models/project_services/datadog_service.rb
deleted file mode 100644
index 9a2d99c46c9..00000000000
--- a/app/models/project_services/datadog_service.rb
+++ /dev/null
@@ -1,144 +0,0 @@
-# frozen_string_literal: true
-
-class DatadogService < Service
- DEFAULT_SITE = 'datadoghq.com'
- URL_TEMPLATE = 'https://webhooks-http-intake.logs.%{datadog_site}/v1/input/'
- URL_TEMPLATE_API_KEYS = 'https://app.%{datadog_site}/account/settings#api'
- URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_SITE}/account_management/api-app-keys/"
-
- SUPPORTED_EVENTS = %w[
- pipeline job
- ].freeze
-
- prop_accessor :datadog_site, :api_url, :api_key, :datadog_service, :datadog_env
-
- with_options if: :activated? do
- validates :api_key, presence: true, format: { with: /\A\w+\z/ }
- validates :datadog_site, format: { with: /\A[\w\.]+\z/, allow_blank: true }
- validates :api_url, public_url: { allow_blank: true }
- validates :datadog_site, presence: true, unless: -> (obj) { obj.api_url.present? }
- validates :api_url, presence: true, unless: -> (obj) { obj.datadog_site.present? }
- end
-
- after_save :compose_service_hook, if: :activated?
-
- def initialize_properties
- super
-
- self.datadog_site ||= DEFAULT_SITE
- end
-
- def self.supported_events
- SUPPORTED_EVENTS
- end
-
- def self.default_test_event
- 'pipeline'
- end
-
- def configurable_events
- [] # do not allow to opt out of required hooks
- end
-
- def title
- 'Datadog'
- end
-
- def description
- 'Trace your GitLab pipelines with Datadog'
- end
-
- def help
- nil
- # Maybe adding something in the future
- # We could link to static help pages as well
- # [More information](#{Gitlab::Routing.url_helpers.help_page_url('integration/datadog')})"
- end
-
- def self.to_param
- 'datadog'
- end
-
- def fields
- [
- {
- type: 'text',
- name: 'datadog_site',
- placeholder: DEFAULT_SITE,
- help: 'Choose the Datadog site to send data to. Set to "datadoghq.eu" to send data to the EU site',
- required: false
- },
- {
- type: 'text',
- name: 'api_url',
- title: 'API URL',
- help: '(Advanced) Define the full URL for your Datadog site directly',
- required: false
- },
- {
- type: 'password',
- name: 'api_key',
- title: _('API key'),
- non_empty_password_title: s_('ProjectService|Enter new API key'),
- non_empty_password_help: s_('ProjectService|Leave blank to use your current API key'),
- help: "<a href=\"#{api_keys_url}\" target=\"_blank\">API key</a> used for authentication with Datadog",
- required: true
- },
- {
- type: 'text',
- name: 'datadog_service',
- title: 'Service',
- placeholder: 'gitlab-ci',
- help: 'Name of this GitLab instance that all data will be tagged with'
- },
- {
- type: 'text',
- name: 'datadog_env',
- title: 'Env',
- help: 'The environment tag that traces will be tagged with'
- }
- ]
- end
-
- def compose_service_hook
- hook = service_hook || build_service_hook
- hook.url = hook_url
- hook.save
- end
-
- def hook_url
- url = api_url.presence || sprintf(URL_TEMPLATE, datadog_site: datadog_site)
- url = URI.parse(url)
- url.path = File.join(url.path || '/', api_key)
- query = { service: datadog_service.presence, env: datadog_env.presence }.compact
- url.query = query.to_query unless query.empty?
- url.to_s
- end
-
- def api_keys_url
- return URL_API_KEYS_DOCS unless datadog_site.presence
-
- sprintf(URL_TEMPLATE_API_KEYS, datadog_site: datadog_site)
- end
-
- def execute(data)
- return if project.disabled_services.include?(to_param)
-
- object_kind = data[:object_kind]
- object_kind = 'job' if object_kind == 'build'
- return unless supported_events.include?(object_kind)
-
- service_hook.execute(data, "#{object_kind} hook")
- end
-
- def test(data)
- begin
- result = execute(data)
- return { success: false, result: result[:message] } if result[:http_status] != 200
- rescue StandardError => error
- return { success: false, result: error }
- end
-
- { success: true, result: result[:message] }
- end
-end
diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb
deleted file mode 100644
index cdb69684d16..00000000000
--- a/app/models/project_services/emails_on_push_service.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-# frozen_string_literal: true
-
-class EmailsOnPushService < Service
- include NotificationBranchSelection
-
- RECIPIENTS_LIMIT = 750
-
- boolean_accessor :send_from_committer_email
- boolean_accessor :disable_diffs
- prop_accessor :recipients, :branches_to_be_notified
- validates :recipients, presence: true, if: :validate_recipients?
- validate :number_of_recipients_within_limit, if: :validate_recipients?
-
- def self.valid_recipients(recipients)
- recipients.split.select do |recipient|
- recipient.include?('@')
- end.uniq(&:downcase)
- end
-
- def title
- s_('EmailsOnPushService|Emails on push')
- end
-
- def description
- s_('EmailsOnPushService|Email the commits and diff of each push to a list of recipients.')
- end
-
- def self.to_param
- 'emails_on_push'
- end
-
- def self.supported_events
- %w(push tag_push)
- end
-
- def initialize_properties
- super
-
- self.branches_to_be_notified = 'all' if branches_to_be_notified.nil?
- end
-
- def execute(push_data)
- return unless supported_events.include?(push_data[:object_kind])
- return if project.emails_disabled?
- return unless notify_for_ref?(push_data)
-
- EmailsOnPushWorker.perform_async(
- project_id,
- recipients,
- push_data,
- send_from_committer_email: send_from_committer_email?,
- disable_diffs: disable_diffs?
- )
- end
-
- def notify_for_ref?(push_data)
- return true if push_data[:object_kind] == 'tag_push'
- return true if push_data.dig(:object_attributes, :tag)
-
- notify_for_branch?(push_data)
- end
-
- def send_from_committer_email?
- Gitlab::Utils.to_boolean(self.send_from_committer_email)
- end
-
- def disable_diffs?
- Gitlab::Utils.to_boolean(self.disable_diffs)
- end
-
- def fields
- domains = Notify.allowed_email_domains.map { |domain| "user@#{domain}" }.join(", ")
- [
- { type: 'checkbox', name: 'send_from_committer_email', title: s_("EmailsOnPushService|Send from committer"),
- help: s_("EmailsOnPushService|Send notifications from the committer's email address if the domain matches the domain used by your GitLab instance (such as %{domains}).") % { domains: domains } },
- { type: 'checkbox', name: 'disable_diffs', title: s_("EmailsOnPushService|Disable code diffs"),
- help: s_("EmailsOnPushService|Don't include possibly sensitive code diffs in notification body.") },
- { type: 'select', name: 'branches_to_be_notified', choices: branch_choices },
- {
- type: 'textarea',
- name: 'recipients',
- placeholder: s_('EmailsOnPushService|tanuki@example.com gitlab@example.com'),
- help: s_('EmailsOnPushService|Emails separated by whitespace.')
- }
- ]
- end
-
- private
-
- def number_of_recipients_within_limit
- return if recipients.blank?
-
- if self.class.valid_recipients(recipients).size > RECIPIENTS_LIMIT
- errors.add(:recipients, s_("EmailsOnPushService|can't exceed %{recipients_limit}") % { recipients_limit: RECIPIENTS_LIMIT })
- end
- end
-end
diff --git a/app/models/project_services/ewm_service.rb b/app/models/project_services/ewm_service.rb
index af402e50292..90fcbb10d2b 100644
--- a/app/models/project_services/ewm_service.rb
+++ b/app/models/project_services/ewm_service.rb
@@ -1,6 +1,8 @@
# 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)
@@ -12,7 +14,12 @@ class EwmService < IssueTrackerService
end
def description
- s_('IssueTracker|EWM work items tracker')
+ 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
diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb
index c41783d1af4..f49b008533d 100644
--- a/app/models/project_services/external_wiki_service.rb
+++ b/app/models/project_services/external_wiki_service.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
-class ExternalWikiService < Service
+class ExternalWikiService < Integration
include ActionView::Helpers::UrlHelper
+
prop_accessor :external_wiki_url
validates :external_wiki_url, presence: true, public_url: true, if: :activated?
@@ -39,7 +40,7 @@ class ExternalWikiService < Service
def execute(_data)
response = Gitlab::HTTP.get(properties['external_wiki_url'], verify: true)
response.body if response.code == 200
- rescue
+ rescue StandardError
nil
end
diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb
index e721fded1d9..7aae5af7454 100644
--- a/app/models/project_services/flowdock_service.rb
+++ b/app/models/project_services/flowdock_service.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
-class FlowdockService < Service
+class FlowdockService < Integration
+ include ActionView::Helpers::UrlHelper
+
prop_accessor :token
validates :token, presence: true, if: :activated?
@@ -9,7 +11,12 @@ class FlowdockService < Service
end
def description
- s_('FlowdockService|Flowdock is a collaboration web app for technical teams.')
+ 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
@@ -18,7 +25,7 @@ class FlowdockService < Service
def fields
[
- { type: 'text', name: 'token', placeholder: s_('FlowdockService|Flowdock Git source token'), required: true }
+ { type: 'text', name: 'token', placeholder: s_('FlowdockService|1b609b52537...'), required: true, help: 'Enter your Flowdock token.' }
]
end
diff --git a/app/models/project_services/hangouts_chat_service.rb b/app/models/project_services/hangouts_chat_service.rb
index 299a306add7..6e7708a169f 100644
--- a/app/models/project_services/hangouts_chat_service.rb
+++ b/app/models/project_services/hangouts_chat_service.rb
@@ -3,12 +3,14 @@
require 'hangouts_chat'
class HangoutsChatService < ChatNotificationService
+ include ActionView::Helpers::UrlHelper
+
def title
- 'Hangouts Chat'
+ 'Google Chat'
end
def description
- 'Receive event notifications in Google Hangouts Chat'
+ 'Send notifications from GitLab to a room in Google Chat.'
end
def self.to_param
@@ -16,13 +18,8 @@ class HangoutsChatService < ChatNotificationService
end
def help
- 'This service sends notifications about projects events to Google Hangouts Chat room.<br />
- To set up this service:
- <ol>
- <li><a href="https://developers.google.com/hangouts/chat/how-tos/webhooks">Set up an incoming webhook for your room</a>. All notifications will come to this room.</li>
- <li>Paste the <strong>Webhook URL</strong> into the field below.</li>
- <li>Select events below to enable notifications.</li>
- </ol>'
+ 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)
@@ -42,7 +39,7 @@ class HangoutsChatService < ChatNotificationService
def default_fields
[
- { type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
+ { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}" },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
{ type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
]
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index cd49c6d253d..71d8e7bfac4 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -1,54 +1,17 @@
# frozen_string_literal: true
-class HipchatService < Service
- include ActionView::Helpers::SanitizeHelper
-
- MAX_COMMITS = 3
- HIPCHAT_ALLOWED_TAGS = %w[
- a b i strong em br img pre code
- table th tr td caption colgroup col thead tbody tfoot
- ul ol li dl dt dd
- ].freeze
-
- prop_accessor :token, :room, :server, :color, :api_version
- boolean_accessor :notify_only_broken_pipelines, :notify
- validates :token, presence: true, if: :activated?
-
- def initialize_properties
- if properties.nil?
- self.properties = {}
- self.notify_only_broken_pipelines = true
- end
- end
-
- def title
- 'HipChat'
- end
-
- def description
- 'Private group chat and IM'
- end
+# 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 fields
- [
- { type: 'text', name: 'token', placeholder: 'Room token', required: true },
- { type: 'text', name: 'room', placeholder: 'Room name or ID' },
- { type: 'checkbox', name: 'notify' },
- { type: 'select', name: 'color', choices: %w(yellow red green purple gray random) },
- { type: 'text', name: 'api_version', title: _('API version'),
- placeholder: 'Leave blank for default (v2)' },
- { type: 'text', name: 'server',
- placeholder: 'Leave blank for default. https://hipchat.example.com' },
- { type: 'checkbox', name: 'notify_only_broken_pipelines' }
- ]
- end
-
def self.supported_events
- %w(push issue confidential_issue merge_request note confidential_note tag_push pipeline)
+ []
end
def execute(data)
@@ -56,96 +19,14 @@ class HipchatService < Service
# HipChat is unusable anyway, so do nothing in this method
end
- def test(data)
- begin
- result = execute(data)
- rescue StandardError => error
- return { success: false, result: error }
- end
-
- { success: true, result: result }
- end
-
private
- def message_options(data = nil)
- { notify: notify.present? && Gitlab::Utils.to_boolean(notify), color: message_color(data) }
- end
-
- def render_line(text)
- markdown(text.lines.first.chomp, pipeline: :single_line) if text
- end
-
- def markdown(text, options = {})
- return "" unless text
-
- context = {
- project: project,
- pipeline: :email
- }
-
- Banzai.render(text, context)
-
- context.merge!(options)
-
- html = Banzai.render_and_post_process(text, context)
- sanitized_html = sanitize(html, tags: HIPCHAT_ALLOWED_TAGS, attributes: %w[href title alt])
-
- sanitized_html.truncate(200, separator: ' ', omission: '...')
- end
-
- def format_title(title)
- "<b>#{render_line(title)}</b>"
- end
-
- def message_color(data)
- pipeline_status_color(data) || color || 'yellow'
- end
-
- def pipeline_status_color(data)
- return unless data && data[:object_kind] == 'pipeline'
-
- case data[:object_attributes][:status]
- when 'success'
- 'green'
- else
- 'red'
- end
- end
-
- def project_name
- project.full_name.gsub(/\s/, '')
- end
-
- def project_url
- project.web_url
- end
-
- def project_link
- "<a href=\"#{project_url}\">#{project_name}</a>"
- end
-
- def update?(data)
- data[:object_attributes][:action] == 'update'
- end
-
- def humanized_status(status)
- case status
- when 'success'
- 'passed'
- else
- status
- end
- end
+ def prevent_save
+ errors.add(:base, _('HipChat endpoint is deprecated and should not be created or modified.'))
- def should_pipeline_be_notified?(data)
- case data[:object_attributes][:status]
- when 'success'
- !notify_only_broken_pipelines?
- when 'failed'
- true
- else
- false
- end
+ # 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
index 4f1ce16ebb2..5cca620c659 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -2,7 +2,7 @@
require 'uri'
-class IrkerService < Service
+class IrkerService < Integration
prop_accessor :server_host, :server_port, :default_irc_uri
prop_accessor :recipients, :channels
boolean_accessor :colorize_messages
@@ -15,8 +15,7 @@ class IrkerService < Service
end
def description
- 'Send IRC messages, on update, to a list of recipients through an Irker '\
- 'gateway.'
+ 'Send IRC messages.'
end
def self.to_param
@@ -103,7 +102,7 @@ class IrkerService < Service
begin
new_recipient = URI.join(default_irc_uri, '/', recipient).to_s
uri = consider_uri(URI.parse(new_recipient))
- rescue
+ rescue StandardError
log_error("Unable to create a valid URL", default_irc_uri: default_irc_uri, recipient: recipient)
end
end
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index 19a5b4a74bb..099e3c336dd 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class IssueTrackerService < Service
+class IssueTrackerService < Integration
validate :one_issue_tracker, if: :activated?, on: :manual_change
# TODO: we can probably just delegate as part of
@@ -73,9 +73,9 @@ class IssueTrackerService < Service
def fields
[
- { type: 'text', name: 'project_url', title: _('Project URL'), required: true },
- { type: 'text', name: 'issues_url', title: s_('ProjectService|Issue URL'), required: true },
- { type: 'text', name: 'new_issue_url', title: s_('ProjectService|New issue URL'), required: true }
+ { 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
@@ -143,10 +143,10 @@ class IssueTrackerService < Service
return if template? || instance?
return if project.blank?
- if project.services.external_issue_trackers.where.not(id: id).any?
+ 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_if_ee('EE::IssueTrackerService')
+IssueTrackerService.prepend_mod_with('IssueTrackerService')
diff --git a/app/models/project_services/jenkins_service.rb b/app/models/project_services/jenkins_service.rb
index 6a123517b84..990a35cd617 100644
--- a/app/models/project_services/jenkins_service.rb
+++ b/app/models/project_services/jenkins_service.rb
@@ -64,12 +64,12 @@ class JenkinsService < CiService
end
def description
- s_('An extendable open source CI/CD server.')
+ 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_('Trigger Jenkins builds 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 }
+ 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
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 3e14bf44c12..5cd6e79eb1d 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -106,9 +106,8 @@ class JiraService < IssueTrackerService
end
def help
- "You need to configure Jira before enabling this service. For more details
- read the
- [Jira service documentation](#{help_page_url('user/project/integrations/jira')})."
+ 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
@@ -116,7 +115,7 @@ class JiraService < IssueTrackerService
end
def description
- s_('JiraService|Track issues in Jira')
+ s_("JiraService|Use Jira as this project's issue tracker.")
end
def self.to_param
@@ -305,7 +304,7 @@ class JiraService < IssueTrackerService
)
true
- rescue => error
+ rescue StandardError => error
log_error(
"Issue transition failed",
error: {
@@ -490,7 +489,7 @@ class JiraService < IssueTrackerService
# Handle errors when doing Jira API calls
def jira_request
yield
- rescue => error
+ rescue StandardError => error
@error = error
log_error("Error sending message", client_url: client_url, error: @error.message)
nil
@@ -539,4 +538,4 @@ class JiraService < IssueTrackerService
end
end
-JiraService.prepend_if_ee('EE::JiraService')
+JiraService.prepend_mod_with('JiraService')
diff --git a/app/models/project_services/microsoft_teams_service.rb b/app/models/project_services/microsoft_teams_service.rb
index 803c1255195..1d2067067da 100644
--- a/app/models/project_services/microsoft_teams_service.rb
+++ b/app/models/project_services/microsoft_teams_service.rb
@@ -6,7 +6,7 @@ class MicrosoftTeamsService < ChatNotificationService
end
def description
- 'Receive event notifications in Microsoft Teams'
+ 'Send notifications about project events to Microsoft Teams.'
end
def self.to_param
diff --git a/app/models/project_services/monitoring_service.rb b/app/models/project_services/monitoring_service.rb
index 1b530a8247b..ea65a200027 100644
--- a/app/models/project_services/monitoring_service.rb
+++ b/app/models/project_services/monitoring_service.rb
@@ -4,7 +4,7 @@
#
# These services integrate with a deployment solution like Prometheus
# to provide additional features for environments.
-class MonitoringService < Service
+class MonitoringService < Integration
default_value_for :category, 'monitoring'
def self.supported_events
diff --git a/app/models/project_services/packagist_service.rb b/app/models/project_services/packagist_service.rb
index 21f0a2b2463..f3ea8c64302 100644
--- a/app/models/project_services/packagist_service.rb
+++ b/app/models/project_services/packagist_service.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class PackagistService < Service
+class PackagistService < Integration
prop_accessor :username, :token, :server
validates :username, presence: true, if: :activated?
@@ -16,7 +16,7 @@ class PackagistService < Service
end
def description
- s_('Integrations|Update your projects on Packagist, the main Composer repository')
+ s_('Integrations|Update your Packagist projects.')
end
def self.to_param
diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb
index 0a0a41c525c..4603193ac8e 100644
--- a/app/models/project_services/pipelines_email_service.rb
+++ b/app/models/project_services/pipelines_email_service.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class PipelinesEmailService < Service
+class PipelinesEmailService < Integration
include NotificationBranchSelection
prop_accessor :recipients, :branches_to_be_notified
diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb
index d3fff100964..6e67984591d 100644
--- a/app/models/project_services/pivotaltracker_service.rb
+++ b/app/models/project_services/pivotaltracker_service.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class PivotaltrackerService < Service
+class PivotaltrackerService < Integration
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'
prop_accessor :token, :restrict_to_branch
@@ -11,7 +11,7 @@ class PivotaltrackerService < Service
end
def description
- s_('PivotalTrackerService|Project Management Software (Source Commits Endpoint)')
+ s_('PivotalTrackerService|Add commit messages as comments to PivotalTracker stories.')
end
def self.to_param
diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb
index 1781ec7456d..89765fbdf41 100644
--- a/app/models/project_services/pushover_service.rb
+++ b/app/models/project_services/pushover_service.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class PushoverService < Service
+class PushoverService < Integration
BASE_URI = 'https://api.pushover.net/1'
prop_accessor :api_key, :user_key, :device, :priority, :sound
@@ -11,7 +11,7 @@ class PushoverService < Service
end
def description
- s_('PushoverService|Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop.')
+ s_('PushoverService|Get real-time notifications on your device.')
end
def self.to_param
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
index 26a6cf86bf4..7a0f500209c 100644
--- a/app/models/project_services/redmine_service.rb
+++ b/app/models/project_services/redmine_service.rb
@@ -9,7 +9,7 @@ class RedmineService < IssueTrackerService
end
def description
- s_('IssueTracker|Use Redmine as the issue tracker.')
+ s_("IssueTracker|Use Redmine as this project's issue tracker.")
end
def help
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index 7badcc24870..92a46f8d01f 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -39,7 +39,7 @@ class SlackService < ChatNotificationService
end
def get_message(object_kind, data)
- return ChatMessage::AlertMessage.new(data) if object_kind == 'alert'
+ return Integrations::ChatMessage::AlertMessage.new(data) if object_kind == 'alert'
super
end
diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb
index d436176a52c..37d16737052 100644
--- a/app/models/project_services/slash_commands_service.rb
+++ b/app/models/project_services/slash_commands_service.rb
@@ -2,7 +2,7 @@
# Base class for Chat services
# This class is not meant to be used directly, but only to inherrit from.
-class SlashCommandsService < Service
+class SlashCommandsService < Integration
default_value_for :category, 'chat'
prop_accessor :token
diff --git a/app/models/project_services/unify_circuit_service.rb b/app/models/project_services/unify_circuit_service.rb
index 1a0eebe7d64..5f43388e1c9 100644
--- a/app/models/project_services/unify_circuit_service.rb
+++ b/app/models/project_services/unify_circuit_service.rb
@@ -6,7 +6,7 @@ class UnifyCircuitService < ChatNotificationService
end
def description
- 'Receive event notifications in Unify Circuit'
+ s_('Integrations|Send notifications about project events to Unify Circuit.')
end
def self.to_param
diff --git a/app/models/project_services/webex_teams_service.rb b/app/models/project_services/webex_teams_service.rb
index 4e8281f4e81..3d92d3bb85e 100644
--- a/app/models/project_services/webex_teams_service.rb
+++ b/app/models/project_services/webex_teams_service.rb
@@ -1,12 +1,14 @@
# frozen_string_literal: true
class WebexTeamsService < ChatNotificationService
+ include ActionView::Helpers::UrlHelper
+
def title
- 'Webex Teams'
+ s_("WebexTeamsService|Webex Teams")
end
def description
- 'Receive event notifications in Webex Teams'
+ s_("WebexTeamsService|Send notifications about project events to Webex Teams.")
end
def self.to_param
@@ -14,13 +16,8 @@ class WebexTeamsService < ChatNotificationService
end
def help
- 'This service sends notifications about projects events to a Webex Teams conversation.<br />
- To set up this service:
- <ol>
- <li><a href="https://apphub.webex.com/teams/applications/incoming-webhooks-cisco-systems">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>'
+ 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)
@@ -36,7 +33,7 @@ class WebexTeamsService < ChatNotificationService
def default_fields
[
- { type: 'text', name: 'webhook', placeholder: "e.g. https://api.ciscospark.com/v1/webhooks/incoming/…", required: true },
+ { 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 }
]
diff --git a/app/models/project_services/youtrack_service.rb b/app/models/project_services/youtrack_service.rb
index 30abd0159b3..9760a22a872 100644
--- a/app/models/project_services/youtrack_service.rb
+++ b/app/models/project_services/youtrack_service.rb
@@ -1,6 +1,8 @@
# 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
@@ -17,7 +19,12 @@ class YoutrackService < IssueTrackerService
end
def description
- s_('IssueTracker|YouTrack issue tracker')
+ 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
@@ -26,8 +33,8 @@ class YoutrackService < IssueTrackerService
def fields
[
- { type: 'text', name: 'project_url', title: _('Project URL'), required: true },
- { type: 'text', name: 'issues_url', title: s_('ProjectService|Issue URL'), required: true }
+ { 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