Commit adc66360 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch 'issue_22269-ee' into 'master'

Port of "Add Mattermost Service" to EE

See merge request !972
parents f85dcc09 5fd1c6a3
......@@ -89,7 +89,8 @@ class Project < ActiveRecord::Base
has_one :asana_service, dependent: :destroy
has_one :gemnasium_service, dependent: :destroy
has_one :mattermost_slash_commands_service, dependent: :destroy
has_one :slack_service, dependent: :destroy
has_one :mattermost_notification_service, dependent: :destroy
has_one :slack_notification_service, dependent: :destroy
has_one :jenkins_service, dependent: :destroy
has_one :jenkins_deprecated_service, dependent: :destroy
has_one :buildkite_service, dependent: :destroy
......
require 'slack-notifier'
class SlackService
module ChatMessage
class BaseMessage
def initialize(params)
raise NotImplementedError
......
class SlackService
module ChatMessage
class BuildMessage < BaseMessage
attr_reader :sha
attr_reader :ref_type
......
class SlackService
module ChatMessage
class IssueMessage < BaseMessage
attr_reader :user_name
attr_reader :title
......
class SlackService
module ChatMessage
class MergeMessage < BaseMessage
attr_reader :user_name
attr_reader :project_name
......
class SlackService
module ChatMessage
class NoteMessage < BaseMessage
attr_reader :message
attr_reader :user_name
......
class SlackService
module ChatMessage
class PipelineMessage < BaseMessage
attr_reader :ref_type, :ref, :status, :project_name, :project_url,
:user_name, :duration, :pipeline_id
......
class SlackService
module ChatMessage
class PushMessage < BaseMessage
attr_reader :after
attr_reader :before
......
class SlackService
module ChatMessage
class WikiPageMessage < BaseMessage
attr_reader :user_name
attr_reader :title
......
class SlackService < Service
# Base class for Chat notifications services
# This class is not meant to be used directly, but only to inherit from.
class ChatNotificationService < Service
include ChatMessage
default_value_for :category, 'chat'
prop_accessor :webhook, :username, :channel
boolean_accessor :notify_only_broken_builds, :notify_only_broken_pipelines
validates :webhook, presence: true, url: true, if: :activated?
def initialize_properties
......@@ -14,35 +21,8 @@ class SlackService < Service
end
end
def title
'Slack'
end
def description
'A team communication tool for the 21st century'
end
def to_param
'slack'
end
def help
'This service sends notifications to your Slack channel.<br/>
To setup this Service you need to create a new <b>"Incoming webhook"</b> in your Slack integration panel,
and enter the Webhook URL below.'
end
def fields
default_fields =
[
{ type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' },
{ type: 'text', name: 'username', placeholder: 'username' },
{ type: 'text', name: 'channel', placeholder: "#general" },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
]
default_fields + build_event_channels
def can_test?
valid?
end
def supported_events
......@@ -67,21 +47,16 @@ class SlackService < Service
message = get_message(object_kind, data)
if message
opt = {}
return false unless message
event_channel = get_channel_field(object_kind) || channel
opt = {}
opt[:channel] = event_channel if event_channel
opt[:channel] = get_channel_field(object_kind).presence || channel || default_channel
opt[:username] = username if username
notifier = Slack::Notifier.new(webhook, opt)
notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback)
true
else
false
end
end
def event_channel_names
......@@ -96,6 +71,10 @@ class SlackService < Service
fields.reject { |field| field[:name].end_with?('channel') }
end
def default_channel
raise NotImplementedError
end
private
def get_message(object_kind, data)
......@@ -124,7 +103,7 @@ class SlackService < Service
def build_event_channels
supported_events.reduce([]) do |channels, event|
channels << { type: 'text', name: event_channel_name(event), placeholder: "#general" }
channels << { type: 'text', name: event_channel_name(event), placeholder: default_channel }
end
end
......@@ -166,11 +145,3 @@ class SlackService < Service
end
end
end
require "slack_service/issue_message"
require "slack_service/push_message"
require "slack_service/merge_message"
require "slack_service/note_message"
require "slack_service/build_message"
require "slack_service/pipeline_message"
require "slack_service/wiki_page_message"
# Base class for Chat services
# This class is not meant to be used directly, but only to inherrit from.
# This class is not meant to be used directly, but only to inherit from.
class ChatService < Service
default_value_for :category, 'chat'
......
class MattermostNotificationService < ChatNotificationService
def title
'Mattermost notifications'
end
def description
'Receive event notifications in Mattermost'
end
def to_param
'mattermost_notification'
end
def help
'This service sends notifications about projects events to Mattermost channels.<br />
To set up this service:
<ol>
<li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks">Enable incoming webhooks</a> in your Mattermost installation. </li>
<li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#creating-integrations-using-incoming-webhooks">Add an incoming webhook</a> in your Mattermost team. The default channel can be overridden for each event. </li>
<li>Paste the webhook <strong>URL</strong> into the field bellow. </li>
<li>Select events below to enable notifications. The channel and username are optional. </li>
</ol>'
end
def fields
default_fields + build_event_channels
end
def default_fields
[
{ type: 'text', name: 'webhook', placeholder: 'http://mattermost_host/hooks/...' },
{ type: 'text', name: 'username', placeholder: 'username' },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
]
end
def default_channel
"#town-square"
end
end
class SlackNotificationService < ChatNotificationService
def title
'Slack notifications'
end
def description
'Receive event notifications in Slack'
end
def to_param
'slack_notification'
end
def help
'This service sends notifications about projects events to Slack channels.<br />
To setup this service:
<ol>
<li><a href="https://slack.com/apps/A0F7XDUAZ-incoming-webhooks">Add an incoming webhook</a> in your Slack team. The default channel can be overridden for each event. </li>
<li>Paste the <strong>Webhook URL</strong> into the field below. </li>
<li>Select events below to enable notifications. The channel and username are optional. </li>
</ol>'
end
def fields
default_fields + build_event_channels
end
def default_fields
[
{ type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' },
{ type: 'text', name: 'username', placeholder: 'username' },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
]
end
def default_channel
"#general"
end
end
......@@ -221,7 +221,8 @@ class Service < ActiveRecord::Base
pivotaltracker
pushover
redmine
slack
mattermost_notification
slack_notification
teamcity
]
end
......
---
title: Create mattermost service
merge_request:
author:
# rubocop:disable all
class MoveSlackServiceToWebhook < ActiveRecord::Migration
DOWNTIME = true
DOWNTIME_REASON = 'Move old fields "token" and "subdomain" to one single field "webhook"'
def change
SlackService.all.each do |slack_service|
SlackNotificationService.all.each do |slack_service|
if ["token", "subdomain"].all? { |property| slack_service.properties.key? property }
token = slack_service.properties['token']
subdomain = slack_service.properties['subdomain']
......
class ChangeSlackServiceToSlackNotificationService < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = true
DOWNTIME_REASON = 'Rename SlackService to SlackNotificationService'
def up
execute("UPDATE services SET type = 'SlackNotificationService' WHERE type = 'SlackService'")
end
def down
execute("UPDATE services SET type = 'SlackService' WHERE type = 'SlackNotificationService'")
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20161202152035) do
ActiveRecord::Schema.define(version: 20161213172958) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......
......@@ -595,9 +595,9 @@ Get Redmine service settings for a project.
GET /projects/:id/services/redmine
```
## Slack
## Slack notifications
A team communication tool for the 21st century
Receive event notifications in Slack
### Create/Edit Slack service
......@@ -629,6 +629,40 @@ Get Slack service settings for a project.
GET /projects/:id/services/slack
```
## Mattermost notifications
Receive event notifications in Mattermost
### Create/Edit Mattermost notifications service
Set Mattermost service for a project.
```
PUT /projects/:id/services/mattermost
```
Parameters:
- `webhook` (**required**) - https://mattermost.example/hooks/1298aff...
- `username` (optional) - username
- `channel` (optional) - #channel
### Delete Mattermost notifications service
Delete Mattermost Notifications service for a project.
```
DELETE /projects/:id/services/mattermost
```
### Get Mattermost notifications service settings
Get Mattermost notifications service settings for a project.
```
GET /projects/:id/services/mattermost
```
## JetBrains TeamCity CI
A continuous integration and build server
......
# Mattermost Notifications Service
## On Mattermost
To enable Mattermost integration you must create an incoming webhook integration:
1. Sign in to your Mattermost instance
1. Visit incoming webhooks, that will be something like: https://mattermost.example/your_team_name/integrations/incoming_webhooks/add
1. Choose a display name, description and channel, those can be overridden on GitLab
1. Save it, copy the **Webhook URL**, we'll need this later for GitLab.
There might be some cases that Incoming Webhooks are blocked by admin, ask your mattermost admin to enable
it on https://mattermost.example/admin_console/integrations/custom.
Display name override is not enabled by default, you need to ask your admin to enable it on that same section.
## On GitLab
After you set up Mattermost, it's time to set up GitLab.
Go to your project's **Settings > Services > Mattermost Notifications** and you will see a
checkbox with the following events that can be triggered:
- Push
- Issue
- Merge request
- Note
- Tag push
- Build
- Wiki page
Bellow each of these event checkboxes, you will have an input field to insert
which Mattermost channel you want to send that event message, with `#town-square`
being the default. The hash sign is optional.
At the end, fill in your Mattermost details:
| Field | Description |
| ----- | ----------- |
| **Webhook** | The incoming webhooks which you have to setup on Mattermost, it will be something like: http://mattermost.example/hooks/5xo... |
| **Username** | Optional username which can be on messages sent to Mattermost. Fill this in if you want to change the username of the bot. |
| **Notify only broken builds** | If you choose to enable the **Build** event and you want to be only notified about failed builds. |
![Mattermost configuration](img/mattermost_configuration.png)
......@@ -43,10 +43,11 @@ further configuration instructions and details. Contributions are welcome.
| [JIRA](jira.md) | JIRA issue tracker |
| JetBrains TeamCity CI | A continuous integration and build server |
| [Mattermost slash commands](mattermost_slash_commands.md) | Mattermost chat and ChatOps slash commands |
| [Mattermost Notifications](mattermost.md) | Receive event notifications in Mattermost |
| [Slack Notifications](slack.md) | Receive event notifications in Slack |
| PivotalTracker | Project Management Software (Source Commits Endpoint) |
| Pushover | Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop |
| [Redmine](redmine.md) | Redmine issue tracker |
| [Slack](slack.md) | A team communication tool for the 21st century |
## Services Templates
......
# Slack Service
# Slack Notifications Service
## On Slack
......@@ -15,7 +15,7 @@ Slack:
After you set up Slack, it's time to set up GitLab.
Go to your project's **Settings > Services > Slack** and you will see a
Go to your project's **Settings > Services > Slack Notifications** and you will see a
checkbox with the following events that can be triggered:
- Push
......
module API
# Projects API
class Services < Grape::API
services = {
'asana' => [
{
required: true,
name: :api_key,
type: String,
desc: 'User API token'
},
{
required: false,
name: :restrict_to_branch,
type: String,
desc: 'Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches'
}
],
'assembla' => [
{
required: true,
name: :token,
type: String,
desc: 'The authentication token'
},
{
required: false,
name: :subdomain,
type: String,
desc: 'Subdomain setting'
}
],
'bamboo' => [
{
required: true,
name: :bamboo_url,
type: String,
desc: 'Bamboo root URL like https://bamboo.example.com'
},
{
required: true,
name: :build_key,
type: String,
desc: 'Bamboo build plan key like'
},
{
required: true,
name: :username,
type: String,
desc: 'A user with API access, if applicable'
},
{
required: true,
name: :password,
type: String,
desc: 'Passord of the user'
}
],
'bugzilla' => [
{
required: true,
name: :new_issue_url,
type: String,
desc: 'New issue URL'
},
{
required: true,
name: :issues_url,
type: String,
desc: 'Issues URL'
},
{
required: true,
name: :project_url,
type: String,
desc: 'Project URL'
},
{
required: false,
name: :description,
type: String,
desc: 'Description'
},
{
required: false,
name: :title,
type: String,
desc: 'Title'
}
],
'buildkite' => [
{
required: true,
name: :token,
type: String,
desc: 'Buildkite project GitLab token'
},
{
required: true,
name: :project_url,
type: String,
desc: 'The buildkite project URL'
},
{
required: false,
name: :enable_ssl_verification,
type: Boolean,
desc: 'Enable SSL verification for communication'
}
],
'builds-email' => [
{
required: true,
name: :recipients,
type: String,
desc: 'Comma-separated list of recipient email addresses'
},
{
required: false,
name: :add_pusher,
type: Boolean,
desc: 'Add pusher to recipients list'
},
{
required: false,
name: :notify_only_broken_builds,
type: Boolean,
desc: 'Notify only broken builds'
}
],
'campfire' => [
{
required: true,
name: :token,
type: String,
desc: 'Campfire token'
},
{
required: false,
name: :subdomain,
type: String,
desc: 'Campfire subdomain'
},
{
required: false,
name: :room,
type: String,
desc: 'Campfire room'
},
],
'custom-issue-tracker' => [
{
required: true,
name: :new_issue_url,
type: String,
desc: 'New issue URL'
},
{
required: true,
name: :issues_url,
type: String,
desc: 'Issues URL'
},
{
required: true,
name: :project_url,
type: String,
desc: 'Project URL'
},
{
required: false,
name: :description,
type: String,
desc: 'Description'
},
{
required: false,
name: :title,
type: String,
desc: 'Title'
}
],
'drone-ci' => [
{
required: true,
name: :token,
type: String,
desc: 'Drone CI token'
},
{
required: true,
name: :drone_url,
type: String,
desc: 'Drone CI URL'
},
{
required: false,
name: :enable_ssl_verification,
type: Boolean,
desc: 'Enable SSL verification for communication'
}
],
'emails-on-push' => [
{
required: true,
name: :recipients,
type: String,
desc: 'Comma-separated list of recipient email addresses'
},
{
required: false,
name: :disable_diffs,
type: Boolean,
desc: 'Disable code diffs'
},
{
required: false,
name: :send_from_committer_email,
type: Boolean,
desc: 'Send from committer'
}
],
'external-wiki' => [
{
required: true,
name: :external_wiki_url,
type: String,
desc: 'The URL of the external Wiki'
}
],
'flowdock' => [
{
required: true,
name: :token,
type: String,
desc: 'Flowdock token'
}
],
'gemnasium' => [
{
required: true,
name: :api_key,
type: String,
desc: 'Your personal API key on gemnasium.com'
},
{
required: true,
name: :token,
type: String,
desc: "The project's slug on gemnasium.com"
}
],
'hipchat' => [
{
required: true,
name: :token,
type: String,
desc: 'The room token'
},
{
required: false,
name: :room,
type: String,
desc: 'The room name or ID'
},
{
required: false,
name: :color,
type: String,
desc: 'The room color'
},
{
required: false,
name: :notify,
type: Boolean,
desc: 'Enable notifications'
},
{
required: false,
name: :api_version,
type: String,
desc: 'Leave blank for default (v2)'
},
{
required: false,
name: :server,
type: String,
desc: 'Leave blank for default. https://hipchat.example.com'
}
],
'irker' => [
{
required: true,
name: :recipients,
type: String,
desc: 'Recipients/channels separated by whitespaces'
},
{
required: false,
name: :default_irc_uri,
type: String,
desc: 'Default: irc://irc.network.net:6697'
},
{
required: false,
name: :server_host,
type: String,
desc: 'Server host. Default localhost'
},
{
required: false,
name: :server_port,
type: Integer,
desc: 'Server port. Default 6659'
},
{
required: false,
name: :colorize_messages,
type: Boolean,
desc: 'Colorize messages'
}
],
'jira' => [
{
required: true,
name: :url,
type: String,
desc: 'The URL to the JIRA project which is being linked to this GitLab project, e.g., https://jira.example.com'
},
{
required: true,
name: :project_key,
type: String,
desc: 'The short identifier for your JIRA project, all uppercase, e.g., PROJ'
},
{
required: false,
name: :username,
type: String,
desc: 'The username of the user created to be used with GitLab/JIRA'
},
{
required: false,
name: :password,
type: String,
desc: 'The password of the user created to be used with GitLab/JIRA'
},
{
required: false,
name: :jira_issue_transition_id,
type: Integer,
desc: 'The ID of a transition that moves issues to a closed state. You can find this number under the JIRA workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column ([see screenshot][trans]). By default, this ID is set to `2`'
}
],
'kubernetes' => [
{
required: true,
name: :namespace,
type: String,
desc: 'The Kubernetes namespace to use'
},
{
required: true,
name: :api_url,
type: String,
desc: 'The URL to the Kubernetes cluster API, e.g., https://kubernetes.example.com'
},
{
required: true,
name: :token,
type: String,
desc: 'The service token to authenticate against the Kubernetes cluster with'
},
{
required: false,
name: :ca_pem,
type: String,
desc: 'A custom certificate authority bundle to verify the Kubernetes cluster with (PEM format)'
},
],
'mattermost-slash-commands' => [
{
required: true,
name: :token,
type: String,
desc: 'The Mattermost token'
}
],
'pipelines-email' => [
{
required: true,
name: :recipients,
type: String,
desc: 'Comma-separated list of recipient email addresses'
},
{
required: false,
name: :notify_only_broken_builds,
type: Boolean,
desc: 'Notify only broken builds'
}
],
'pivotaltracker' => [
{
required: true,
name: :token,
type: String,
desc: 'The Pivotaltracker token'
},
{
required: false,
name: :restrict_to_branch,
type: String,
desc: 'Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches.'
}
],
'pushover' => [
{
required: true,
name: :api_key,
type: String,
desc: 'The application key'
},
{
required: true,
name: :user_key,
type: String,
desc: 'The user key'
},
{
required: true,
name: :priority,
type: String,
desc: 'The priority'
},
{
required: true,
name: :device,
type: String,
desc: 'Leave blank for all active devices'
},
{
required: true,
name: :sound,
type: String,
desc: 'The sound of the notification'
}
],
'redmine' => [
{
required: true,
name: :new_issue_url,
type: String,
desc: 'The new issue URL'
},
{
required: true,
name: :project_url,
type: String,
desc: 'The project URL'
},
{
required: true,
name: :issues_url,
type: String,
desc: 'The issues URL'
},
{
required: false,
name: :description,
type: String,
desc: 'The description of the tracker'
}
],
'slack-notification' => [
{
required: true,
name: :webhook,
type: String,
desc: 'The Slack webhook. e.g. https://hooks.slack.com/services/...'
},
{
required: false,
name: :new_issue_url,
type: String,
desc: 'The user name'
},
{
required: false,
name: :channel,
type: String,
desc: 'The channel name'
}
],
'mattermost-notification' => [
{
required: true,
name: :webhook,
type: String,
desc: 'The Mattermost webhook. e.g. http://mattermost_host/hooks/...'
}
],
'teamcity' => [
{
required: true,
name: :teamcity_url,
type: String,
desc: 'TeamCity root URL like https://teamcity.example.com'
},
{
required: true,
name: :build_type,
type: String,
desc: 'Build configuration ID'
},
{
required: true,
name: :username,
type: String,
desc: 'A user with permissions to trigger a manual build'
},
{
required: true,
name: :password,
type: String,
desc: 'The password of the user'
}
]
}.freeze
trigger_services = {
'mattermost-slash-commands' => [
{
name: :token,
type: String,
desc: 'The Mattermost token'
}
]
}.freeze
resource :projects do
before { authenticate! }
before { authorize_admin_project }
......
......@@ -2,8 +2,8 @@ require 'spec_helper'
feature 'Projects > Slack service > Setup events', feature: true do
let(:user) { create(:user) }
let(:service) { SlackService.new }
let(:project) { create(:project, slack_service: service) }
let(:service) { SlackNotificationService.new }
let(:project) { create(:project, slack_notification_service: service) }
background do
service.fields
......
......@@ -145,7 +145,8 @@ project:
- assembla_service
- asana_service
- gemnasium_service
- slack_service
- slack_notification_service
- mattermost_notification_service
- buildkite_service
- bamboo_service
- teamcity_service
......
require 'spec_helper'
describe SlackService::BuildMessage do
subject { SlackService::BuildMessage.new(args) }
describe ChatMessage::BuildMessage do
subject { described_class.new(args) }
let(:args) do
{
......
require 'spec_helper'
describe SlackService::IssueMessage, models: true do
subject { SlackService::IssueMessage.new(args) }
describe ChatMessage::IssueMessage, models: true do
subject { described_class.new(args) }
let(:args) do
{
......
require 'spec_helper'
describe SlackService::MergeMessage, models: true do
subject { SlackService::MergeMessage.new(args) }
describe ChatMessage::MergeMessage, models: true do
subject { described_class.new(args) }
let(:args) do
{
......
require 'spec_helper'
describe SlackService::NoteMessage, models: true do
describe ChatMessage::NoteMessage, models: true do
let(:color) { '#345' }
before do
......@@ -36,7 +36,7 @@ describe SlackService::NoteMessage, models: true do
end
it 'returns a message regarding notes on commits' do
message = SlackService::NoteMessage.new(@args)
message = described_class.new(@args)
expect(message.pretext).to eq("test.user <url|commented on " \
"commit 5f163b2b> in <somewhere.com|project_name>: " \
"*Added a commit message*")
......@@ -62,7 +62,7 @@ describe SlackService::NoteMessage, models: true do
end
it 'returns a message regarding notes on a merge request' do
message = SlackService::NoteMessage.new(@args)
message = described_class.new(@args)
expect(message.pretext).to eq("test.user <url|commented on " \
"merge request !30> in <somewhere.com|project_name>: " \
"*merge request title*")
......@@ -88,7 +88,7 @@ describe SlackService::NoteMessage, models: true do
end
it 'returns a message regarding notes on an issue' do
message = SlackService::NoteMessage.new(@args)
message = described_class.new(@args)
expect(message.pretext).to eq(
"test.user <url|commented on " \
"issue #20> in <somewhere.com|project_name>: " \
......@@ -114,7 +114,7 @@ describe SlackService::NoteMessage, models: true do
end
it 'returns a message regarding notes on a project snippet' do
message = SlackService::NoteMessage.new(@args)
message = described_class.new(@args)
expect(message.pretext).to eq("test.user <url|commented on " \
"snippet #5> in <somewhere.com|project_name>: " \
"*snippet title*")
......
require 'spec_helper'
describe SlackService::PipelineMessage do
subject { SlackService::PipelineMessage.new(args) }
describe ChatMessage::PipelineMessage do
subject { described_class.new(args) }
let(:user) { { name: 'hacker' } }
let(:args) do
{
......
require 'spec_helper'
describe SlackService::PushMessage, models: true do
subject { SlackService::PushMessage.new(args) }
describe ChatMessage::PushMessage, models: true do
subject { described_class.new(args) }
let(:args) do
{
......
require 'spec_helper'
describe SlackService::WikiPageMessage, models: true do
describe ChatMessage::WikiPageMessage, models: true do
subject { described_class.new(args) }
let(:args) do
......
require 'spec_helper'
describe ChatNotificationService, models: true do
describe "Associations" do
before do
allow(subject).to receive(:activated?).and_return(true)
end
it { is_expected.to validate_presence_of :webhook }
end
end
require 'spec_helper'
describe MattermostNotificationService, models: true do
it_behaves_like "slack or mattermost"
end
require 'spec_helper'
describe SlackNotificationService, models: true do
it_behaves_like "slack or mattermost"
end
......@@ -22,7 +22,8 @@ describe Project, models: true do
it { is_expected.to have_many(:protected_branches).dependent(:destroy) }
it { is_expected.to have_many(:chat_services) }
it { is_expected.to have_one(:forked_project_link).dependent(:destroy) }
it { is_expected.to have_one(:slack_service).dependent(:destroy) }
it { is_expected.to have_one(:slack_notification_service).dependent(:destroy) }
it { is_expected.to have_one(:mattermost_notification_service).dependent(:destroy) }
it { is_expected.to have_one(:pushover_service).dependent(:destroy) }
it { is_expected.to have_one(:asana_service).dependent(:destroy) }
it { is_expected.to have_many(:boards).dependent(:destroy) }
......
require 'spec_helper'
Dir[Rails.root.join("app/models/project_services/chat_message/*.rb")].each { |f| require f }
describe SlackService, models: true do
let(:slack) { SlackService.new }
RSpec.shared_examples 'slack or mattermost' do
let(:chat_service) { described_class.new }
let(:webhook_url) { 'https://example.gitlab.com/' }
describe "Associations" do
......@@ -24,7 +24,7 @@ describe SlackService, models: true do
end
end
describe "Execute" do
describe "#execute" do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:username) { 'slack_username' }
......@@ -35,7 +35,7 @@ describe SlackService, models: true do
end
before do
allow(slack).to receive_messages(
allow(chat_service).to receive_messages(
project: project,
project_id: project.id,
service_hook: true,
......@@ -77,54 +77,55 @@ describe SlackService, models: true do
@wiki_page_sample_data = wiki_page_service.hook_data(@wiki_page, 'create')
end
it "calls Slack API for push events" do
slack.execute(push_sample_data)
it "calls Slack/Mattermost API for push events" do
chat_service.execute(push_sample_data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
it "calls Slack API for issue events" do
slack.execute(@issues_sample_data)
it "calls Slack/Mattermost API for issue events" do
chat_service.execute(@issues_sample_data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
it "calls Slack API for merge requests events" do
slack.execute(@merge_sample_data)
it "calls Slack/Mattermost API for merge requests events" do
chat_service.execute(@merge_sample_data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
it "calls Slack API for wiki page events" do
slack.execute(@wiki_page_sample_data)
it "calls Slack/Mattermost API for wiki page events" do
chat_service.execute(@wiki_page_sample_data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
it 'uses the username as an option for slack when configured' do
allow(slack).to receive(:username).and_return(username)
allow(chat_service).to receive(:username).and_return(username)
expect(Slack::Notifier).to receive(:new).
with(webhook_url, username: username).
with(webhook_url, username: username, channel: chat_service.default_channel).
and_return(
double(:slack_service).as_null_object
)
slack.execute(push_sample_data)
chat_service.execute(push_sample_data)
end
it 'uses the channel as an option when it is configured' do
allow(slack).to receive(:channel).and_return(channel)
allow(chat_service).to receive(:channel).and_return(channel)
expect(Slack::Notifier).to receive(:new).
with(webhook_url, channel: channel).
and_return(
double(:slack_service).as_null_object
)
slack.execute(push_sample_data)
chat_service.execute(push_sample_data)
end
context "event channels" do
it "uses the right channel for push event" do
slack.update_attributes(push_channel: "random")
chat_service.update_attributes(push_channel: "random")
expect(Slack::Notifier).to receive(:new).
with(webhook_url, channel: "random").
......@@ -132,11 +133,11 @@ describe SlackService, models: true do
double(:slack_service).as_null_object
)
slack.execute(push_sample_data)
chat_service.execute(push_sample_data)
end
it "uses the right channel for merge request event" do
slack.update_attributes(merge_request_channel: "random")
chat_service.update_attributes(merge_request_channel: "random")
expect(Slack::Notifier).to receive(:new).
with(webhook_url, channel: "random").
......@@ -144,11 +145,11 @@ describe SlackService, models: true do
double(:slack_service).as_null_object
)
slack.execute(@merge_sample_data)
chat_service.execute(@merge_sample_data)
end
it "uses the right channel for issue event" do
slack.update_attributes(issue_channel: "random")
chat_service.update_attributes(issue_channel: "random")
expect(Slack::Notifier).to receive(:new).
with(webhook_url, channel: "random").
......@@ -156,11 +157,11 @@ describe SlackService, models: true do
double(:slack_service).as_null_object
)
slack.execute(@issues_sample_data)
chat_service.execute(@issues_sample_data)
end
it "uses the right channel for wiki event" do
slack.update_attributes(wiki_page_channel: "random")
chat_service.update_attributes(wiki_page_channel: "random")
expect(Slack::Notifier).to receive(:new).
with(webhook_url, channel: "random").
......@@ -168,7 +169,7 @@ describe SlackService, models: true do
double(:slack_service).as_null_object
)
slack.execute(@wiki_page_sample_data)
chat_service.execute(@wiki_page_sample_data)
end
context "note event" do
......@@ -177,7 +178,7 @@ describe SlackService, models: true do
end
it "uses the right channel" do
slack.update_attributes(note_channel: "random")
chat_service.update_attributes(note_channel: "random")
note_data = Gitlab::DataBuilder::Note.build(issue_note, user)
......@@ -187,7 +188,7 @@ describe SlackService, models: true do
double(:slack_service).as_null_object
)
slack.execute(note_data)
chat_service.execute(note_data)
end
end
end
......@@ -198,7 +199,7 @@ describe SlackService, models: true do
let(:project) { create(:project, creator_id: user.id) }
before do
allow(slack).to receive_messages(
allow(chat_service).to receive_messages(
project: project,
project_id: project.id,
service_hook: true,
......@@ -216,9 +217,9 @@ describe SlackService, models: true do
note: 'a comment on a commit')
end
it "calls Slack API for commit comment events" do
it "calls Slack/Mattermost API for commit comment events" do
data = Gitlab::DataBuilder::Note.build(commit_note, user)
slack.execute(data)
chat_service.execute(data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
......@@ -232,7 +233,7 @@ describe SlackService, models: true do
it "calls Slack API for merge request comment events" do
data = Gitlab::DataBuilder::Note.build(merge_request_note, user)
slack.execute(data)
chat_service.execute(data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
......@@ -245,7 +246,7 @@ describe SlackService, models: true do
it "calls Slack API for issue comment events" do
data = Gitlab::DataBuilder::Note.build(issue_note, user)
slack.execute(data)
chat_service.execute(data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
......@@ -259,7 +260,7 @@ describe SlackService, models: true do
it "calls Slack API for snippet comment events" do
data = Gitlab::DataBuilder::Note.build(snippet_note, user)
slack.execute(data)
chat_service.execute(data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
......@@ -277,21 +278,21 @@ describe SlackService, models: true do
end
before do
allow(slack).to receive_messages(
allow(chat_service).to receive_messages(
project: project,
service_hook: true,
webhook: webhook_url
)
end
shared_examples 'call Slack API' do
shared_examples 'call Slack/Mattermost API' do
before do
WebMock.stub_request(:post, webhook_url)
end
it 'calls Slack API for pipeline events' do
it 'calls Slack/Mattermost API for pipeline events' do
data = Gitlab::DataBuilder::Pipeline.build(pipeline)
slack.execute(data)
chat_service.execute(data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
......@@ -300,16 +301,16 @@ describe SlackService, models: true do
context 'with failed pipeline' do
let(:status) { 'failed' }
it_behaves_like 'call Slack API'
it_behaves_like 'call Slack/Mattermost API'
end
context 'with succeeded pipeline' do
let(:status) { 'success' }
context 'with default to notify_only_broken_pipelines' do
it 'does not call Slack API for pipeline events' do
it 'does not call Slack/Mattermost API for pipeline events' do
data = Gitlab::DataBuilder::Pipeline.build(pipeline)
result = slack.execute(data)
result = chat_service.execute(data)
expect(result).to be_falsy
end
......@@ -317,10 +318,10 @@ describe SlackService, models: true do
context 'with setting notify_only_broken_pipelines to false' do
before do
slack.notify_only_broken_pipelines = false
chat_service.notify_only_broken_pipelines = false
end
it_behaves_like 'call Slack API'
it_behaves_like 'call Slack/Mattermost API'
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment