GitLab steht Mittwoch, den 08. Juli, zwischen 09:00 und 13:00 Uhr aufgrund von Wartungsarbeiten nicht zur Verfügung.

Commit 1f404065 authored by Tiago Botelho's avatar Tiago Botelho

adds relevant tests

parent 05f69ef2
......@@ -2,14 +2,24 @@ require 'slack-notifier'
module ChatMessage
class BaseMessage
attr_reader :markdown_format
attr_reader :markdown
attr_reader :user_name
attr_reader :user_avatar
attr_reader :project_name
attr_reader :project_url
def initialize(params)
@markdown_format = params[:markdown_format] || false
@markdown = params[:markdown] || false
@project_name = params.dig(:project, :path_with_namespace) || params[:project_name]
@project_url = params.dig(:project, :web_url) || params[:project_url]
@user_name = params.dig(:user, :username) || params[:user_name]
@user_avatar = params.dig(:user, :avatar_url) || params[:user_avatar]
end
def pretext
markdown_format ? message : format(message)
return message if markdown
format(message)
end
def fallback
......
module ChatMessage
class IssueMessage < BaseMessage
attr_reader :user_name
attr_reader :user_avatar
attr_reader :title
attr_reader :project_name
attr_reader :project_url
attr_reader :issue_iid
attr_reader :issue_url
attr_reader :action
......@@ -12,12 +8,7 @@ module ChatMessage
attr_reader :description
def initialize(params)
super(params)
@user_name = params[:user][:username]
@user_avatar = params[:user][:avatar_url]
@project_name = params[:project_name]
@project_url = params[:project_url]
super
obj_attr = params[:object_attributes]
obj_attr = HashWithIndifferentAccess.new(obj_attr)
......@@ -31,23 +22,28 @@ module ChatMessage
def attachments
return [] unless opened_issue?
return description if markdown
markdown_format ? description : description_message
description_message
end
def activity
MicrosoftTeams::Activity.new(
"Issue #{state} by #{user_name}",
"to: #{project_link}",
issue_link,
user_avatar
).to_json
{
title: "Issue #{state} by #{user_name}",
subtitle: "in #{project_link}",
text: issue_link,
image: user_avatar
}
end
private
def message
"[#{project_link}] Issue #{issue_link} #{state} by #{user_name}"
if state == 'opened'
"[#{project_link}] Issue #{state} by #{user_name}"
else
"[#{project_link}] Issue #{issue_link} #{state} by #{user_name}"
end
end
def opened_issue?
......@@ -72,7 +68,7 @@ module ChatMessage
end
def issue_title
"##{issue_iid} #{title}"
"#{Issue.reference_prefix}#{issue_iid} #{title}"
end
end
end
module ChatMessage
class MergeMessage < BaseMessage
attr_reader :user_name
attr_reader :user_avatar
attr_reader :project_name
attr_reader :project_url
attr_reader :merge_request_id
attr_reader :merge_request_iid
attr_reader :source_branch
attr_reader :target_branch
attr_reader :state
attr_reader :title
def initialize(params)
super(params)
@user_name = params[:user][:username]
@user_avatar = params[:user][:avatar_url]
@project_name = params[:project_name]
@project_url = params[:project_url]
super
obj_attr = params[:object_attributes]
obj_attr = HashWithIndifferentAccess.new(obj_attr)
@merge_request_id = obj_attr[:iid]
@merge_request_iid = obj_attr[:iid]
@source_branch = obj_attr[:source_branch]
@target_branch = obj_attr[:target_branch]
@state = obj_attr[:state]
@title = format_title(obj_attr[:title])
end
def activity
MicrosoftTeams::Activity.new(
"Merge Request #{state} by #{user_name}",
"to: #{project_link}",
merge_request_link,
user_avatar
).to_json
end
def attachments
[]
end
def activity
{
title: "Merge Request #{state} by #{user_name}",
subtitle: "in #{project_link}",
text: merge_request_link,
image: user_avatar
}
end
private
def format_title(title)
......@@ -59,11 +50,15 @@ module ChatMessage
end
def merge_request_link
link("merge request !#{merge_request_id}", merge_request_url)
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_id}"
"#{project_url}/merge_requests/#{merge_request_iid}"
end
end
end
module ChatMessage
class NoteMessage < BaseMessage
attr_reader :user_name
attr_reader :user_avatar
attr_reader :project_name
attr_reader :project_url
attr_reader :note
attr_reader :note_url
attr_reader :comment_attrs
attr_reader :title
attr_reader :target
def initialize(params)
params = HashWithIndifferentAccess.new(params)
super(params)
super
@user_name = params[:user][:name]
@user_avatar = params[:user][:avatar_url]
@project_name = params[:project_name]
@project_url = params[:project_url]
obj_attr = HashWithIndifferentAccess.new(params[:object_attributes])
params = HashWithIndifferentAccess.new(params)
obj_attr = params[:object_attributes]
@note = obj_attr[:note]
@note_url = obj_attr[:url]
@comment_attrs = comment_params(obj_attr[:noteable_type], params)
@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 activity
MicrosoftTeams::Activity.new(
"#{user_name} #{link('commented on ' + comment_attrs[:target], note_url)}",
"to: #{project_link}",
comment_attrs[:title],
user_avatar
).to_json
def attachments
return note if markdown
description_message
end
def attachments
markdown_format ? note : description_message
def activity
{
title: "#{user_name} #{link('commented on ' + target, note_url)}",
subtitle: "in #{project_link}",
text: formatted_title,
image: user_avatar
}
end
private
def message
"#{user_name} #{link('commented on ' + comment_attrs[:target], note_url)} in #{project_link}: *#{comment_attrs[:title]}*"
end
def comment_params(noteable_type, params)
params = HashWithIndifferentAccess.new(params)
case 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
"#{user_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)
{ target: "issue ##{issue[:iid]}", title: format_title(issue[:title]) }
["issue #{Issue.reference_prefix}#{issue[:iid]}", issue[:title]]
end
def create_commit_note(commit)
commit_sha = Commit.truncate_sha(commit[:id])
{ target: "commit #{commit_sha}", title: format_title(commit[:message]) }
["commit #{commit_sha}", commit[:message]]
end
def create_merge_note(merge_request)
{ target: "merge request !#{merge_request[:iid]}", title: format_title(merge_request[:title]) }
["merge request #{MergeRequest.reference_prefix}#{merge_request[:iid]}", merge_request[:title]]
end
def create_snippet_note(snippet)
{ target: "snippet ##{snippet[:id]}", title: format_title(snippet[:title]) }
["snippet #{Snippet.reference_prefix}#{snippet[:id]}", snippet[:title]]
end
def description_message
......
......@@ -3,27 +3,20 @@ module ChatMessage
attr_reader :ref_type
attr_reader :ref
attr_reader :status
attr_reader :project_name
attr_reader :project_url
attr_reader :user_name
attr_reader :duration
attr_reader :pipeline_id
attr_reader :user_avatar
def initialize(data)
super
@user_name = data.dig(:user, :name) || 'API'
pipeline_attributes = data[:object_attributes]
@ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
@ref = pipeline_attributes[:ref]
@status = pipeline_attributes[:status]
@duration = pipeline_attributes[:duration]
@pipeline_id = pipeline_attributes[:id]
super(data)
@project_name = data[:project][:path_with_namespace]
@project_url = data[:project][:web_url]
@user_name = (data[:user] && data[:user][:name]) || 'API'
@user_avatar = data[:user][:avatar_url] || ''
end
def pretext
......@@ -34,17 +27,19 @@ module ChatMessage
format(message)
end
def activity
MicrosoftTeams::Activity.new(
"Pipeline #{pipeline_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status}",
"to: #{project_link}",
"in #{duration} #{time_measure}",
user_avatar
).to_json
def attachments
return message if markdown
[{ text: format(message), color: attachment_color }]
end
def attachments
markdown_format ? message : [{ text: format(message), color: attachment_color }]
def activity
{
title: "Pipeline #{pipeline_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status}",
subtitle: "in #{project_link}",
text: "in #{duration} #{time_measure}",
image: user_avatar || ''
}
end
private
......
......@@ -3,49 +3,41 @@ module ChatMessage
attr_reader :after
attr_reader :before
attr_reader :commits
attr_reader :project_name
attr_reader :project_url
attr_reader :ref
attr_reader :ref_type
attr_reader :user_name
attr_reader :user_avatar
def initialize(params)
super(params)
super
@after = params[:after]
@before = params[:before]
@commits = params.fetch(:commits, [])
@project_name = params[:project_name]
@project_url = params[:project_url]
@ref_type = Gitlab::Git.tag_ref?(params[:ref]) ? 'tag' : 'branch'
@ref = Gitlab::Git.ref_name(params[:ref])
@user_name = params[:user_name]
@user_avatar = params[:user_avatar]
end
def activity
action =
if new_branch?
"created"
elsif removed_branch?
"removed"
else
"pushed to"
end
MicrosoftTeams::Activity.new(
"#{user_name} #{action} #{ref_type}",
"to: #{project_link}",
compare_link,
user_avatar
).to_json
end
def attachments
return [] if new_branch? || removed_branch?
return commit_messages if markdown
markdown_format ? commit_messages : commit_message_attachments
commit_message_attachments
end
def activity
action = if new_branch?
"created"
elsif removed_branch?
"removed"
else
"pushed to"
end
{
title: "#{user_name} #{action} #{ref_type}",
subtitle: "in #{project_link}",
text: compare_link,
image: user_avatar
}
end
private
......
module ChatMessage
class WikiPageMessage < BaseMessage
attr_reader :user_name
attr_reader :title
attr_reader :project_name
attr_reader :project_url
attr_reader :wiki_page_url
attr_reader :action
attr_reader :description
def initialize(params)
super(params)
@user_name = params[:user][:username]
@user_avatar = params[:user][:avatar_url]
@project_name = params[:project_name]
@project_url = params[:project_url]
super
obj_attr = params[:object_attributes]
obj_attr = HashWithIndifferentAccess.new(obj_attr)
......@@ -31,17 +23,19 @@ module ChatMessage
end
end
def activity
MicrosoftTeams::Activity.new(
"#{user_name} #{action} #{wiki_page_link}",
"in: #{project_link}",
title,
user_avatar
).to_json
def attachments
return description if markdown
description_message
end
def attachments
markdown_format ? @description : description_message
def activity
{
title: "#{user_name} #{action} #{wiki_page_link}",
subtitle: "in #{project_link}",
text: title,
image: user_avatar
}
end
private
......
......@@ -49,10 +49,7 @@ class ChatNotificationService < Service
object_kind = data[:object_kind]
data = data.merge(
project_url: project_url,
project_name: project_name
)
data = custom_data(data)
# WebHook events often have an 'update' event that follows a 'open' or
# 'close' action. Ignore update events for now to prevent duplicate
......@@ -68,8 +65,7 @@ class ChatNotificationService < Service
opts[:channel] = channel_name if channel_name
opts[:username] = username if username
notifier = Slack::Notifier.new(webhook, opts)
notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback)
return false unless notify(message, opts)
true
end
......@@ -92,6 +88,18 @@ class ChatNotificationService < Service
private
def notify(message, opts)
Slack::Notifier.new(webhook, opts).ping(
message.pretext,
attachments: message.attachments,
fallback: message.fallback
)
end
def custom_data(data)
data.merge(project_url: project_url, project_name: project_name)
end
def get_message(object_kind, data)
case object_kind
when "push", "tag_push"
......
......@@ -4,14 +4,13 @@ class MicrosoftTeamsService < ChatNotificationService
end
def description
'Receive event notifications in Microsoft Team'
'Receive event notifications in Microsoft Teams'
end
def self.to_param
'microsoft_teams'
end
#TODO: Setup the description accordingly
def help
'This service sends notifications about projects events to Microsoft Teams channels.<br />
To set up this service:
......@@ -22,10 +21,6 @@ class MicrosoftTeamsService < ChatNotificationService
</ol>'
end
def default_channel_placeholder
"Channel name (e.g. general)"
end
def webhook_placeholder
'https://outlook.office.com/webhook/…'
end
......@@ -33,6 +28,9 @@ class MicrosoftTeamsService < ChatNotificationService
def event_field(event)
end
def default_channel_placeholder
end
def default_fields
[
{ type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
......@@ -41,27 +39,18 @@ class MicrosoftTeamsService < ChatNotificationService
]
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
return unless webhook.present?
object_kind = data[:object_kind]
private
data = data.merge(
project_url: project_url,
project_name: project_name,
markdown_format: true
)
message = get_message(object_kind, data)
return false unless message
MicrosoftTeams::Notifier.new(webhook).ping({
def notify(message, opts)
MicrosoftTeams::Notifier.new(webhook).ping(
title: message.project_name,
pretext: message.pretext,
activity: message.activity,
attachments: message.attachments
})
)
end
def custom_data(data)
super(data).merge(markdown: true)
end
end
---
title: Integrates Microsoft Teams webhooks with GitLab
merge_request: 10412
author:
# Microsoft Teams Service
## On Microsoft Teams
To enable Microsoft Teams integration you must create an incoming webhook integration on Microsoft Teams by following the steps described in this [document](https://msdn.microsoft.com/en-us/microsoft-teams/connectors)
## On GitLab
After you set up Microsoft Teams, it's time to set up GitLab.
Navigate to the [Integrations page](project_services.md#accessing-the-project-services)
and select the **Microsoft Teams Notification** service to configure it.
There, you will see a checkbox with the following events that can be triggered:
- Push
- Issue
- Confidential issue
- Merge request
- Note
- Tag push
- Pipeline
- Wiki page
At the end fill in your Microsoft Teams details:
| Field | Description |
| ----- | ----------- |
| **Webhook** | The incoming webhook URL which you have to setup on Microsoft Teams. |
| **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. |
After you are all done, click **Save changes** for the changes to take effect.
![Microsoft Teams configuration](img/microsoft_teams_configuration.png)
\ No newline at end of file
......@@ -488,6 +488,14 @@ module API
desc: 'The channel name'
}
],
'microsoft-teams' => [
{
required: true,
name: :webhook,
type: String,
desc: 'The Microsoft Teams webhook. e.g. https://outlook.office.com/webhook/…'
}
],
'mattermost' => [
{
required: true,
......@@ -550,6 +558,7 @@ module API
RedmineService,
SlackService,
MattermostService,
MicrosoftTeamsService,
TeamcityService,
]
......
......@@ -501,6 +501,12 @@ module API
desc: 'The channel name'
}
],
'microsoft-teams' => [
required: true,
name: :webhook,
type: String,
desc: 'The Microsoft Teams webhook. e.g. https://outlook.office.com/webhook/…'
],
'mattermost' => [
{
required: true,
......
module MicrosoftTeams
class Activity
def initialize(title, subtitle, text, image)
def initialize(title:, subtitle:, text:, image:)
@title = title
@subtitle = subtitle
@text = text
@image = image
end
def to_json
def prepare
{
'activityTitle' => @title,
'activitySubtitle' => @subtitle,
'activityText' => @text,
'activityImage' => @image
}.to_json
}
end
end
end
......@@ -2,30 +2,43 @@ module MicrosoftTeams
class Notifier
def initialize(webhook)
@webhook = webhook
@header = { 'Content-type' => 'application/json' }
end