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:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-05-17 18:10:15 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-17 18:10:15 +0300
commit68c476dbd8a2c670aeeebffce8b63b554a3ac7f0 (patch)
treec46b90a5c131d8e8d7fb530f1b8f9390b8eb2613 /app/models/integrations/chat_message
parenta62238de7302e54edafa3407a2dc8ba1a5f96e4d (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/models/integrations/chat_message')
-rw-r--r--app/models/integrations/chat_message/alert_message.rb76
-rw-r--r--app/models/integrations/chat_message/base_message.rb88
-rw-r--r--app/models/integrations/chat_message/deployment_message.rb87
-rw-r--r--app/models/integrations/chat_message/issue_message.rb74
-rw-r--r--app/models/integrations/chat_message/merge_message.rb83
-rw-r--r--app/models/integrations/chat_message/note_message.rb86
-rw-r--r--app/models/integrations/chat_message/pipeline_message.rb267
-rw-r--r--app/models/integrations/chat_message/push_message.rb120
-rw-r--r--app/models/integrations/chat_message/wiki_page_message.rb63
9 files changed, 944 insertions, 0 deletions
diff --git a/app/models/integrations/chat_message/alert_message.rb b/app/models/integrations/chat_message/alert_message.rb
new file mode 100644
index 00000000000..ef0579124fe
--- /dev/null
+++ b/app/models/integrations/chat_message/alert_message.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+module Integrations
+ 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
+end
diff --git a/app/models/integrations/chat_message/base_message.rb b/app/models/integrations/chat_message/base_message.rb
new file mode 100644
index 00000000000..2f70384d3b9
--- /dev/null
+++ b/app/models/integrations/chat_message/base_message.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+module Integrations
+ module ChatMessage
+ class BaseMessage
+ RELATIVE_LINK_REGEX = %r{!\[[^\]]*\]\((/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
+end
diff --git a/app/models/integrations/chat_message/deployment_message.rb b/app/models/integrations/chat_message/deployment_message.rb
new file mode 100644
index 00000000000..c4f3bf9610d
--- /dev/null
+++ b/app/models/integrations/chat_message/deployment_message.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+module Integrations
+ 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
+end
diff --git a/app/models/integrations/chat_message/issue_message.rb b/app/models/integrations/chat_message/issue_message.rb
new file mode 100644
index 00000000000..5fa6bd4090f
--- /dev/null
+++ b/app/models/integrations/chat_message/issue_message.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+module Integrations
+ 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
+end
diff --git a/app/models/integrations/chat_message/merge_message.rb b/app/models/integrations/chat_message/merge_message.rb
new file mode 100644
index 00000000000..d2f48699f50
--- /dev/null
+++ b/app/models/integrations/chat_message/merge_message.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+module Integrations
+ 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
+end
diff --git a/app/models/integrations/chat_message/note_message.rb b/app/models/integrations/chat_message/note_message.rb
new file mode 100644
index 00000000000..96675d2b27c
--- /dev/null
+++ b/app/models/integrations/chat_message/note_message.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+module Integrations
+ 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
+end
diff --git a/app/models/integrations/chat_message/pipeline_message.rb b/app/models/integrations/chat_message/pipeline_message.rb
new file mode 100644
index 00000000000..a0f6f582e4c
--- /dev/null
+++ b/app/models/integrations/chat_message/pipeline_message.rb
@@ -0,0 +1,267 @@
+# frozen_string_literal: true
+
+module Integrations
+ 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.t(:'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.t(:'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
+end
diff --git a/app/models/integrations/chat_message/push_message.rb b/app/models/integrations/chat_message/push_message.rb
new file mode 100644
index 00000000000..0952986e923
--- /dev/null
+++ b/app/models/integrations/chat_message/push_message.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+module Integrations
+ 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
+end
diff --git a/app/models/integrations/chat_message/wiki_page_message.rb b/app/models/integrations/chat_message/wiki_page_message.rb
new file mode 100644
index 00000000000..9b5275b8c03
--- /dev/null
+++ b/app/models/integrations/chat_message/wiki_page_message.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module Integrations
+ 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
+end